From dd8711dbd045db6a2eb3b3d9a13a65dfaf448a85 Mon Sep 17 00:00:00 2001 From: Folkert Kevelam Date: Wed, 5 Mar 2025 22:51:09 +0100 Subject: [PATCH] Clean up day 2 solution and reduce time complexity --- AoC/2024/day_2/day_2.adb | 197 ++++++++++++++++++++------------------- 1 file changed, 103 insertions(+), 94 deletions(-) diff --git a/AoC/2024/day_2/day_2.adb b/AoC/2024/day_2/day_2.adb index dbd8164..5ef232f 100644 --- a/AoC/2024/day_2/day_2.adb +++ b/AoC/2024/day_2/day_2.adb @@ -4,7 +4,7 @@ with Ada.Containers.Vectors; procedure Day_2 is type Safety is (Unsafe, Safe); - type Ordering is (Increasing, Decreasing, Equal); + type Ordering is (Increasing, Decreasing); type Levels is array (Natural range <> ) of Integer; @@ -15,123 +15,129 @@ procedure Day_2 is end record; 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 - record - Order : Orderings( 0 .. 9 ) := (others => Equal); - Diff : Levels( 0 .. 9 ) := (others => 0); - Count : Natural := 0; - end record; - - function Calculate_Levels( R : Report ) return Level_Report is - Out_R : Level_Report; + function Is_Level_Safe( First, Second : Integer; Prev : Ordering ) return Safety is + Diff : Integer := Second - First; + ADiff : Natural := abs(Diff); 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 Diff : Integer := R.Values(I+1) - R.Values(I); begin if Diff > 0 then - Out_R.Order(I) := Increasing; + Sum := @ + 1; elsif Diff < 0 then - Out_R.Order(I) := Decreasing; - else - Out_R.Order(I) := Equal; + Sum := @ - 1; end if; - - Out_R.Diff(I) := abs(Diff); end; end loop; - Out_R.Count := R.Count - 1; - - return Out_R; - end Calculate_Levels; - - function Calculate_Report( Levels : Level_Report ) return Safety is - begin - if Levels.Diff(0) < 1 or Levels.Diff(0) > 3 then + if abs(Sum) < Chances then return Unsafe; + else + if Sum > 0 then + Prev_Order := Increasing; + else + Prev_Order := Decreasing; + end if; end if; - for I in 0 .. Levels.Count - 2 loop - if not (Levels.Order(I+1) = Levels.Order(I)) then - return Unsafe; - end if; - if Levels.Diff(I+1) < 1 or Levels.Diff(I+1) > 3 then - return Unsafe; - end if; + while Skip_Ahead <= Chances loop + Current_Chances := Chances - Skip_Ahead; + + 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 + + while (Second_Index < R.Count) and (Current_Chances >= 0) loop + if Is_Level_Safe(R.Values(First_Index), R.Values(Second_Index), Prev_Order) = Safe then + First_Index := Second_Index; + Second_Index := @ + 1; + else + Current_Chances := @ - 1; + Second_Index := @ + 1; + end if; + end loop; + + if Current_Chances >= 0 then + return Safe; + else + Skip_Ahead := @ + 1; + end if; + end; end loop; - if Levels.Order(0) = Equal then - return Unsafe; - end if; - - return Safe; + return Unsafe; end Calculate_Report; - function Calculate_Report_Lossy( R : Report ) return Safety 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 + function Get_Safe_Reports( R : Reports; Chances : Integer ) return Natural is Sum : Natural := 0; begin for Rep of R loop - declare - Result : Safety := Calculate_Report_Lossy(Rep); - begin - if Result = Safe then - Sum := @ + 1; - 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; + if Calculate_Report( Rep, Chances ) = Safe then + Sum := @ + 1; + end if; end loop; return Sum; 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 begin return Integer'Value(Num); @@ -195,7 +201,10 @@ procedure Day_2 is File_Name : String := "input_day_2.txt"; + Reps : Reports := Read_File(File_Name); + begin - Put_Line("Day 2 1:" & Get_Safe_Reports(Read_File(File_Name))'Image); - Put_Line("Day 2 2:" & Get_Safe_Reports_Lossy(Read_File(File_Name))'Image); + Put_Line("Day 2 1:" & Get_Safe_Reports(Reps, 0)'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;