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;
}
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))
{
@ -166,30 +166,26 @@ public class Day10 : IPuzzleSolver<long>
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()));
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 (memory.TryGetValue(targetJoltages, out var knownResult)) return knownResult;
var minCount = 1_000_000_000L;
var patternsToCheck = patternCosts
.Where(pc =>
pc.joltages
.Zip(targetJoltages, (pJolt, tJolt) => (pJolt % 2) == (tJolt % 2))
.All(isEqual => isEqual))
.ToArray();
var joltageMask = GetMask(targetJoltages, level => level % 2 == 1);
var patternsToCheck = patternCosts.GetValueOrDefault(joltageMask, []);
foreach (var (joltageLevels, cost) in patternsToCheck)
{
var nextPattern = targetJoltages.Zip(joltageLevels, (jolts, level) => (jolts - level) / 2).ToArray();
var resultNext = 2 * GetMinCountInternal(nextPattern, patternCosts, memory) + cost;
if (resultNext < minCount)
{
minCount = resultNext;
}
minCount = Math.Min(minCount, resultNext);
}
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++)
{
foreach (var buttonPattern in buttons.Combinations(i))
{
var joltageLevels = GetJoltageLevelsForButtonPattern(patternLength, buttonPattern);
yield return (joltageLevels, buttonPattern.Length);
var mask = GetMask(joltageLevels, level => level % 2 == 1);
yield return (mask,(joltageLevels, buttonPattern.Length));
}
}
@ -226,6 +223,9 @@ public class Day10 : IPuzzleSolver<long>
}
}
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)
{
using var context = new Context();