Das Ziel dieser Herausforderung ist es, eine unglaublich kurze Implementierung der folgenden Funktion p
in der Sprache Ihrer Wahl zu finden. Hier ist C-Code, der es implementiert (siehe
diesen TIO-Link , der auch seine Ausgaben druckt) und eine Wikipedia-Seite, die es enthält.
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];
}
Was ist p
p
ist Bestandteil zweier russischer kryptographischer Standards, nämlich der Hash-Funktion Streebog und der Blockchiffre Kuznyechik . In diesem Artikel (und während ISO-Meetings) behaupteten die Entwickler dieser Algorithmen, dass sie das Array pi
durch Auswahl zufälliger 8-Bit-Permutationen generierten .
"Unmögliche" Implementierungen
Es gibt Permutationen auf 8 Bits. Für eine gegebene zufällige Permutation wird daher nicht erwartet, dass ein Programm, das sie implementiert, weniger als 1683 Bits benötigt.
Wir haben jedoch mehrere ungewöhnlich kleine Implementierungen gefunden (die wir hier auflisten ), zum Beispiel das folgende C-Programm:
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;}
die nur 158 Zeichen enthält und somit in 1264 Bit passt. Klicken Sie hier, um zu sehen, dass es funktioniert.
Wir sprechen von einer "unmöglich" kurzen Implementierung, da, wenn die Permutation die Ausgabe eines zufälligen Prozesses wäre (wie von seinen Designern behauptet), ein Programm dieser kurzen Art nicht existieren würde (siehe diese Seite für weitere Details).
Referenzimplementierung
Eine besser lesbare Version des vorherigen C-Codes ist:
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;
}
Die Tabelle k
ist so, dass k[x] = L(16-x)
, wo L
in dem Sinne linear ist L(x^y)==L(x)^L(y)
, und wo, wie in C, ^
das XOR bezeichnet. Es ist uns jedoch nicht gelungen, diese Eigenschaft zu nutzen, um unsere Implementierung zu verkürzen. Wir kennen keine Struktur s
, die eine einfachere Implementierung ermöglichen könnte - ihre Ausgabe befindet sich jedoch immer im Unterfeld, dh wobei die Exponentiation im endlichen Feld erfolgt. Natürlich steht es Ihnen frei, einen einfacheren Ausdruck zu verwenden, falls Sie einen finden sollten!s
Die while-Schleife entspricht der Auswertung eines diskreten Logarithmus im endlichen Feld mit 256 Elementen. Es funktioniert über eine einfache Brute-Force-Suche: Die Dummy-Variable a
wird als Generator des endlichen Feldes festgelegt und mit diesem Generator multipliziert, bis das Ergebnis gleich ist x
. Wenn dies der Fall ist, haben wir l
das diskrete Protokoll von x
. Diese Funktion ist nicht in 0 definiert, daher der der if
Anweisung entsprechende Sonderfall .
Die Multiplikation mit dem Generator kann als Multiplikation mit in die dann modulo des Polynoms reduziert wird . Die Aufgabe von ist es sicherzustellen, dass die Variable auf 8 Bits bleibt. Alternativ könnten wir verwenden , in welchem Fall ein (oder ein anderer ganzzahliger) Typ sein könnte. Auf der anderen Seite ist es notwendig, mit zu beginnen, da wir haben müssen, wann gleich 1 ist.unsigned char
a
a=(a<<1)^(a>>7)*(256^29)
a
int
l=1,a=2
l=255
x
Weitere Details zu den Eigenschaften von p
finden Sie in unserem Artikel, in dem die meisten Optimierungen beschrieben werden, um die vorherige kurze Implementierung zu erhalten.
Regeln
Schlagen Sie ein Programm vor, das die Funktion p
in weniger als 1683 Bit implementiert . Je kürzer das Programm ist, desto anormaler ist es, je kürzer die Sprache ist, desto besser. Wenn Ihre Sprache Kuznyechik, Streebog oder p
ein eingebautes hat, können Sie sie nicht verwenden.
Die Metrik, mit der wir die beste Implementierung ermitteln, ist die Programmlänge in Byte. Wir verwenden die Bitlänge in unserer wissenschaftlichen Arbeit, aber der Einfachheit halber halten wir uns hier an Bytes.
Wenn Ihre Sprache nicht über eine klare Vorstellung von Funktion, Argument oder Ausgang, ist die Codierung an Ihnen zu definieren, aber Tricks wie kodieren den Wert pi[x]
als x
offensichtlich verboten.
Wir haben bereits ein Forschungspapier mit unseren Ergebnissen zu diesem Thema eingereicht. Es ist hier erhältlich . Sollte es jedoch an einem wissenschaftlichen Ort veröffentlicht werden, werden wir gerne die Autoren der besten Implementierungen auszeichnen.
Übrigens, danke an xnor für seine Hilfe beim Verfassen dieser Frage!
quelle
1683 bits at most
eine strenge Einschränkung oder das Ziel?Antworten:
AMD64-Assembly (78 Byte oder 624 Bit Maschinencode)
64-Bit x86-Assembly
Zerlegter 64-Bit-Code
32-Bit-x86-Assembly
Zerlegter 32-Bit-Code
quelle
uint8_t
Args für JRCXZ auf 64-Bit erweitert werden). Wenn Sie einen positionsabhängigen Code schreiben, können Sie die Tabellenadresse auch in ein Register mit einem 5-Byte-Wertmov ebx, imm32
anstelle eines 6-Byte-Wertscall
/ einfügenpop
. Oder benutze es alsdisp32
inmov al, [table + rax]
, aber das könnte verlieren, da du schon zweixlatb
und ein hastmov
. Der Call + Pop-Shellcode-Trick gewinnt gegenüber dem 7-Byte-RIP-relativen LEA mit den Daten nach demret
.CJam ,
72676663 Byteses*
wiederholt etwas durch den aktuellen Zeitstempel, der eine große Zahl ist, und es würde zu lange dauern, um fertig zu werden.Aktuell testbare Version, 64 Bytes:
Probieren Sie es online!
Probiere alles online aus!
Um diesen Code auszuführen (auf einem Linux - Rechner), müssen Sie die Zeile hinzufügen
en_US.ISO-8859-1 ISO-8859-1
in/etc/locale.gen
und laufenlocale-gen
. Dann könnten Sie verwenden:Oder versuchen Sie diese äquivalente 72-Byte-UTF-8-Version:
Erläuterung
Die Zeichen in der Zeichenfolge sind:
quelle
"Ý0$&Ü™ÖD�’\n˜×EO“N"
?Jelly
7159 BytesProbieren Sie es online!
Überprüfen Sie alle Möglichkeiten
Jetzt neu geschrieben eine überarbeitete Version mit Antwort jimmy23013 cleveren CJam so sicher sein , auch , dass man upvote! Verwendet nur 472 Bit (28,0% des naiven Ansatzes). @ jimmy23013 hat auch ein weiteres Byte gespeichert!
Erläuterung
Bühne 1
Stufe 2
Stufe 3
Ursprünglicher Ansatz
Jelly ,
7166 BytesProbieren Sie es online!
Überprüfen Sie alle Möglichkeiten
Ein monadischer Link oder ein vollständiges Programm, das ein einzelnes Argument verwendet und den relevanten Wert von zurückgibt
pi[x]
. Das sind 536 Bit, also weniger als ein Drittel der naiven Speicherung von pi.3 Bytes gespart, indem die Methode zum Finden
l
aus der CJam-Antwort von jimmy23013 verwendet wurde. Stellen Sie also sicher, dass Sie auch diese positiv bewerten!Erläuterung
Bühne 1
Stufe 2
Stufe 3
quelle
C (gcc) ,
157 148 140139 BytesBescheidene Verbesserung gegenüber dem C-Beispiel.
Probieren Sie es online!
C (GCC) ,
150 142 127126 BytesDieser hängt von den Macken von gcc und x86 und UTF-8 ab.
Probieren Sie es online!
Vielen Dank an @XavierBonnetain für -1 und weniger undefiniertes Verhalten.
quelle
05AB1E ,
10110098979594 Bytes-3 Bytes dank @Grimy .
Probieren Sie es online aus oder überprüfen Sie alle Testfälle .
Erläuterung:
Port von Xavier Bonnetains C-Funktion (1106-Bit-Version) mit der gleichen Verbesserung, die @ceilingcat in seiner C-Antwort vorgenommen hat , um 3 Bytes zu sparen.
Sehen Sie sich meinen Tipp 05AB1E an (Abschnitte Wie komprimiere ich große Ganzzahlen? Und Wie komprimiere ich Ganzzahlenlisten? ) , Um zu verstehen, warum dies so
•α">η≠ε∍$<Θγ\&@(Σα•
ist20576992798525946719126649319401629993024
;•α">η≠ε∍$<Θγ\&@(Σα•₅в
ist[64,96,114,70,84,68,86,98,112,80,66,118,100,116,102,82,64]
;Ƶ¹
ist285
;•¾#kôlb¸ù,-ó"a·ú•
ist930891775969900394811589640717060184
;•¾#kôlb¸ù,-ó"a·ú•₅в
ist[189,97,46,243,47,37,183,248,106,107,242,96,36,182,249]
; undƵ∞
ist188
.quelle
s^
=>^
(XOR ist kommutativ). Ist das nichts^_
dasselbe wieQ
?i==0 || X==0 || X==1
.Stax ,
6564625958 BytesFühren Sie es aus und debuggen Sie es
Leider verwendet dieses Programm einige Anweisungen, die intern einige veraltete stax-Anweisungen verwenden. Ich habe nur vergessen, ihre Implementierung zu aktualisieren. Dies führt dazu, dass eine falsche Warnung angezeigt wird, die Ergebnisse jedoch immer noch korrekt sind.
Dies ist inspiriert von jimmy23013s exzellenter Antwort . Einige Teile wurden geändert, um besser zu stax zu passen.
In druckbarem ASCII geschriebene Stax-Programme haben eine alternative Darstellung, die etwas mehr als 1 Bit pro Byte speichert, da nur 95 druckbare ASCII-Zeichen vorhanden sind.
Hier ist die ASCII-Darstellung dieses Programms, die für "Lesbarkeit" mit Kommentaren formatiert ist.
Führen Sie dieses aus
Geänderte Version für alle Eingänge 0..255
quelle
S
für Leistung gesorgt. Sie könnten den Potenzsatz von [18 38 36 48] erhalten, indexieren und durch xor reduzieren. (Ich kenne Stax nicht und ich bin mir nicht sicher, ob es kürzer sein würde.)S
Betreiber erstellt wurden, ist nicht in der richtigen Reihenfolge, damit dies funktioniert. Beispiel"abc"SJ
(Powerset von "ABC" mit Räumen verbunden ist ) erzeugt "ein abc ac b bc c ab".Python 3 , 151 Bytes
Probieren Sie es online!
Eine Funktion, die die Permutation implementiert. Der Code verwendet nur 7-Bit-ASCII-Zeichen.
Codiert
k
als Python 3-Bytestring, verschoben^64
in den druckbaren Bereich. Im Gegensatz dazus
wird als Basis 256 Ziffern einer numerischen Konstante codiert und die Ziffern werden als extrahiert[number]>>[shift]*8&255
. Dies war kürzer als die Codierungs
in einer Zeichenfolge, da dies eine Anzahl von Escape-Zeichen erforderte, selbst bei einer optimalen Verschiebung, um diese^160
zu minimieren.Die Berechnung des diskreten Protokolls erfolgt rückwärts. Das Update durchläuft eine
x=x*2^x//128*285
Schleife in der zyklischen Gruppe, indem es das Multiplizieren mit dem Erzeuger simuliert, bis es die Identität erreichtx=1
. Wir beginnen das diskrete Protokoll beil=255
(der Zykluslänge) und dekrementieren es bei jeder Iteration. Um denx=0
Fall zu behandeln und es nicht für immer zu schleifen, beenden wir auch wannl=0
, wodurch diex=0
Zuordnung zul=0
wie angegeben erfolgt.Python 2 verliert, weil es keine netten Bytestrings gibt, also müssen wir es tun
map(ord,...)
(ArBo hat hier ein Byte gespeichert). Es lässt uns/
eher als//
für die Ganzzahldivision verwenden.Python 2 , 156 Bytes
Probieren Sie es online!
quelle
JavaScript (ES6), 139 Byte
Ähnlich wie die Node.js-Version, jedoch mit Zeichen außerhalb des ASCII-Bereichs.
Probieren Sie es online!
JavaScript (Node.js) ,
149 bis148 ByteBasierend auf Xavier Bonnetain des C - Implementierung (die präsentierte hier ).
Probieren Sie es online!
Codierung
In der ursprünglichen Antwort von Xavier werden die Tabellen
s[]
undk[]
in der folgenden Zeichenfolge gespeichert:Die ersten 17 Zeichen sind die ASCII-Darstellungen von,
k[i] XOR 64
und die nächsten 15 Zeichen sind die ASCII-Darstellungen vons[i-17] XOR 173
, oders[i-17] XOR 64 XOR 17 XOR 252
.k[i] XOR 64
s[i-17] XOR 173
Folgendes bekommen wir:
110010011001001
NB: Dies ist nur eine Randnotiz, die nichts mit den obigen Antworten zu tun hat.
Probieren Sie es online!
quelle
C # (Visual C # Interactive Compiler) , 141 Byte
Nur ein Port der Beispiellösung, portiert auf C #.
Probieren Sie es online!
quelle
Python 3 , 182 Bytes
Probieren Sie es online!
Python wird hier nicht den ersten Preis gewinnen, aber dies ist immer noch ein 10-Byte-Golf des besten Python-Programms hier .
Python 3 , 176 Bytes
Probieren Sie es online!
Als Lambda ist es noch sechs Bytes kürzer. Es tut mir weh, es zu benutzen
if... else
, aber ich sehe keine andere Option zum Kurzschließen, wenn man bedenkt, wie 0 eine mögliche Antwort ist.Python 3 , 173 Bytes
Probieren Sie es online!
Noch kürzer in Bytes (obwohl ich mir bei Bits nicht sicher bin, weil dies kein reines ASCII mehr ist), mit freundlicher Genehmigung von ovs.
quelle
\x..
EscapezeichenRust ,
170163 BytesProbieren Sie es online!
Dies ist ein Port meiner Lösung in C mit einer etwas anderen Zeichenfolge, die nicht xor 17 erfordert. Ich gehe davon aus, dass die meisten Lösungen auf der Zeichenfolge "@` rFTDVbpPBvdtfR @ \ xacp? \ Xe2> 4 \ xa6 \ xe9 basieren {z \ xe3q5 \ xa7 \ xe8 "kann ebenfalls verbessert werden (ändern Sie einfach die Zeichenfolge, entfernen Sie xor 17 und xor 173 anstelle von 188).
Ich habe einen der Lookups entfernt, indem ich ihn bedingt hinzugefügt
17*17
habel
, wie wir es (mehr oder weniger) in der ARM-Maschinencodelösung getan haben.Rust hat Typinferenz und Closures, aber seine Casts (auch für Boolesche Werte oder zwischen ganzen Zahlen) sind immer explizit, die Mutabilität muss markiert werden, es gibt keinen ternären Operator, Ganzzahloperationen, standardmäßig Panik bei Überlauf und Mutationsoperationen (
l+=1
) gibt die Einheit zurück. Ich habe es nicht geschafft, mit Iteratoren eine kürzere Lösung zu finden, da Closures + Mapping immer noch ziemlich ausführlich sind.Dies scheint Rust zu einer ziemlich schlechten Wahl für das Golfen zu machen. Trotzdem sind wir selbst in einer Sprache, in der Lesbarkeit und Sicherheit im Vordergrund stehen, viel zu kurz.
Update: verwendet eine anonyme Funktion, aus manatworks Vorschlag.
quelle
let p=
in den Header wechseln und diesen nicht zählen können. Unsicher über die;
, da für anonymen Anruf nicht benötigt wird: Probieren Sie es online! .05AB1E , 74 Bytes
Port von @NickKennedys erster Jelly-Antwort . Ich habe an einem Port von @ jimmy23013s CJam-Antwort direkt gearbeitet , aber ich hatte bereits 78 Bytes und musste immer noch einen Fehler beheben, sodass dieser größer gewesen wäre. Das kann man aber definitiv noch ein bisschen spielen.
Probieren Sie es online aus oder überprüfen Sie alle Testfälle .
Erläuterung:
Sehen Sie sich meinen Tipp 05AB1E an (Abschnitte Wie komprimiere ich große Ganzzahlen? Und Wie komprimiere ich Ganzzahlenlisten? ) , Um zu verstehen, warum dies so
Ƶf
ist142
;•5›@¾ÂÌLìÁŒ©.ðǝš«YWǝŠ•
ist29709448685778434533295690952203992295278432248
,ƵŠ
ist239
; und•5›@¾ÂÌLìÁŒ©.ðǝš«YWǝŠ•ƵŠв
ist[19,48,36,38,18,238,87,24,138,206,92,197,196,86,25,139,129,93,128,207]
.quelle