Цель этой задачи - найти невероятно короткую реализацию следующей функции p
в выбранном вами языке. Вот код C, реализующий его (см.
Эту ссылку TIO, которая также печатает его выходные данные) и страницу википедии, содержащую его.
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];
}
Что такое p
p
является составной частью двух российских криптографических стандартов, а именно хеш-функции Streebog и блочного шифра Kuznyechik . В этой статье (и во время совещаний ISO) разработчики этих алгоритмов утверждали, что они генерировали массив pi
, выбирая случайные 8-битные перестановки.
«Невозможные» реализации
Есть перестановок на 8 бит. Следовательно, для данной случайной перестановки не следует ожидать, что программе, которая ее реализует, потребуется менее 1683 битов.
Однако мы нашли несколько аномально небольших реализаций (которые мы перечислим здесь ), например, следующую программу на 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;}
который содержит только 158 символов и, таким образом, помещается в 1264 бит. Нажмите здесь, чтобы увидеть, что это работает.
Мы говорим о «невозможной» короткой реализации, потому что, если перестановка была результатом случайного процесса (как утверждают его разработчики), то такой короткой программы не существовало бы (см. Эту страницу для более подробной информации).
Ссылочная реализация
Более читаемая версия предыдущего кода 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;
}
Таблица k
такова, что k[x] = L(16-x)
, где L
линейно в том смысле, что L(x^y)==L(x)^L(y)
и где, как в C, ^
обозначает XOR. Однако нам не удалось использовать это свойство для сокращения нашей реализации. Мы не знаем ни о какой структуре, s
которая могла бы позволить более простую реализацию - хотя ее вывод всегда находится в подполе, то есть где возведение в степень выполняется в конечном поле. Конечно, вы можете свободно использовать более простое выражение, s
если найдете его!
Цикл while соответствует оценке дискретного логарифма в конечном поле с 256 элементами. Он работает с помощью простого перебора: фиктивная переменная a
задается как генератор конечного поля и умножается на этот генератор до тех пор, пока результат не будет равен x
. Когда это так, у нас l
есть это дискретный журнал x
. Эта функция не определена в 0, следовательно, это особый случай, соответствующий if
утверждению.
Умножение на генератор можно рассматривать как умножение на в которое затем уменьшается по модулю полинома . Роль unsigned char
заключается в том, чтобы гарантировать, что переменная a
остается на 8 битах. В качестве альтернативы мы могли бы использовать a=(a<<1)^(a>>7)*(256^29)
, в этом случае a
может быть int
(или любой другой целочисленный тип). С другой стороны, нужно начинать с того, l=1,a=2
что нужно, l=255
когда x
равен 1.
Более подробная информация о свойствах p
представлена в нашей статье с описанием большинства наших оптимизаций для получения предыдущей краткой реализации.
правила
Предложите программу, которая реализует функцию p
менее чем в 1683 битах. Чем короче программа, тем ненормальнее она для данного языка, тем короче, тем лучше. Если на вашем языке есть Kuznyechik, Streebog или p
как встроенный, вы не можете их использовать.
Метрика, которую мы используем для определения наилучшей реализации, - это длина программы в байтах. Мы используем длину в битах в нашей академической статье, но здесь для простоты мы придерживаемся байтов.
Если ваш язык не имеет четкого представления о функции, аргумента или вывода, кодирование до вас , чтобы определить, но приемы , такие как кодирующая значение , pi[x]
как x
, очевидно , запрещены.
Мы уже представили исследовательскую работу с нашими выводами по этой теме. Это доступно здесь . Однако, если она будет опубликована в научном центре, мы с радостью примем авторов лучших реализаций.
Кстати, спасибо xnor за помощь при составлении этого вопроса!
1683 bits at most
это строгое ограничение [sic?] Или цель?