Refactor Day10 to optimize pattern cost calculation by introducing bitmasking and grouping logic (speedup from 7 sec to 0.2 sec)

This commit is contained in:
Sebastian Lindemeier 2025-12-12 15:03:01 +01:00
parent aaf32260d0
commit 1873ce8e66
1 changed files with 16 additions and 16 deletions

View File

@ -155,7 +155,7 @@ public class Day10 : IPuzzleSolver<long>
yield break; yield break;
} }
foreach (var (pattern, cost) in GetPatternCosts(lampState.Length, buttons)) foreach (var (_,(pattern, cost)) in GetPatternCosts(lampState.Length, buttons))
{ {
if (pattern.Zip(lampState, (pat, lam) => (pat % 2 == 1) == lam).All(x => x)) if (pattern.Zip(lampState, (pat, lam) => (pat % 2 == 1) == lam).All(x => x))
{ {
@ -166,30 +166,26 @@ public class Day10 : IPuzzleSolver<long>
private long GetMinCountButtonPressesForJoltageLevels(int[] joltages, int[][] buttons) private long GetMinCountButtonPressesForJoltageLevels(int[] joltages, int[][] buttons)
{ {
var patternCosts = GetPatternCosts(joltages.Length, buttons).ToArray(); var patternCosts = GetPatternCosts(joltages.Length, buttons)
.GroupBy(x => x.Item1)
.ToDictionary(x => x.Key, x => x.Select(y => y.Item2).ToArray());
var res = GetMinCountInternal(joltages, patternCosts, new Dictionary<int[], long>(new IntArrayEqualityComparer())); var res = GetMinCountInternal(joltages, patternCosts, new Dictionary<int[], long>(new IntArrayEqualityComparer()));
return res; return res;
static long GetMinCountInternal(int[] targetJoltages, (int[] joltages, int cots)[] patternCosts, Dictionary<int[], long> memory) static long GetMinCountInternal(int[] targetJoltages, Dictionary<uint, (int[] joltages, int cots)[]> patternCosts, Dictionary<int[], long> memory)
{ {
if (targetJoltages.Min() < 0) return 1_000_000_000L;
if (targetJoltages.All(j => j == 0)) return 0; if (targetJoltages.All(j => j == 0)) return 0;
if (memory.TryGetValue(targetJoltages, out var knownResult)) return knownResult; if (memory.TryGetValue(targetJoltages, out var knownResult)) return knownResult;
var minCount = 1_000_000_000L; var minCount = 1_000_000_000L;
var patternsToCheck = patternCosts var joltageMask = GetMask(targetJoltages, level => level % 2 == 1);
.Where(pc => var patternsToCheck = patternCosts.GetValueOrDefault(joltageMask, []);
pc.joltages
.Zip(targetJoltages, (pJolt, tJolt) => (pJolt % 2) == (tJolt % 2))
.All(isEqual => isEqual))
.ToArray();
foreach (var (joltageLevels, cost) in patternsToCheck) foreach (var (joltageLevels, cost) in patternsToCheck)
{ {
var nextPattern = targetJoltages.Zip(joltageLevels, (jolts, level) => (jolts - level) / 2).ToArray(); var nextPattern = targetJoltages.Zip(joltageLevels, (jolts, level) => (jolts - level) / 2).ToArray();
var resultNext = 2 * GetMinCountInternal(nextPattern, patternCosts, memory) + cost; var resultNext = 2 * GetMinCountInternal(nextPattern, patternCosts, memory) + cost;
if (resultNext < minCount) minCount = Math.Min(minCount, resultNext);
{
minCount = resultNext;
}
} }
memory[targetJoltages] = minCount; memory[targetJoltages] = minCount;
@ -197,15 +193,16 @@ public class Day10 : IPuzzleSolver<long>
} }
} }
private IEnumerable<(int[], int)> GetPatternCosts(int patternLength, int[][] buttons) private IEnumerable<(uint, (int[], int))> GetPatternCosts(int patternLength, int[][] buttons)
{ {
yield return ([..Enumerable.Repeat(0, patternLength)], 0); yield return (0,([..Enumerable.Repeat(0, patternLength)], 0));
for (var i = 1; i <= buttons.Length; i++) for (var i = 1; i <= buttons.Length; i++)
{ {
foreach (var buttonPattern in buttons.Combinations(i)) foreach (var buttonPattern in buttons.Combinations(i))
{ {
var joltageLevels = GetJoltageLevelsForButtonPattern(patternLength, buttonPattern); var joltageLevels = GetJoltageLevelsForButtonPattern(patternLength, buttonPattern);
yield return (joltageLevels, buttonPattern.Length); var mask = GetMask(joltageLevels, level => level % 2 == 1);
yield return (mask,(joltageLevels, buttonPattern.Length));
} }
} }
@ -225,6 +222,9 @@ public class Day10 : IPuzzleSolver<long>
return joltagesProduced; return joltagesProduced;
} }
} }
private static uint GetMask(int[] toBeMasked, Func<int, bool> maskPredicate) =>
toBeMasked.Aggregate((uint)0L, (acc, val) => (acc << 1) | (uint)(maskPredicate(val) ? 1 : 0));
private long GetMinCountButtonPressesForJoltagesZ3(int[] joltages, int[][] buttons) private long GetMinCountButtonPressesForJoltagesZ3(int[] joltages, int[][] buttons)
{ {