Perl, 137 caractères
($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
Avertissements
- Imprime parfois un
00octet supplémentaire à la fin du résultat. Bien sûr, le résultat est toujours correct, même avec cet octet supplémentaire.
- Imprime un espace supplémentaire après le dernier octet hexadécimal du résultat.
Explication
L'explication va être un peu longue, mais je pense que la plupart des gens ici trouveront cela intéressant.
Tout d'abord, quand j'avais 10 ans, on m'a appris le petit truc suivant. Vous pouvez multiplier deux nombres positifs avec ceci. Je vais décrire cela en utilisant l'exemple de 13 × 47. Vous commencez par écrire le premier nombre, 13, et en le divisant par 2 (arrondir à chaque fois) jusqu'à ce que vous atteigniez 1:
13
6
3
1
Maintenant, à côté du 13, vous écrivez l'autre nombre, 47, et continuez à le multiplier par 2 le même nombre de fois:
13 47
6 94
3 188
1 376
Maintenant, vous biffez toutes les lignes où le nombre sur la gauche est pair . Dans ce cas, ce n'est que le 6. (Je ne peux pas barrer dans le code, donc je vais juste le supprimer.) Enfin, vous ajoutez tous les chiffres restants à droite:
13 47
3 188
1 376
----
611
Et c'est la bonne réponse. 13 × 47 = 611.
Maintenant, puisque vous êtes tous des geeks informatiques, vous aurez réalisé que ce que nous faisons réellement dans les colonnes de gauche et de droite est x >> 1et y << 1, respectivement. De plus, nous ajoutons le yseul if x & 1 == 1. Cela se traduit directement par un algorithme, que j'écrirai ici en pseudocode:
input x, y
result = 0
while x > 0:
if x & 1 == 1:
result = result + y
x = x >> 1
y = y << 1
print result
Nous pouvons réécrire le ifpour utiliser une multiplication, puis nous pouvons facilement le changer pour qu'il fonctionne octet par octet au lieu de bit par bit:
input x, y
result = 0
while x > 0:
result = result + (y * (x & 255))
x = x >> 8
y = y << 8
print result
Cela contient toujours une multiplication avec y, qui est de taille arbitraire, nous devons donc aussi changer cela en boucle. Nous le ferons en Perl.
Maintenant, traduisez tout en Perl:
$xet $ysont les entrées au format hexadécimal, donc elles ont d' abord l' octet le moins significatif .
Ainsi, au lieu de x >> 8 moi $x =~ s/.. *//s. J'ai besoin de l'espace + étoile parce que le dernier octet pourrait ne pas avoir d'espace (pourrait utiliser l'espace + ?aussi). Cela place automatiquement l'octet supprimé ( x & 255) dans $&.
y << 8est tout simplement $y = "00$y".
Le resultest en fait un tableau numérique,@r . À la fin, chaque élément de @rcontient un octet de la réponse, mais à mi-chemin du calcul, il peut contenir plus d'un octet. Je vais vous prouver ci-dessous que chaque valeur ne dépasse jamais deux octets (16 bits) et que le résultat est toujours un octet à la fin.
Voici donc le code Perl démêlé et commenté:
# 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
Maintenant, pour la preuve que cela génère toujours des octets et que le calcul ne génère jamais de valeurs supérieures à deux octets. Je vais le prouver par induction sur la whileboucle:
Le vide @rau début n'a clairement aucune valeur supérieure à 0xFF (car il ne contient aucune valeur). Ceci conclut le scénario de base.
Maintenant, étant donné qu'il @rne contient que des octets uniques au début de chaquewhile itération:
La forboucle contient explicitement &=toutes les valeurs du tableau de résultats avec 255 sauf la dernière , nous n'avons donc qu'à regarder cette dernière.
Nous savons que nous supprimons toujours un seul octet $xet $y:
Par conséquent, $e*hexest une multiplication de deux valeurs à un octet, ce qui signifie qu'il est dans la plage0 — 0xFE01 .
Par l'hypothèse inductive, $r[$i]est un octet; est donc $s = $r[$i] += $e*hexdans la plage0 — 0xFF00 .
Par conséquent, $s >> 8est toujours un octet.
$yaugmente de plus 00à chaque itération de la whileboucle:
Par conséquent, à chaque itération de la whileboucle, la forboucle interne s'exécute pour une itération de plus que lors de l' whileitération précédente .
Par conséquent, la $r[++$i] += $s >> 8dans la dernière itération de la forboucle ajoute toujours $s >> 8à 0, et nous avons déjà établi que $s >> 8est toujours un octet.
Par conséquent, la dernière valeur stockée @rà la fin de la forboucle est également un octet unique.
Ceci conclut un défi merveilleux et passionnant. Merci beaucoup de l'avoir posté!