Perl, 137 caracteres
($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
Advertencias
- A veces imprime un
00byte adicional al final del resultado. Por supuesto, el resultado sigue siendo correcto incluso con ese byte adicional.
- Imprime un espacio extra después del último byte hexadecimal en el resultado.
Explicación
La explicación va a ser un poco larga, pero creo que la mayoría de la gente aquí la encontrará interesante.
En primer lugar, cuando tenía 10 años, me enseñaron el siguiente truco. Puedes multiplicar dos números positivos con esto. Describiré esto usando el ejemplo de 13 × 47. Comienzas escribiendo el primer número, 13, y dividiéndolo entre 2 (redondeando hacia abajo cada vez) hasta llegar a 1:
13
6
3
1
Ahora, al lado del 13, escribe el otro número, 47, y sigue multiplicándolo por 2 la misma cantidad de veces:
13 47
6 94
3 188
1 376
Ahora tachas todas las líneas donde el número de la izquierda es par . En este caso, esto es solo el 6. (No puedo hacer tachado en el código, así que lo eliminaré). Finalmente, agrega todos los números restantes a la derecha:
13 47
3 188
1 376
----
611
Y esta es la respuesta correcta. 13 × 47 = 611.
Ahora, como todos ustedes son geeks de la computadora, se habrán dado cuenta de que lo que realmente estamos haciendo en las columnas izquierda y derecha es x >> 1y y << 1, respectivamente. Además, agregamos el yúnico si x & 1 == 1. Esto se traduce directamente en un algoritmo, que escribiré aquí en pseudocódigo:
input x, y
result = 0
while x > 0:
if x & 1 == 1:
result = result + y
x = x >> 1
y = y << 1
print result
Podemos volver a escribir el ifpara usar una multiplicación, y luego podemos cambiar esto fácilmente para que funcione byte a byte en lugar de bit a bit:
input x, y
result = 0
while x > 0:
result = result + (y * (x & 255))
x = x >> 8
y = y << 8
print result
Esto todavía contiene una multiplicación con y, que es de tamaño arbitrario, por lo que también debemos cambiar eso en un ciclo. Lo haremos en Perl.
Ahora traduzca todo a Perl:
$xy $yson las entradas en formato hexadecimal, por lo que primero tienen el byte menos significativo .
Por lo tanto, en lugar de lo x >> 8que hago $x =~ s/.. *//s. Necesito el espacio + estrella porque el último byte podría no tener espacio (también podría usar espacio + ?). Esto coloca automáticamente el byte eliminado ( x & 255) en $&.
y << 8es simplemente $y = "00$y".
La resultrealidad es una matriz numérica, @r. Al final, cada elemento @rcontiene un byte de la respuesta, pero a la mitad del cálculo puede contener más de un byte. Te demostraré a continuación que cada valor nunca tiene más de dos bytes (16 bits) y que el resultado es siempre un byte al final.
Así que aquí está el código Perl descifrado y comentado:
# 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
Ahora para la prueba de que esto siempre genera bytes , y que el cálculo nunca genera valores superiores a dos bytes. Lo probaré por inducción sobre el whilebucle:
El vacío @ral principio claramente no tiene valores mayores que 0xFF (porque no tiene valores en absoluto). Esto concluye el caso base.
Ahora, dado que @rcontiene solo bytes individuales al comienzo de cada whileiteración:
El forbucle explícitamente &=muestra todos los valores en la matriz de resultados con 255 excepto el último , por lo que solo tenemos que mirar ese último.
Sabemos que siempre eliminamos solo un byte $xy $y:
Por lo tanto, $e*hexes una multiplicación de dos valores de un solo byte, lo que significa que está en el rango 0 — 0xFE01.
Por la hipótesis inductiva, $r[$i]es un byte; por lo tanto, $s = $r[$i] += $e*hexestá en el rango 0 — 0xFF00.
Por lo tanto, $s >> 8siempre es un byte.
$ycrece un extra 00en cada iteración del whilebucle:
Por lo tanto, en cada iteración del whileciclo, el forciclo interno se ejecuta por una iteración más que en la whileiteración anterior .
Por lo tanto, $r[++$i] += $s >> 8en la última iteración del forbucle siempre se agrega $s >> 8a 0, y ya establecimos que $s >> 8siempre es un byte.
Por lo tanto, el último valor almacenado @ral final del forciclo también es un solo byte.
Esto concluye un desafío maravilloso y emocionante. ¡Muchas gracias por publicarlo!