Powered by AppSignal & Oban Pro
Would you like to see your link here? Contact us

Day 7: The Treachery of Whales

day7/solution.livemd

Day 7: The Treachery of Whales

Part 1

I think the solution to this challenge is extremely simple and is basically calculating the integer median of all values.

That is intuitively the position that will cause the least number of movements overall to get all crabs aligned.

If you think, for example, of it as a modelling problem where each crab is in an increasing x position and a y position equivalent to its given current alignment and we’d like to fit a horizontal line, the minimization will result in the median.

I suppose it will not be the average, because a single outlier (a hermit crab!! lol) could cause all crabs to have to move, whereas the median will make the hermit move alot, and the rest of the crabs move just a little.

Let’s try it!

input = "16,1,2,0,4,2,7,1,2,14"

positions = input |> String.split(",") |> Enum.map(&String.to_integer/1)
[16, 1, 2, 0, 4, 2, 7, 1, 2, 14]

The median is basically the number between the middle elements of the ordered set

median = positions |> Enum.sort() |> IO.inspect() |> Enum.at(div(length(positions), 2))

positions
|> Enum.reduce(0, &(abs(&1 - median) + &2))
[0, 1, 1, 2, 2, 2, 4, 7, 14, 16]
37
puzzle_input =
  "1101,1,29,67,1102,0,1,65,1008,65,35,66,1005,66,28,1,67,65,20,4,0,1001,65,1,65,1106,0,8,99,35,67,101,99,105,32,110,39,101,115,116,32,112,97,115,32,117,110,101,32,105,110,116,99,111,100,101,32,112,114,111,103,114,97,109,10,315,1081,464,841,82,86,483,2,1163,1189,184,369,510,519,13,0,1127,899,1157,1018,3,1014,507,330,20,4,374,641,1768,773,1130,827,1132,166,217,3,460,51,767,484,109,48,143,977,790,1139,426,50,391,537,1140,478,41,1560,1457,180,814,138,338,751,1164,1088,157,1168,163,880,1477,1236,496,446,582,215,840,19,6,626,84,473,977,191,281,1166,230,643,772,985,65,290,389,409,366,1339,499,4,20,262,58,1294,1043,1625,77,284,415,220,583,110,70,639,424,273,28,669,920,1580,434,377,24,446,113,100,1040,1271,874,73,629,87,88,877,959,896,1478,377,14,409,1150,975,547,181,367,549,941,923,1175,19,293,91,1079,495,8,156,807,1423,337,544,505,694,45,968,349,472,1333,43,1339,139,383,676,105,81,0,208,397,628,1033,429,1642,1635,397,675,273,263,165,965,600,571,834,13,411,592,2,49,1706,1288,241,115,5,1046,1301,688,474,34,817,200,410,233,181,173,8,124,527,1266,238,33,482,1066,1074,381,197,139,828,45,843,133,823,265,685,463,680,958,561,101,258,339,7,578,38,176,1127,57,147,186,795,685,769,27,547,1089,252,125,1182,63,907,1300,1022,1040,613,1082,964,693,7,130,259,75,5,1172,1380,362,1203,1936,666,50,600,482,497,106,724,153,52,480,848,1216,814,156,730,20,831,441,160,81,4,750,81,537,1006,320,493,526,63,112,0,467,65,50,1228,105,121,1123,568,232,6,946,436,429,1116,309,13,1471,60,15,1143,385,34,374,140,413,24,593,97,7,127,504,221,1380,585,661,326,709,91,1323,722,256,346,43,296,5,477,356,182,476,1109,107,1137,151,1303,201,796,760,1144,51,410,694,68,49,78,32,24,781,1347,34,16,137,999,47,94,788,409,188,853,362,862,576,386,621,52,341,908,362,160,1373,1118,368,222,125,389,327,168,136,197,445,206,564,517,95,547,765,526,584,1115,150,674,312,246,33,168,266,92,535,23,442,518,145,358,531,150,174,545,1525,576,1721,5,1369,1112,787,9,1266,955,265,385,823,1129,62,1045,1682,4,157,268,449,77,340,12,401,818,28,722,232,116,41,1250,504,1448,108,748,161,42,634,975,1046,50,371,474,669,271,910,263,1091,566,35,181,29,163,652,497,1108,878,448,44,241,319,585,530,176,596,1334,2,181,63,807,53,738,851,952,502,127,983,59,0,22,1143,393,75,292,105,592,649,1709,1505,0,1,634,1815,1505,23,145,117,1286,1641,372,273,348,688,113,0,823,1829,607,89,110,30,667,997,987,354,804,766,243,211,783,76,152,401,667,477,555,280,504,252,287,448,495,59,83,353,219,112,198,174,1496,3,803,765,1166,446,1062,451,1365,778,115,381,102,3,209,236,8,87,169,145,139,1032,1702,176,525,436,73,5,35,1512,198,370,70,358,135,46,153,473,48,521,315,709,473,136,363,60,256,1048,81,1037,59,456,343,18,935,51,1329,1624,1134,189,526,578,190,1635,396,211,583,83,165,89,1073,1251,241,607,833,105,183,300,998,1849,1127,734,325,767,215,1375,326,300,228,246,1221,204,40,718,26,231,31,608,453,11,169,104,380,339,281,23,778,1023,385,251,972,47,101,779,122,471,1003,45,261,1223,493,48,92,948,1269,519,319,720,3,145,1509,24,65,219,25,687,781,465,38,554,244,99,335,55,817,13,1009,319,112,93,537,454,298,393,1217,941,673,356,142,213,140,422,11,1050,143,270,21,58,145,188,486,821,942,1,420,844,451,45,560,701,758,624,149,267,63,286,236,339,120,1145,747,1835,471,540,684,1549,204,285,0,335,387,729,81,17,162,24,25,212,64,1051,373,629,187,34,228,562,362,25,16,475,114,1092,736,1014,896,91,1516,93,210,12,432,88,411,353,638,480,418,1087,45,818,9,606,568,286,507,139,1281,1709,228,510,218,484,498,559,948,88,207,968,84,142,364,44,15,542,133,363,299,753,212,313,277,589,628,821,1481,2,59,18,125,644,324,38,704,559,387,72,36,15,231,647,57,202,1140,311,1125,538,611,192,459,1,598,310,211,406,1868,624,1126,373,369,202,373,1309,903,554,202,259,1174,254,436,997,39,513,811,28,948,434,428,426,419,167,320,748,145,447,735,1128,440,232,211,481,332,591,4,325,875,45,834,269,527,361,603,488,1071,166,1734,326,241,1434,899,738,225,240,1407,6,1197,743,850,25,136,241"

