Lo scopo di questa sfida è trovare un'implementazione incredibilmente breve della seguente funzione p
, nella lingua che hai scelto. Ecco il codice C che lo implementa (vedi
questo link TIO che stampa anche i suoi output) e una pagina di Wikipedia che lo contiene.
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];
}
Cosa è p
p
è un componente di due standard crittografici russi, vale a dire la funzione hash Streebog e la cifra di blocchi Kuznyechik . In questo articolo (e durante le riunioni ISO), i progettisti di questi algoritmi hanno affermato di aver generato l'arraypi
selezionando permutazioni casuali a 8 bit.
Implementazioni "impossibili"
Ce ne sono permutazioni su 8 bit. Pertanto, per una determinata permutazione casuale, non è previsto che un programma che lo implementa abbia bisogno di meno di 1683 bit.
Tuttavia, abbiamo trovato più implementazioni anormalmente piccole (che elenchiamo qui ), ad esempio il seguente programma C:
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;}
che contiene solo 158 caratteri e si adatta quindi a 1264 bit. Clicca qui per vedere che funziona.
Parliamo di un'implementazione breve "impossibile" perché, se la permutazione fosse l'output di un processo casuale (come affermato dai suoi progettisti), allora un programma così corto non esisterebbe (vedi questa pagina per maggiori dettagli).
Implementazione di riferimento
Una versione più leggibile del precedente codice C è:
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;
}
La tabella k
è tale che k[x] = L(16-x)
, dove L
è lineare nel senso che L(x^y)==L(x)^L(y)
, e dove, come in C, ^
indica lo XOR. Tuttavia, non siamo riusciti a sfruttare questa proprietà per abbreviare la nostra implementazione. Non siamo a conoscenza di alcuna struttura in s
grado di consentire un'implementazione più semplice --- il suo output è sempre nel sottocampo, cioè dove l'espiazione è fatta nel campo finito. Certo, sei assolutamente libero di usare un'espressione più semplice dis
dovessi trovarne una!
Il ciclo while corrisponde alla valutazione di un logaritmo discreto nel campo finito con 256 elementi. Funziona tramite una semplice ricerca della forza bruta: la variabile fittizia a
è impostata per essere un generatore del campo finito, e viene moltiplicata per questo generatore fino a quando il risultato è uguale x
. Quando è il caso, abbiamo quello l
è il registro discreto di x
. Questa funzione non è definita in 0, quindi il caso speciale corrispondente if
all'istruzione.
La moltiplicazione per il generatore può essere vista come una moltiplicazione per in che viene quindi ridotta nel modulo polinomiale . Il ruolo di è assicurarsi che la variabile rimanga su 8 bit. In alternativa, potremmo usare , nel qual caso potrebbe essere un (o qualsiasi altro tipo intero). D'altra parte, è necessario iniziare come abbiamo bisogno quando è uguale a 1.unsigned char
a
a=(a<<1)^(a>>7)*(256^29)
a
int
l=1,a=2
l=255
x
Maggiori dettagli sulle proprietà di p
sono presentati nel nostro documento , con un riepilogo della maggior parte delle nostre ottimizzazioni per ottenere la precedente implementazione breve.
Regole
Propone un programma che implementa la funzione p
in meno di 1683 bit. Poiché più breve è il programma, più è anormale, per una determinata lingua, più breve è meglio. Se la tua lingua sembra avere Kuznyechik, Streebog op
come incorporato, non puoi usarli.
La metrica che utilizziamo per determinare la migliore implementazione è la lunghezza del programma in byte. Usiamo la lunghezza in bit nel nostro documento accademico ma ci atteniamo ai byte qui per motivi di semplicità.
Se la tua lingua non ha una chiara nozione di funzione, argomento o output, la codifica dipende da te definire, ma trucchi come codificare il valore pi[x]
comex
sono ovviamente proibito.
Abbiamo già presentato un documento di ricerca con i nostri risultati su questo argomento. È disponibile qui . Tuttavia, qualora fosse pubblicato in una sede scientifica, saremo lieti di riconoscere gli autori delle migliori implementazioni.
A proposito, grazie a xnor per il suo aiuto durante la stesura di questa domanda!
1683 bits at most
una stretta restrizione [sic?] O l'obiettivo?