# Advent of Code Day 17–Filling the Fridge

- Posted in:
- Advent of Code
- C#
- F#
- LINQ

In day 17 of the Advent of Code challenge, we have 150 litres of eggnog and need to work out all the ways of using our various containers to store that amount. The brute force solution to this problem involves checking the “power set” of our containers, but the solution I show in C# and F# performs much faster than this.

Here’s my C# code, which uses a recursive function called `Distribute`

to return all the combinations of containers whose sizes add up to exactly 150

void Main() { var sizes = File.ReadAllLines("day17.txt") .Select(int.Parse) .ToList(); var combs = Distribute(new List<int>(), sizes, 150).ToList(); combs.Count.Dump("a"); var min = combs.Min(p => p.Count); combs.Count(p => p.Count == min).Dump("b"); } IEnumerable<List<int>> Distribute(List<int> used, List<int> pool, int amount) { var remaining = amount - used.Sum(); for (int n = 0; n < pool.Count; n++) { var s = pool[n]; if (pool[n] > remaining) continue; var x = used.ToList(); x.Add(s); if (s == remaining) { yield return x; } else { var y = pool.Skip(n+1).ToList(); foreach (var d in Distribute(x, y, amount)) { yield return d; } } } }

And here’s a slower but more concise C# solution using power sets (note that there was a bug in the code when I showed this in the video)

var containers = new int[] { 11, 30, 47, 31, 32, 36, 3, 1, 5, 3, 32, 36, 15, 11, 46, 26, 28, 1, 19, 3 }; var powerset = Enumerable.Range(1, (1 << containers.Length) - 1) .Select(n => containers.Where((_, bit) => ((1 << bit) & n) != 0).ToList()); var solutions = powerset.Where(c => c.Sum() == 150).ToArray(); solutions.Length.Dump("a"); var min = solutions.Min(s => s.Count); solutions.Count(s => s.Count == min).Dump("b");

And here’s my F# solution which uses the recursive method again for performance, and also enjoys other performance benefits due to F#’s immutable lists:

let containers = "day17.txt" |> File.ReadAllLines |> Seq.map int |> Seq.toList let rec distribute used pool target runningTotal = seq { match pool with | h::tail -> if h + runningTotal = target then yield h::used |> List.toArray elif h + runningTotal < target then yield! distribute (h::used) tail target (h + runningTotal) yield! distribute used tail target runningTotal | _ -> () } let perms = distribute [] containers 150 0 |> Seq.toArray perms.Length |> printfn "a: %d" let m = perms |> Seq.map (fun f -> f.Length) |> Seq.min perms |> Seq.filter (fun a -> a.Length = m) |> Seq.length |> printfn "b: %d"