Clean up day 2 solution and reduce time complexity

This commit is contained in:
Folkert Kevelam 2025-03-05 22:51:09 +01:00
parent 42f70ecd1b
commit dd8711dbd0

View File

@ -4,7 +4,7 @@ with Ada.Containers.Vectors;
procedure Day_2 is procedure Day_2 is
type Safety is (Unsafe, Safe); type Safety is (Unsafe, Safe);
type Ordering is (Increasing, Decreasing, Equal); type Ordering is (Increasing, Decreasing);
type Levels is array (Natural range <> ) of Integer; type Levels is array (Natural range <> ) of Integer;
@ -15,123 +15,129 @@ procedure Day_2 is
end record; end record;
type Reports is array (Natural range <>) of Report; type Reports is array (Natural range <>) of Report;
type Orderings is array (Natural range <>) of Ordering;
type Safeties is array (Natural range <>) of Safety;
type Level_Report is function Is_Level_Safe( First, Second : Integer; Prev : Ordering ) return Safety is
record Diff : Integer := Second - First;
Order : Orderings( 0 .. 9 ) := (others => Equal); ADiff : Natural := abs(Diff);
Diff : Levels( 0 .. 9 ) := (others => 0);
Count : Natural := 0;
end record;
function Calculate_Levels( R : Report ) return Level_Report is
Out_R : Level_Report;
begin begin
for I in 0 .. R.Count - 2 loop if ADiff < 1 or ADiff > 3 then
return Unsafe;
else
if Diff > 0 and Prev = Increasing then
return Safe;
elsif Diff < 0 and Prev = Decreasing then
return Safe;
else
return Unsafe;
end if;
end if;
end Is_Level_Safe;
-- Safe for day 2 2:
-- 5 6 4 3 2 1
-- 5 4 6 7 8 9
-- 5 5 6 7 8 9
-- 5 5 4 3 2 1
-- For all wrong levels in the middle of the report, where a prevailing
-- order can already be determined, and easy algorithm is to jump ahead
-- n levels from the unsafe integer and determine if that is safe. If the
-- jump is less than the amount of chances, then it will catch all errors.
--
-- The problem resides in determining the correct order. In previous
-- iterations, I used the first two levels to determine the errors,
-- but in the above examples, the first order was incorrect.
--
-- The easiest solution I have found is to nest the above algorithm
-- in another loop that skips ahead from the report. This does increase
-- the time complexity, although in the case of day 2.2 only to 2*n.
function Calculate_Report( R : Report; Chances : Integer ) return Safety is
Current_Chances : Integer := Chances;
Skip_Ahead : Natural := 0;
Prev_Order : Ordering;
Sum : Integer := 0;
begin
for I in R.Values'First .. R.Values'Last - 1 loop
declare declare
Diff : Integer := R.Values(I+1) - R.Values(I); Diff : Integer := R.Values(I+1) - R.Values(I);
begin begin
if Diff > 0 then if Diff > 0 then
Out_R.Order(I) := Increasing; Sum := @ + 1;
elsif Diff < 0 then elsif Diff < 0 then
Out_R.Order(I) := Decreasing; Sum := @ - 1;
else
Out_R.Order(I) := Equal;
end if; end if;
Out_R.Diff(I) := abs(Diff);
end; end;
end loop; end loop;
Out_R.Count := R.Count - 1; if abs(Sum) < Chances then
return Unsafe;
else
if Sum > 0 then
Prev_Order := Increasing;
else
Prev_Order := Decreasing;
end if;
end if;
return Out_R; while Skip_Ahead <= Chances loop
end Calculate_Levels; Current_Chances := Chances - Skip_Ahead;
function Calculate_Report( Levels : Level_Report ) return Safety is declare
First_Index : Natural := R.Values'First + Skip_Ahead;
Second_Index : Natural := First_Index + 1;
Diff : Integer := R.Values(Second_Index) - R.Values(First_Index);
begin begin
if Levels.Diff(0) < 1 or Levels.Diff(0) > 3 then
return Unsafe;
end if;
for I in 0 .. Levels.Count - 2 loop while (Second_Index < R.Count) and (Current_Chances >= 0) loop
if not (Levels.Order(I+1) = Levels.Order(I)) then if Is_Level_Safe(R.Values(First_Index), R.Values(Second_Index), Prev_Order) = Safe then
return Unsafe; First_Index := Second_Index;
end if; Second_Index := @ + 1;
if Levels.Diff(I+1) < 1 or Levels.Diff(I+1) > 3 then else
return Unsafe; Current_Chances := @ - 1;
Second_Index := @ + 1;
end if; end if;
end loop; end loop;
if Levels.Order(0) = Equal then if Current_Chances >= 0 then
return Unsafe;
end if;
return Safe; return Safe;
else
Skip_Ahead := @ + 1;
end if;
end;
end loop;
return Unsafe;
end Calculate_Report; end Calculate_Report;
function Calculate_Report_Lossy( R : Report ) return Safety is function Get_Safe_Reports( R : Reports; Chances : Integer ) return Natural is
Levels : Level_Report := Calculate_Levels(R);
begin
if Calculate_Report( Levels ) = Safe then
return Safe;
else
for I in 0 .. R.Count - 1 loop
declare
New_Report : Report;
begin
for J in 0 .. I-1 loop
New_Report.Values(J) := R.Values(J);
end loop;
for J in I+1 .. R.Count -1 loop
New_Report.Values(J-1) := R.Values(J);
end loop;
New_Report.Count := R.Count - 1;
Put_Line("Check level reduced report @" & Integer'Image(I));
Put_Line(R'Image);
Put_Line(New_Report'Image);
if Calculate_Report(Calculate_Levels( New_Report)) = Safe then
return Safe;
end if;
end;
end loop;
return Unsafe;
end if;
end Calculate_Report_Lossy;
function Get_Safe_Reports_Lossy( R : Reports ) return Natural is
Sum : Natural := 0; Sum : Natural := 0;
begin begin
for Rep of R loop for Rep of R loop
declare if Calculate_Report( Rep, Chances ) = Safe then
Result : Safety := Calculate_Report_Lossy(Rep);
begin
if Result = Safe then
Sum := @ + 1; Sum := @ + 1;
end if; end if;
end;
end loop;
return Sum;
end Get_Safe_Reports_Lossy;
function Get_Safe_Reports( R : Reports ) return Natural is
Sum : Natural := 0;
begin
for Rep of R loop
declare
Result : Safety := Calculate_Report(Calculate_Levels(Rep));
begin
if Result = Safe then
Sum := @ + 1;
end if;
end;
end loop; end loop;
return Sum; return Sum;
end Get_Safe_Reports; end Get_Safe_Reports;
procedure Diff_Reports( R : Reports ) is
begin
Put_Line("Running diff");
for Rep of R loop
declare
Result_First : Safety := Calculate_Report(Rep, 0);
Result_Second : Safety := Calculate_Report(Rep, 1);
begin
if Result_First /= Result_Second then
Put_Line("Result First: " & Result_First'Image);
Put_Line("Result Second: " & Result_Second'Image);
Put_Line("Report: " & Rep'Image);
end if;
end;
end loop;
end;
function ToNum( Num : String ) return Integer is function ToNum( Num : String ) return Integer is
begin begin
return Integer'Value(Num); return Integer'Value(Num);
@ -195,7 +201,10 @@ procedure Day_2 is
File_Name : String := "input_day_2.txt"; File_Name : String := "input_day_2.txt";
Reps : Reports := Read_File(File_Name);
begin begin
Put_Line("Day 2 1:" & Get_Safe_Reports(Read_File(File_Name))'Image); Put_Line("Day 2 1:" & Get_Safe_Reports(Reps, 0)'Image);
Put_Line("Day 2 2:" & Get_Safe_Reports_Lossy(Read_File(File_Name))'Image); Put_Line("Day 2 2:" & Get_Safe_Reports(Reps, 1)'Image);
Put_Line("Day 2 3:" & Get_Safe_Reports(Reps, 2)'Image);
end Day_2; end Day_2;