Dopo aver attraversato il deserto indenni, arriviamo a un’oasi ristoratrice. Il narratore ci fa notare che, sospesa proprio sopra di noi, c’è un’altra isola galleggiante completamente fatta di metallo. Ci aspettiamo di doverla esplorare prima di Natale.

Nel frattempo, decidiamo di studiare l’ecosistema dell’oasi. L’input è un report di come alcuni parametri evolvono nel tempo:

0 3 6 9 12 15
1 3 6 10 15 21
10 13 16 21 30 45

Vogliamo fare una previsione del prossimo valore per ogni serie storica (una singola linea), e per farlo dobbiamo:

  1. Calcolare le differenze dei valori di una serie e ottenere una nuova sequenza.
  2. Ripetere l’operazione finché tutti i nuovi valori sono zero.
  3. Aggiungere uno zero extra e ricostruire i valori precedenti con la stessa regola.

Nell’input di esempio, la prima sequenza di differenze sono tutti 3. La seconda sequenza tutti 0.

0   3   6   9  12  15
  3   3   3   3   3
    0   0   0   0

Aggiungiamo un quinto 0 e torniamo indietro: il numero extra della seconda riga è 3, perché la differenza fa lo zero che abbiamo appena aggiunto. Stessa cosa per le due righe precedenti. Quello che ci serve, quindi, è l’ultimo elemento di ogni riga che andrà sommato all’ultimo elemento della riga precedente. Alla fine, dobbiamo calcolare il totale di tutti i numeri che abbiamo aggiunti.

La soluzione è elegante e compatta grazie a Fold e NestWhileList:

With[{data = IntegerList /@ SplitLines@ex9},
 Total[
   Fold[{Last@#1 + Last@#2}&,
     Reverse@NestWhileList[
       Differences, #, !AllTrue[#, (# == 0 &)] &]]& /@ data, 2]
 ]

Part 2

Nonostante sia un problema del weekend, la seconda parte è altrettanto semplice. Ci chiede di fare la stessa cosa ma considerando i primi numeri di ogni serie. L’unica cosa che cambia è l’operazione da fare con i primi numeri.

  • Part 1: $x - p = c$ quindi $x = p + c$, dove $p$ e $c$ sono gli ultimi numeri della riga precedente ($p$) e quella corrente ($c$).
  • Part 2: $p - x = c$, quindi $x = p - c$. Qui $p$ e $c$ sono i primi valori della precedente/corrente.

La soluzione è identica, cambia solo la funzione di Fold che diventa {First@#2 - First@#1} &.

Dando un’occhiata al mio input (non l’esempio), per un attimo ho temuto che queste serie storiche impiegassero troppi step a convergere a zero, generando quindi delle liste troppo grandi da tenere in memoria. Non è stato così, fortunatamente, e ciò mi ha permesso di completare l’intero puzzle in circa cinque minuti – incluso il tempo di lettura dell’input, che leggo sempre per intero. Perché? Mi piace pensare che leggere l’intero input sia anche una forma di rispetto verso chi ci ha messo così tanto tempo a preparare tutto ciò. Un po’ come rimanere in sala al cinema durante i titoli di coda: se abbiamo potuto guardare il film appena concluso, è solo grazie a tutte le persone menzionate alla fine.