Celem tego wyzwania jest znalezienie niemożliwie krótkiej realizacji następującej funkcji p
, w wybranym przez ciebie języku. Oto kod C implementujący go (zobacz
ten link TIO, który również drukuje jego wyniki) i zawierającą go stronę wikipedii .
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];
}
Co jest p
p
jest składnikiem dwóch rosyjskich standardów kryptograficznych, mianowicie funkcji skrótu Streebog i szyfru blokowego Kuźnyechik . W tym artykule (i podczas spotkań ISO) projektanci tych algorytmów twierdzili, że wygenerowali tablicę pi
, wybierając losowe permutacje 8-bitowe.
Implementacje „niemożliwe”
Jest permutacji na 8 bitach. Dlatego dla danej losowej permutacji program, który ją implementuje, nie powinien wymagać mniej niż 1683 bitów.
Znaleźliśmy jednak wiele nienormalnie małych implementacji (które wymieniliśmy tutaj ), na przykład następujący program 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;}
który zawiera tylko 158 znaków, a zatem mieści się w 1264 bitach. Kliknij tutaj, aby zobaczyć, czy to działa.
Mówimy o „niemożliwie” krótkiej implementacji, ponieważ jeśli permutacja byłaby wynikiem losowego procesu (jak twierdzą jej projektanci), to taki program nie istniałby ( więcej szczegółów na tej stronie ).
Realizacja referencyjna
Bardziej czytelna wersja poprzedniego kodu C to:
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;
}
Tabela k
jest taka, że k[x] = L(16-x)
gdzie L
jest liniowy w tym sensie L(x^y)==L(x)^L(y)
, i gdzie, podobnie jak w C, ^
oznacza XOR. Nie udało nam się jednak wykorzystać tej właściwości, aby skrócić nasze wdrożenie. Nie jesteśmy świadomi żadnej struktury, s
która mogłaby pozwolić na prostszą implementację --- jednak jej dane wyjściowe są zawsze w polu podrzędnym, tj. gdzie potęgowanie odbywa się w polu skończonym. Oczywiście masz absolutną swobodę w stosowaniu prostszego wyrażenia, jeśli go znajdziesz!s
Pętla while odpowiada ocenie dyskretnego logarytmu w polu skończonym z 256 elementami. Działa poprzez proste wyszukiwanie siłowe: zmienna fikcyjna a
jest ustawiona na generator pola skończonego i jest mnożona przez ten generator, aż wynik będzie równy x
. W takim przypadku mamy do czynienia l
z dyskretnym dziennikiem x
. Ta funkcja nie jest zdefiniowana w 0, stąd specjalny przypadek odpowiadający if
instrukcji.
Mnożenie przez generator może być postrzegane jako mnożenie przez w który jest następnie redukowany modulo do wielomianu . Rolą tego jest zapewnienie, że zmienna pozostaje na 8 bitach. Alternatywnie, możemy użyć , w którym to przypadku może być (lub dowolny inny typ całkowity). Z drugiej strony należy zacząć od tego, co musimy mieć, gdy jest równe 1.unsigned char
a
a=(a<<1)^(a>>7)*(256^29)
a
int
l=1,a=2
l=255
x
Więcej szczegółów na temat właściwości p
przedstawiono w naszym artykule , z podsumowaniem większości naszych optymalizacji w celu uzyskania poprzedniej krótkiej implementacji.
Zasady
Zaproponuj program, który implementuje funkcję p
w mniej niż 1683 bitach. Im krótszy program, tym bardziej nienormalny, dla danego języka, krótszy jest lepszy. Jeśli twój język ma Kuznyechik, Streebog lub p
jako wbudowany, nie możesz ich używać.
Miarą używaną do określenia najlepszej implementacji jest długość programu w bajtach. W naszej pracy naukowej używamy długości bitów, ale dla uproszczenia trzymamy się tu bajtów.
Jeśli język nie ma jasnego pojęcia funkcji, argumentu lub wyjścia, kodowanie jest do ciebie, aby określić, ale sztuczki jak kodowania wartości pi[x]
jak x
są oczywiście zabronione.
Przedłożyliśmy już dokument badawczy z naszymi ustaleniami na ten temat. Jest dostępny tutaj . Jeśli jednak zostanie opublikowany w miejscu naukowym, chętnie uznamy autorów najlepszych wdrożeń.
Nawiasem mówiąc, dzięki xnor za pomoc w przygotowaniu tego pytania!
1683 bits at most
to ścisłe ograniczenie [sic?] Czy cel?