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
00
byte 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 >> 1
y 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 if
para 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:
$x
y $y
son las entradas en formato hexadecimal, por lo que primero tienen el byte menos significativo .
Por lo tanto, en lugar de lo x >> 8
que 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 << 8
es simplemente $y = "00$y"
.
La result
realidad es una matriz numérica, @r
. Al final, cada elemento @r
contiene 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 while
bucle:
El vacío @r
al principio claramente no tiene valores mayores que 0xFF (porque no tiene valores en absoluto). Esto concluye el caso base.
Ahora, dado que @r
contiene solo bytes individuales al comienzo de cada while
iteración:
El for
bucle 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 $x
y $y
:
Por lo tanto, $e*hex
es 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*hex
está en el rango 0 — 0xFF00
.
Por lo tanto, $s >> 8
siempre es un byte.
$y
crece un extra 00
en cada iteración del while
bucle:
Por lo tanto, en cada iteración del while
ciclo, el for
ciclo interno se ejecuta por una iteración más que en la while
iteración anterior .
Por lo tanto, $r[++$i] += $s >> 8
en la última iteración del for
bucle siempre se agrega $s >> 8
a 0
, y ya establecimos que $s >> 8
siempre es un byte.
Por lo tanto, el último valor almacenado @r
al final del for
ciclo también es un solo byte.
Esto concluye un desafío maravilloso y emocionante. ¡Muchas gracias por publicarlo!