Perl, 137 caratteri
($x,$y)=<>;while($x=~s/.. *//s){$e=hex$&;$i=0;$s=$r[$i]+=$e*hex,$r[$i]&=255,$r[++$i]+=$s>>8 for$y=~/.. */gs;$y="00$y"}printf'%02x 'x@r,@r
Avvertenze
- A volte stampa un
00byte extra alla fine del risultato. Ovviamente il risultato è ancora corretto anche con quel byte extra.
- Stampa uno spazio extra dopo l'ultimo byte esadecimale nel risultato.
Spiegazione
La spiegazione sarà un po 'lunga, ma penso che molte persone qui la troveranno interessante.
Prima di tutto, quando avevo 10 anni, mi è stato insegnato il seguente piccolo trucco. Puoi moltiplicare due numeri positivi per questo. Lo descriverò usando l'esempio di 13 × 47. Inizi scrivendo il primo numero, 13, e dividendolo per 2 (arrotondando ogni volta verso il basso) fino a raggiungere 1:
13
6
3
1
Ora, accanto al 13 scrivi l'altro numero, 47 e continui a moltiplicarlo per 2 lo stesso numero di volte:
13 47
6 94
3 188
1 376
Ora cancelli tutte le linee in cui il numero a sinistra è pari . In questo caso, questo è solo il 6. (Non riesco a fare lo strike-through nel codice, quindi lo rimuoverò.) Infine, aggiungi tutti i numeri rimanenti sulla destra:
13 47
3 188
1 376
----
611
E questa è la risposta giusta. 13 × 47 = 611.
Ora, dato che siete tutti fanatici dei computer, avrete capito che ciò che stiamo effettivamente facendo nelle colonne sinistra e destra è x >> 1e y << 1, rispettivamente. Inoltre, aggiungiamo il ysolo se x & 1 == 1. Questo si traduce direttamente in un algoritmo, che scriverò qui in pseudocodice:
input x, y
result = 0
while x > 0:
if x & 1 == 1:
result = result + y
x = x >> 1
y = y << 1
print result
Possiamo riscrivere il ifper usare una moltiplicazione, e quindi possiamo facilmente cambiarlo in modo che funzioni su una base byte per byte anziché bit per bit:
input x, y
result = 0
while x > 0:
result = result + (y * (x & 255))
x = x >> 8
y = y << 8
print result
Questo contiene ancora una moltiplicazione con ydimensioni arbitrarie, quindi dobbiamo trasformarlo anche in un ciclo. Lo faremo in Perl.
Ora traduci tutto in Perl:
$xe $ysono gli input in formato esadecimale, quindi hanno prima il byte meno significativo .
Quindi, invece di x >> 8me $x =~ s/.. *//s. Ho bisogno di spazio + stella perché l'ultimo byte potrebbe non avere uno spazio su di esso (potrebbe usare anche spazio + ?). Ciò inserisce automaticamente il byte rimosso ( x & 255) in $&.
y << 8è semplicemente $y = "00$y".
La resultrealtà è una matrice numerica, @r. Alla fine, ogni elemento di @rcontiene un byte della risposta, ma a metà del calcolo può contenere più di un byte. Ti dimostrerò di seguito che ogni valore non è mai più di due byte (16 bit) e che il risultato è sempre un byte alla fine.
Quindi ecco il codice Perl svelato e commentato:
# Input x and y
($x, $y) = <>;
# Do the equivalent of $& = x & 255, x = x >> 8
while ($x =~ s/.. *//s)
{
# Let e = x & 255
$e = hex $&;
# For every byte in y... (notice this sets $_ to each byte)
$i = 0;
for ($y =~ /.. */gs)
{
# Do the multiplication of two single-byte values.
$s = $r[$i] += $e*hex,
# Truncate the value in $r[$i] to one byte. The rest of it is still in $s
$r[$i] &= 255,
# Move to the next array item and add the carry there.
$r[++$i] += $s >> 8
}
# Do the equivalent of y = y << 8
$y = "00$y"
}
# Output the result in hex format.
printf '%02x ' x @r, @r
Ora per la prova che ciò genera sempre byte e che il calcolo non genera mai valori maggiori di due byte. Lo dimostrerò per induzione over the whileloop:
Il vuoto @rall'inizio non contiene chiaramente valori superiori a 0xFF (perché non contiene alcun valore). Questo conclude il caso base.
Ora, dato che @rcontiene solo singoli byte all'inizio di ogni whileiterazione:
Il forciclo &=s esplicitamente tutti i valori nella matrice dei risultati con 255 tranne l'ultimo , quindi dobbiamo solo guardare l'ultimo.
Sappiamo che rimuoviamo sempre solo un byte da $xe $y:
Pertanto, $e*hexè una moltiplicazione di due valori a byte singolo, il che significa che è nell'intervallo 0 — 0xFE01.
Per l'ipotesi induttiva, $r[$i]è un byte; pertanto, $s = $r[$i] += $e*hexè nella gamma 0 — 0xFF00.
Pertanto, $s >> 8è sempre un byte.
$ycresce un extra 00in ogni iterazione del whileciclo:
Pertanto, in ogni iterazione del whileciclo, il forciclo interno viene eseguito per un'iterazione in più rispetto a quella whiledell'iterazione precedente .
Pertanto, $r[++$i] += $s >> 8nell'ultima iterazione del forciclo si aggiunge sempre $s >> 8a 0, e abbiamo già stabilito che $s >> 8è sempre un byte.
Pertanto, l'ultimo valore memorizzato @ralla fine del forloop è anche un singolo byte.
Questo conclude una sfida meravigliosa ed eccitante. Grazie mille per averlo pubblicato!