O objetivo desse desafio é encontrar uma implementação incrivelmente curta da função a seguir p
, no idioma de sua escolha. Aqui está o código C implementando-o (veja
este link TIO que também imprime suas saídas) e uma página da Wikipedia que o contém.
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];
}
O que é p
p
é um componente de dois padrões criptográficos russos, a função hash Streebog e a cifra de bloco Kuznyechik . Em este artigo (e durante reuniões ISO), os designers destes algoritmos reivindicado que eles gerada a matriz pi
, escolhendo as permutações de 8 bits aleatórios.
Implementações "impossíveis"
Existem permutações em 8 bits. Portanto, para uma dada permutação aleatória, não se espera que um programa que a implemente precise menos de 1683 bits.
No entanto, encontramos várias implementações anormalmente pequenas (que listamos aqui ), por exemplo, o seguinte programa 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;}
que contém apenas 158 caracteres e, portanto, cabe em 1264 bits. Clique aqui para ver se funciona.
Falamos sobre uma implementação "impossivelmente" curta, porque, se a permutação fosse a saída de um processo aleatório (conforme reivindicado por seus designers), um programa desse tamanho não existiria (consulte esta página para mais detalhes).
Implementação de referência
Uma versão mais legível do código C anterior é:
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;
}
A tabela k
é tal que k[x] = L(16-x)
, onde L
é linear no sentido em que L(x^y)==L(x)^L(y)
, e onde, como em C, ^
denota o XOR. No entanto, não conseguimos alavancar essa propriedade para reduzir nossa implementação. Não temos conhecimento de nenhuma estrutura s
que possa permitir uma implementação mais simples - porém sua saída está sempre no subcampo, ou seja, onde a exponenciação é feita no campo finito. Claro, você é absolutamente livre para usar uma expressão mais simples, caso encontre uma!s
O loop while corresponde à avaliação de um logaritmo discreto no campo finito com 256 elementos. Ele funciona através de uma pesquisa simples de força bruta: a variável dummy a
é configurada para ser um gerador do campo finito e é multiplicada por esse gerador até que o resultado seja igual a x
. Quando for o caso, temos esse l
log discreto x
. Esta função não está definida em 0, portanto, o caso especial correspondente à if
instrução.
A multiplicação pelo gerador pode ser vista como uma multiplicação por em que é então reduzida no módulo polinomial . O papel do é garantir que a variável permaneça em 8 bits. Como alternativa, poderíamos usar ; nesse caso, poderia ser um (ou qualquer outro tipo inteiro). Por outro lado, é necessário começar como precisamos ter quando for igual a 1.unsigned char
a
a=(a<<1)^(a>>7)*(256^29)
a
int
l=1,a=2
l=255
x
Mais detalhes sobre as propriedades de p
são apresentados em nosso artigo , com um resumo da maioria de nossas otimizações para obter a implementação curta anterior.
Regras
Propor um programa que implemente a função p
em menos de 1683 bits. Quanto mais curto o programa, mais anormal é, para um determinado idioma, mais curto é melhor. Se o seu idioma possuir Kuznyechik, Streebog ou p
como um componente interno, você não poderá usá-lo.
A métrica que usamos para determinar a melhor implementação é o tamanho do programa em bytes. Usamos o tamanho do bit em nosso artigo acadêmico, mas mantemos os bytes aqui por uma questão de simplicidade.
Se o seu idioma não tem uma noção clara da função, argumento ou de saída, a codificação é até você para definir, mas truques como codificar o valor pi[x]
como x
são, obviamente, proibida.
Já submetemos um trabalho de pesquisa com nossas descobertas sobre esse tópico. Está disponível aqui . No entanto, se for publicado em um local científico, teremos o prazer de reconhecer os autores das melhores implementações.
A propósito, obrigado a xnor por sua ajuda ao elaborar esta pergunta!
1683 bits at most
uma restrição estrita [sic?] Ou a meta?