Le but de ce défi est de trouver une implémentation incroyablement courte de la fonction suivante p
, dans le langage de votre choix. Voici le code C l’implémentant (voir
ce lien TIO qui affiche également ses sorties) et une page wikipedia le contenant.
unsigned char pi[] = {
252,238,221,17,207,110,49,22,251,196,250,218,35,197,4,77,
233,119,240,219,147,46,153,186,23,54,241,187,20,205,95,193,
249,24,101,90,226,92,239,33,129,28,60,66,139,1,142,79,
5,132,2,174,227,106,143,160,6,11,237,152,127,212,211,31,
235,52,44,81,234,200,72,171,242,42,104,162,253,58,206,204,
181,112,14,86,8,12,118,18,191,114,19,71,156,183,93,135,
21,161,150,41,16,123,154,199,243,145,120,111,157,158,178,177,
50,117,25,61,255,53,138,126,109,84,198,128,195,189,13,87,
223,245,36,169,62,168,67,201,215,121,214,246,124,34,185,3,
224,15,236,222,122,148,176,188,220,232,40,80,78,51,10,74,
167,151,96,115,30,0,98,68,26,184,56,130,100,159,38,65,
173,69,70,146,39,94,85,47,140,163,165,125,105,213,149,59,
7,88,179,64,134,172,29,247,48,55,107,228,136,217,231,137,
225,27,131,73,76,63,248,254,141,83,170,144,202,216,133,97,
32,113,103,164,45,43,9,91,203,155,37,208,190,229,108,82,
89,166,116,210,230,244,180,192,209,102,175,194,57,75,99,182,
};
unsigned char p(unsigned char x) {
return pi[x];
}
Quel est p
p
est un composant de deux standards cryptographiques russes, à savoir la fonction de hachage Streebog et le chiffrement de bloc Kuznyechik . Dans cet article (et lors des réunions ISO), les concepteurs de ces algorithmes ont affirmé avoir généré le tableau pi
en choisissant des permutations aléatoires de 8 bits.
Implémentations "impossibles"
Il y en a permutations sur 8 bits. Par conséquent, pour une permutation aléatoire donnée, un programme qui l'implémente ne doit pas nécessiter moins de 1683 bits.
Cependant, nous avons trouvé plusieurs implémentations anormalement petites (que nous énumérons ici ), par exemple le programme C suivant:
p(x){unsigned char*k="@`rFTDVbpPBvdtfR@\xacp?\xe2>4\xa6\xe9{z\xe3q5\xa7\xe8",l=0,b=17;while(--l&&x^1)x=2*x^x/128*285;return l%b?k[l%b]^k[b+l/b]^b:k[l/b]^188;}
qui ne contient que 158 caractères et tient donc dans 1264 bits. Cliquez ici pour voir que cela fonctionne.
Nous parlons d'une implémentation courte "impossible" parce que, si la permutation était la sortie d'un processus aléatoire (comme l'affirment ses concepteurs), un programme de cette durée n'existerait pas (voir cette page pour plus de détails).
Mise en oeuvre de référence
Une version plus lisible du code C précédent est:
unsigned char p(unsigned char x){
unsigned char
s[]={1,221,146,79,147,153,11,68,214,215,78,220,152,10,69},
k[]={0,32,50,6,20,4,22,34,48,16,2,54,36,52,38,18,0};
if(x != 0) {
unsigned char l=1, a=2;
while(a!=x) {
a=(a<<1)^(a>>7)*29;
l++;
}
unsigned char i = l % 17, j = l / 17;
if (i != 0) return 252^k[i]^s[j];
else return 252^k[j];
}
else return 252;
}
Le tableau k
est tel que k[x] = L(16-x)
, où L
est linéaire en ce sens L(x^y)==L(x)^L(y)
et où, comme en C, ^
désigne le XOR. Cependant, nous n'avons pas réussi à tirer parti de cette propriété pour raccourcir notre mise en œuvre. Nous ne connaissons aucune structure s
qui pourrait permettre une implémentation plus simple - sa sortie est toujours dans le sous-champ, c.-à- où l’exponentiation est effectuée dans le champ fini. Bien sûr, vous êtes absolument libre d'utiliser une expression plus simple de si vous en trouvez une!s
La boucle while correspond à l'évaluation d'un logarithme discret dans le corps fini à 256 éléments. Cela fonctionne via une recherche simple par force brute: la variable factice a
est définie pour être un générateur du corps fini, et elle est multipliée par ce générateur jusqu'à ce que le résultat soit égal à x
. Quand c'est le cas, nous avons c'est l
le journal discret de x
. Cette fonction n'est pas définie à 0, d'où le cas particulier correspondant à l' if
instruction.
La multiplication par le générateur peut être vue comme une multiplication par dans qui est alors réduite modulo le polynôme . Le rôle de l ' est de s'assurer que la variable reste sur 8 bits. Alternativement, nous pourrions utiliser , auquel cas ce pourrait être un type (ou tout autre type entier). D'autre part, il est nécessaire de commencer par ce que nous devons avoir quand est égal à 1.unsigned char
a
a=(a<<1)^(a>>7)*(256^29)
a
int
l=1,a=2
l=255
x
Plus de détails sur les propriétés de p
sont présentés dans notre article , avec un résumé de la plupart de nos optimisations pour obtenir la mise en œuvre courte précédente.
Règles
Proposez un programme qui implémente la fonction p
en moins de 1683 bits. Plus le programme est court, plus il est anormal, pour une langue donnée, plus c'est court, mieux c'est. Si votre langue est Kuznyechik, Streebog ou p
intégrée, vous ne pouvez pas les utiliser.
La métrique que nous utilisons pour déterminer la meilleure implémentation est la longueur du programme en octets. Nous utilisons la longueur de bit dans notre article académique, mais nous nous en tenons aux octets pour des raisons de simplicité.
Si votre langue ne dispose pas d' une notion claire de la fonction, l' argument ou de sortie, le codage est à vous de définir, mais des trucs comme codant pour la valeur pi[x]
que x
sont évidemment interdites.
Nous avons déjà soumis un document de recherche contenant nos conclusions sur ce sujet. Il est disponible ici . Toutefois, s’il est publié dans un lieu scientifique, nous nous ferons un plaisir de reconnaître les auteurs des meilleures implémentations.
En passant, merci à xnor pour son aide lors de la rédaction de cette question!
la source
1683 bits at most
une restriction stricte [sic?] Ou un objectif?Réponses:
Assemblage AMD64 (78 octets ou 624 bits de code machine)
Assemblage x86 64 bits
Code 64 bits désassemblé
Assemblage 32 bits x86
Code 32 bits désassemblé
la source
uint8_t
arguments sont étendus à 64 bits pour JRCXZ). De plus, si vous écrivez un code dépendant de la position, vous pouvez mettre l'adresse de la table dans un registre avec un 5 octetsmov ebx, imm32
au lieu d'un 6 octetscall
/pop
. Ou utilisez-le comme undisp32
dansmov al, [table + rax]
, mais cela pourrait perdre puisque vous avez déjà deuxxlatb
et unmov
. Le piège call + pop shellcode l'emporte sur le LEA relatif relatif au RIP sur 7 octets avec les données qui suiventret
.CJam ,
72676663 octetses*
répète quelque chose en fonction de l'horodatage actuel, ce qui est un grand nombre, et cela prendrait trop de temps.Version actuellement testable, 64 octets:
Essayez-le en ligne!
Essayez tout en ligne!
Pour exécuter ce code (sur une machine Linux), vous devez ajouter la ligne
en_US.ISO-8859-1 ISO-8859-1
dans/etc/locale.gen
et exécuterlocale-gen
. Ensuite, vous pouvez utiliser:Ou vous pouvez essayer cette version équivalente de 72 octets UTF-8:
Explication
Les caractères de la chaîne sont:
la source
"Ý0$&Ü™ÖD�’\n˜×EO“N"
?Gelée
7159 octetsEssayez-le en ligne!
Vérifier toutes les possibilités
Maintenant, réécrivez en utilisant une version remaniée de la réponse CJam intelligente de jimmy23013, alors assurez-vous de l'evoter aussi! Utilise seulement 472 bits (28,0% de l'approche naïve). @ jimmy23013 a également enregistré un autre octet!
Explication
Étape 1
Étape 2
Étape 3
Approche originale
Gelée ,
71 à66 octetsEssayez-le en ligne!
Vérifier toutes les possibilités
Un lien monadique ou un programme complet qui prend un seul argument et renvoie la valeur pertinente de
pi[x]
. C'est 536 bits, donc moins d'un tiers de la mémoire naïve de pi.Sauvegardé de 3 octets en utilisant la méthode de recherche
l
de la réponse CJam de jimmy23013, assurez-vous donc de l'emporter également!Explication
Étape 1
Étape 2
Étape 3
la source
C (gcc) ,
157 148 140139 octetsAmélioration modeste par rapport à l'exemple C.
Essayez-le en ligne!
C (gcc) ,
150 142 127126 octetsCelui-ci dépend des bizarreries de gcc, x86 et UTF-8.
Essayez-le en ligne!
Merci à @XavierBonnetain pour -1 et un comportement moins indéfini.
la source
05AB1E ,
10110098979594 octets-3 octets grâce à @Grimy .
Essayez-le en ligne ou vérifiez tous les cas de test .
Explication:
La fonction C du port de Xavier Bonnetain (version 1106 bits) à partir d'ici , avec la même amélioration que @ceilingcat apportée dans sa réponse en C pour économiser 3 octets, alors assurez-vous de l'inverser!
Voir cette astuce 05AB1E de mes (sections Comment compresser les grands entiers? Et Comment compresser des listes entières? ) Pour comprendre pourquoi
•α">η≠ε∍$<Θγ\&@(Σα•
est20576992798525946719126649319401629993024
;•α">η≠ε∍$<Θγ\&@(Σα•₅в
est[64,96,114,70,84,68,86,98,112,80,66,118,100,116,102,82,64]
;Ƶ¹
est285
;•¾#kôlb¸ù,-ó"a·ú•
est930891775969900394811589640717060184
;•¾#kôlb¸ù,-ó"a·ú•₅в
est[189,97,46,243,47,37,183,248,106,107,242,96,36,182,249]
; etƵ∞
est188
.la source
s^
=>^
(XOR est commutatif). En fait, n'est-ce pass^_
la même chose queQ
?i==0 || X==0 || X==1
.Stax ,
6564625958 octetsExécuter et déboguer
Malheureusement, ce programme utilise des instructions qui utilisent en interne des instructions stax obsolètes. J'ai juste oublié de mettre à jour leur implémentation. Cela fait apparaître des avertissements parasites, mais les résultats sont toujours corrects.
Ceci est inspiré par l' excellente réponse de jimmy23013 . Certaines parties ont été modifiées pour mieux convenir à Stax.
Les programmes Stax écrits en ASCII imprimable ont une représentation alternative qui enregistre un peu plus de 1 bit par octet, car il n’ya que 95 caractères ASCII imprimables.
Voici la représentation ascii de ce programme au format "lisibilité" avec commentaires.
Exécuter celui-ci
Version modifiée à exécuter pour toutes les entrées 0..255
la source
S
pour pouvoir mis. Vous pouvez obtenir le jeu de puissance de [18 38 36 48], index et réduction de xor. (Je ne connais pas Stax et je ne suis pas sûr que ce serait plus court cependant.)S
opérateur ne sont pas dans le bon ordre pour que cela fonctionne. par exemple"abc"SJ
(un ensemble de "abc" joint à des espaces) produit "a ab abc ac b bc c".Python 3 , 151 octets
Essayez-le en ligne!
Une fonction qui implémente la permutation. Le code utilise uniquement des caractères ASCII 7 bits.
Encode
k
comme une chaîne d’essai Python 3, déplacée^64
dans la plage imprimable. En revanche, ils
est codé en tant que base en 256 chiffres d’une constante numérique et les chiffres sont extraits en tant que[number]>>[shift]*8&255
. Cela était plus court que l'encodages
dans une chaîne en raison du nombre de caractères d'échappement requis, même avec un décalage optimal^160
pour les minimiser.Le calcul discret-log est effectué à l'envers. La mise à jour effectue une
x=x*2^x//128*285
boucle dans le groupe cyclique en simulant la multiplication par la génération, jusqu'à atteindre l'identitéx=1
. Nous commençons le journal discret àl=255
(la longueur du cycle) et le décrémentons à chaque itération. Pour gérer lex=0
cas et le faire ne pas boucler indéfiniment, nous terminons également whenl=0
, ce qui rend lax=0
carte conforme àl=0
celle spécifiée.Python 2 perd sur le fait de ne pas avoir de beaux bytestrings, nous devons donc le faire
map(ord,...)
(ArBo a sauvegardé un octet ici). Cela nous permet d'utiliser/
plutôt que//
pour la division entière.Python 2 , 156 octets
Essayez-le en ligne!
la source
JavaScript (ES6), 139 octets
Similaire à la version de Node.js, mais utilisant des caractères au-delà de la plage ASCII.
Essayez-le en ligne!
JavaScript (Node.js) ,
149148 octetsBasé sur l'implémentation C de Xavier Bonnetain (présentée ici ).
Essayez-le en ligne!
Codage
Dans la réponse originale de Xavier, les tables
s[]
etk[]
sont stockées dans la chaîne suivante:Les 17 premiers caractères sont les représentations ASCII de
k[i] XOR 64
et les 15 suivants sont les représentations ASCII des[i-17] XOR 173
, ous[i-17] XOR 64 XOR 17 XOR 252
.Tous les codes ASCII126 128
k[i] XOR 64
correspondent aux caractères imprimables, mais 7 codes ASCIIs[i-17] XOR 173
dépassent et doivent être échappés. Nous les forçons dans la plage imprimable en soustrayant .Voici ce que nous obtenons:
En inversant le masque binaire formé par la 2 ème ligne du tableau ci-dessus, nous obtenons25801
110010011001001
en binaire ou en décimal.Ce masque binaire est multiplié par , de sorte que nous pouvons directement exécuter un OU au niveau du bit extrait. D'où la formule:128
Bâtiments
NB: Ceci est juste une note de côté, sans rapport avec les réponses ci-dessus.
Ce code montre comment peut être construit en ne mettant XOR que 4 valeurs distinctes ensemble.s
Dans cet exemple, nous utilisons les 15 sous-ensembles non vides de , mais d'autres valeurs peuvent être choisies.{1,11,79,146}
Essayez-le en ligne!
la source
C # (compilateur interactif Visual C #) , 141 octets
Juste un port de la solution exemple, porté en C #.
Essayez-le en ligne!
la source
Python 3 , 182 octets
Essayez-le en ligne!
Python ne va pas gagner le premier prix ici, mais il s’agit toujours d’un parcours de golf sur 10 octets du meilleur programme Python ici .
Python 3 , 176 octets
Essayez-le en ligne!
En tant que lambda, il est encore six octets plus court. Cela me fait mal d'utiliser
if... else
, mais je ne vois pas d'autre option de court-circuit, vu que 0 est une réponse possible.Python 3 , 173 octets
Essayez-le en ligne!
Encore plus court en octets (bien que je ne sois pas sûr des bits, car ce n'est plus du pur ASCII), grâce à ovs.
la source
\x..
évasionsRouille ,
170163 octetsEssayez-le en ligne!
C’est un port de ma solution en C, avec une chaîne légèrement différente, qui ne nécessite pas de xor 17. Je suppose que la plupart des solutions reposent sur la chaîne "@` rFTDVbpPBvdtfR @ \ xacp? \ Xe2> 4 \ xa6 \ xe9 {z \ xe3q5 \ xa7 \ xe8 "peut également être amélioré (changez simplement la chaîne, supprimez le xor 17 et le xor 173 au lieu de 188).
J'ai supprimé l'une des recherches en ajoutant conditionnellement
17*17
àl
, comme nous l'avons fait (plus ou moins) dans la solution de code machine ARM.Rust a des inférences de type et des fermetures, mais ses conversions (même pour les booléens ou entre les entiers) sont toujours explicites, la mutabilité doit être marquée, elle n’a pas d’opérateur ternaire, d’opérations sur les entiers, par défaut, de panique lors du débordement et de mutations (
l+=1
) renvoie l'unité. Je n'ai pas réussi à obtenir une solution plus courte avec les itérateurs, car closures + mapping est encore assez prolixe.Cela semble faire de Rust un très mauvais choix pour le golf. Néanmoins, même dans un langage qui met l'accent sur la lisibilité et la sécurité plutôt que sur la concision, nous sommes beaucoup trop courts.
Mise à jour: utilisé une fonction anonyme, suggérée par manatwork.
la source
let p=
à l'en-tête et ne pas les compter. Pas sûr du;
, comme pour les appels anonymes n'est pas nécessaire: essayez-le en ligne! .05AB1E , 74 octets
La première réponse de Jelly du port de @NickKennedy . Je travaillais directement sur un port de la réponse CJam de @ jimmy23013 , mais j’étais déjà à 78 octets et je devais toujours corriger un bogue, qui aurait donc été plus volumineux. Cela peut certainement encore être joué au golf un peu, cependant.
Essayez-le en ligne ou vérifiez tous les cas de test .
Explication:
Voir cette astuce 05AB1E de mes (sections Comment compresser les grands entiers? Et Comment compresser des listes entières? ) Pour comprendre pourquoi
Ƶf
est142
;•5›@¾ÂÌLìÁŒ©.ðǝš«YWǝŠ•
est29709448685778434533295690952203992295278432248
,ƵŠ
est239
; et•5›@¾ÂÌLìÁŒ©.ðǝš«YWǝŠ•ƵŠв
est[19,48,36,38,18,238,87,24,138,206,92,197,196,86,25,139,129,93,128,207]
.la source