positions = puzzle_input |> String.split(",") |> Enum.map(&String.to_integer/1)

median = positions |> Enum.sort() |> Enum.at(div(length(positions), 2))

positions
|> Enum.reduce(0, &(abs(&1 - median) + &2))
342534

Part 2

For part 2, the cost of moving changes linearly with distance, so median will not work, and I am not seeing an intuitive way to solve this…

moving 4 steps costs in fact (1 + 2 + 3 + 4), this is an arithmethical progression sum, which can be calculated with

$Sn = n/2[2a + (n − 1) × d]$

where,

  • a = first term of arithmetic progression,
  • n = number of terms in the arithmetic progression, and
  • d = common difference

In our case, it can be simplified to

$Sn = \frac {n[2 + (n − 1)]} 2$

And our $n$ is actually the distance between crab $m$ current position $x_m$ and the proposed alignment position $p$.

$n = |p - x_m|$

Let’s think in optimization terms…

The cost function we need to minimize is:

$\sum_{\substack{0 Enum.reduce(0, fn x_i, acc ->

n = abs(p - x_i)
acc + div(n * (2 + (n - 1)), 2)

end)

cost for the initial position 342534 was 58502586209944

decrementing the position to 342533 gets 58502244150530

incrementing the position to 342535 gives 58502928270358

it means we need to decrement until it starts incrementing

58502586209944

defmodule Recur do def recur(pos, k) do

recur(pos, k - 1, cost(pos, k))

end

def recur(pos, k, last_cost) do

case cost(pos, k) do
  # we want to exit when cost starts rising
  c when c > last_cost -> last_cost
  c -> recur(pos, k - 1, c)
end

end

defp cost(pos, k) do

pos
|> Enum.reduce(0, fn x_i, acc ->
  n = abs(k - x_i)
  acc + div(n * (2 + (n - 1)), 2)
end)

end end

Recur.recur(positions, p)

94004208