El objetivo de este desafío es encontrar una implementación imposiblemente corta de la siguiente función p
, en el idioma que elija. Aquí está el código C que lo implementa (vea
este enlace TIO que también imprime sus resultados) y una página de Wikipedia que 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];
}
Que es p
p
es un componente de dos estándares criptográficos rusos, a saber, la función hash Streebog y el cifrado de bloque Kuznyechik . En este artículo (y durante las reuniones de ISO), los diseñadores de estos algoritmos afirmaron que generaron la matriz pi
seleccionando permutaciones aleatorias de 8 bits.
Implementaciones "imposibles"
Hay permutaciones en 8 bits. Por lo tanto, para una permutación aleatoria dada, no se espera que un programa que la implemente necesite menos de 1683 bits.
Sin embargo, hemos encontrado múltiples implementaciones anormalmente pequeñas (que enumeramos aquí ), por ejemplo, el siguiente programa en 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 contiene solo 158 caracteres y, por lo tanto, cabe en 1264 bits. Haga clic aquí para ver que funciona.
Hablamos de una implementación corta "imposiblemente" porque, si la permutación fuera el resultado de un proceso aleatorio (como afirman sus diseñadores), entonces no existiría un programa tan corto (consulte esta página para obtener más detalles).
Implementación de referencia
Una versión más legible del código C anterior es:
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 tabla k
es tal que k[x] = L(16-x)
, donde L
es lineal en el sentido de que L(x^y)==L(x)^L(y)
, y donde, como en C, ^
denota el XOR. Sin embargo, no logramos aprovechar esta propiedad para acortar nuestra implementación. No conocemos ninguna estructura s
que pueda permitir una implementación más simple, aunque su salida siempre está en el subcampo, es decir donde la exponenciación se realiza en el campo finito. ¡Por supuesto, usted es absolutamente libre de usar una expresión más simple de si encuentra una!s
El ciclo while corresponde a la evaluación de un logaritmo discreto en el campo finito con 256 elementos. Funciona a través de una simple búsqueda de fuerza bruta: la variable ficticia a
está configurada para ser un generador del campo finito, y se multiplica por este generador hasta que el resultado sea igual a x
. Cuando es el caso, tenemos que l
es el registro discreto de x
. Esta función no está definida en 0, de ahí el caso especial correspondiente a la if
declaración.
La multiplicación por el generador puede verse como una multiplicación por en que luego se reduce el módulo del polinomio . El papel de la es garantizar que la variable permanezca en 8 bits. Alternativamente, podríamos usar , en cuyo caso podría ser un (o cualquier otro tipo de entero). Por otro lado, es necesario comenzar con lo que necesitamos tener cuando es igual a 1.unsigned char
a
a=(a<<1)^(a>>7)*(256^29)
a
int
l=1,a=2
l=255
x
En nuestro documentop
se presentan más detalles sobre las propiedades de , con un resumen de la mayoría de nuestras optimizaciones para obtener la implementación breve anterior.
Reglas
Proponga un programa que implemente la función p
en menos de 1683 bits. Mientras más corto sea el programa, más anormal es, para un idioma dado, más corto es mejor. Si su idioma tiene Kuznyechik, Streebog o p
como incorporado, no puede usarlos.
La métrica que usamos para determinar la mejor implementación es la longitud del programa en bytes. Usamos la longitud de bits en nuestro trabajo académico, pero nos quedamos con los bytes aquí por simplicidad.
Si su lenguaje no tiene una noción clara de función, argumento o salida, la codificación depende de usted para definir, pero trucos como codificar el valor pi[x]
como x
obviamente están prohibidos.
Ya hemos presentado un trabajo de investigación con nuestros hallazgos sobre este tema. Está disponible aquí . Sin embargo, si se publica en un lugar científico, con gusto reconoceremos a los autores de las mejores implementaciones.
Por cierto, ¡gracias a xnor por su ayuda al redactar esta pregunta!
fuente
1683 bits at most
restricción estricta [sic?] O la meta?Respuestas:
Conjunto AMD64 (78 bytes o 624 bits de código de máquina)
Conjunto x86 de 64 bits
Código de 64 bits desmontado
Ensamblado x86 de 32 bits
Código de 32 bits desmontado
fuente
uint8_t
argumentos son extendidos a cero a 64 bits para JRCXZ). Además, si escribe código dependiente de la posición, puede colocar la dirección de la tabla en un registro con 5 bytes enmov ebx, imm32
lugar de 6 bytescall
/pop
. O utilizarlo comodisp32
enmov al, [table + rax]
, pero que podría perder ya que tiene dosxlatb
y unamov
ya. Sin embargo, el truco call + pop shellcode gana vs. LEA relativo a RIP de 7 bytes con los datos después delret
.CJam ,
72676663 byteses*
repite algo con la marca de tiempo actual, que es un gran número, y tardaría demasiado en terminar.Versión realmente comprobable, 64 bytes:
Pruébalo en línea!
¡Prueba todo en línea!
Para ejecutar este código (en una máquina Linux), es necesario agregar la línea
en_US.ISO-8859-1 ISO-8859-1
en/etc/locale.gen
y ejecutarlocale-gen
. Entonces podrías usar:O puede probar esta versión equivalente de 72 bytes UTF-8:
Explicación
Los caracteres en la cadena son:
fuente
"Ý0$&Ü™ÖD�’\n˜×EO“N"
?Jalea
7159 bytesPruébalo en línea!
Verificar todas las posibilidades
Ahora reescrito usando una versión reelaborada de la inteligente respuesta CJam de jimmy23013, ¡ así que asegúrese de votar esa también! Utiliza solo 472 bits (28.0% del enfoque ingenuo). ¡@ jimmy23013 también ha guardado otro byte!
Explicación
Nivel 1
Etapa 2
Etapa 3
Enfoque original
Gelatina ,
7166 bytesPruébalo en línea!
Verificar todas las posibilidades
Un enlace monádico o programa completo que toma un solo argumento y devuelve el valor relevante de
pi[x]
. Esto es 536 bits, por lo tanto, debajo de un tercio del almacenamiento ingenuo de pi.Guardado 3 bytes utilizando el método para encontrar
l
de respuesta Cjam de jimmy23013 así que asegúrese de upvote que uno también!Explicación
Nivel 1
Etapa 2
Etapa 3
fuente
C (gcc) ,
157148140139bytesMejora modesta sobre el ejemplo C.
Pruébalo en línea!
C (gcc) ,
150 142 127126 bytesEste depende de las peculiaridades de gcc y x86 y UTF-8.
Pruébalo en línea!
Gracias a @XavierBonnetain por -1 y menos comportamiento indefinido.
fuente
05AB1E ,
10110098979594 bytes-3 bytes gracias a @Grimy .
Pruébelo en línea o verifique todos los casos de prueba .
Explicación:
Puerto de la función C de Xavier Bonnetain (versión de 1106 bits) desde aquí , con la misma mejora que @ceilingcat hizo en su respuesta C para ahorrar 3 bytes, ¡así que asegúrese de votarlo!
Vea esta sugerencia mía 05AB1E (secciones ¿Cómo comprimir enteros grandes? Y ¿Cómo comprimir listas enteras? ) Para comprender por qué
•α">η≠ε∍$<Θγ\&@(Σα•
es20576992798525946719126649319401629993024
;•α">η≠ε∍$<Θγ\&@(Σα•₅в
es[64,96,114,70,84,68,86,98,112,80,66,118,100,116,102,82,64]
;Ƶ¹
es285
;•¾#kôlb¸ù,-ó"a·ú•
es930891775969900394811589640717060184
;•¾#kôlb¸ù,-ó"a·ú•₅в
es[189,97,46,243,47,37,183,248,106,107,242,96,36,182,249]
; yƵ∞
es188
.fuente
s^
=>^
(XOR es conmutativo). En realidad, ¿no es los^_
mismo queQ
?i==0 || X==0 || X==1
.Stax ,
6564625958 bytesEjecutar y depurarlo
Desafortunadamente, este programa usa algunas instrucciones que usan internamente algunas instrucciones obsoletas obsoletas. Simplemente olvidé actualizar su implementación. Esto hace que aparezca una advertencia espuria, pero los resultados aún son correctos.
Esto está inspirado en la excelente respuesta de jimmy23013 . Algunas partes se han cambiado para adaptarse mejor a Stax.
Los programas Stax escritos en ASCII imprimible tienen una representación alternativa que ahorra un poco más de 1 bit por byte, ya que solo hay 95 caracteres ASCII imprimibles.
Aquí está la representación ascii de este programa formateado para "legibilidad" con comentarios.
Ejecute este
Versión modificada para ejecutar todas las entradas 0..255
fuente
S
para poder establecer. Puede obtener el conjunto de potencia de [18 38 36 48], indexar y reducir en xor. (No conozco a Stax y no estoy seguro de que sea más corto).S
operador no está en el orden correcto para que eso funcione. p"abc"SJ
. ej. (conjunto de potencia de "abc" unido con espacios) produce "a ab abc ac b bc c".Python 3 , 151 bytes
Pruébalo en línea!
Una función que implementa la permutación. El código usa solo caracteres ASCII de 7 bits.
Codifica
k
como una cadena de bytes de Python 3, desplazada^64
al rango imprimible. Por el contrario,s
se codifica como los dígitos de base 256 de una constante numérica, y los dígitos se extraen como[number]>>[shift]*8&255
. Esto fue más corto que la codificacións
en una cadena debido a la cantidad de caracteres de escape que esto requería, incluso con un desplazamiento óptimo^160
para minimizarlos.El cálculo de registro discreto se realiza al revés. La actualización
x=x*2^x//128*285
avanza en el grupo cíclico simulando la multiplicación por la generación, hasta que alcanza la identidadx=1
. Comenzamos el registro discreto enl=255
(la duración del ciclo) y lo disminuimos en cada iteración. Para manejar elx=0
caso y hacer que no se repita para siempre, también terminamos cuandol=0
, lo que hace quex=0
map sel=0
especifique.Python 2 pierde al no tener buenas cadenas de bytes, por lo que debemos hacerlo
map(ord,...)
(ArBo guardó un byte aquí). Nos permite usar en/
lugar de//
para la división de enteros.Python 2 , 156 bytes
Pruébalo en línea!
fuente
JavaScript (ES6), 139 bytes
Similar a la versión Node.js, pero usando caracteres más allá del rango ASCII.
Pruébalo en línea!
JavaScript (Node.js) ,
149148 bytesBasado en la implementación C de Xavier Bonnetain (que se presenta aquí ).
Pruébalo en línea!
Codificación
En la respuesta original de Xavier, las tablas
s[]
yk[]
se almacenan en la siguiente cadena:Los primeros 17 caracteres son las representaciones ASCII de
k[i] XOR 64
y los siguientes 15 caracteres son las representaciones ASCII des[i-17] XOR 173
, os[i-17] XOR 64 XOR 17 XOR 252
.k[i] XOR 64
s[i-17] XOR 173
Esto es lo que obtenemos:
110010011001001
NB: Esta es solo una nota al margen, no relacionada con las respuestas anteriores.
Pruébalo en línea!
fuente
C # (compilador interactivo de Visual C #) , 141 bytes
Solo un puerto de la solución de ejemplo, portado a C #.
Pruébalo en línea!
fuente
Python 3 , 182 bytes
Pruébalo en línea!
Python no va a ganar el primer premio aquí, pero este sigue siendo un golf de 10 bytes del mejor programa de Python aquí .
Python 3 , 176 bytes
Pruébalo en línea!
Como lambda, todavía es seis bytes más corto. Me duele tener que usar
if... else
, pero no veo otra opción para el cortocircuito, dado que 0 es una respuesta posible.Python 3 , 173 bytes
Pruébalo en línea!
Incluso más corto en bytes (aunque no estoy seguro acerca de los bits, porque esto ya no es ASCII puro), cortesía de ovs.
fuente
\x..
escapesRust ,
170163 bytesPruébalo en línea!
Este es un puerto de mi solución en C, con una cadena ligeramente diferente, que no requiere xor 17. Espero que la mayoría de las soluciones basen la cadena "@` rFTDVbpPBvdtfR @ \ xacp? \ Xe2> 4 \ xa6 \ xe9 {z \ xe3q5 \ xa7 \ xe8 "también se puede mejorar (simplemente cambie la cadena, elimine xor 17 y xor 173 en lugar de 188).
Quité una de las operaciones de búsqueda mediante la adición de condicionalmente
17*17
al
, ya que (más o menos) hicimos en la solución de código de máquina ARM.Rust tiene inferencia de tipo y cierres, pero sus conversiones (incluso para booleanos o entre enteros) siempre son explícitas, la mutabilidad debe estar marcada, no tiene un operador ternario, operaciones de enteros, por defecto, pánico al desbordarse y operaciones de mutación (
l+=1
) devuelve la unidad. No logré obtener una solución más corta con iteradores, ya que los cierres + mapeo siguen siendo bastante detallados.Esto parece hacer que Rust sea una muy mala elección para jugar al golf. Sin embargo, incluso en un lenguaje que enfatiza la legibilidad y la seguridad sobre la concisión, somos demasiado cortos.
Actualización: se utilizó una función anónima, a partir de la sugerencia de manatwork.
fuente
let p=
al encabezado y no contarlo. No estoy seguro acerca de;
, ya que no se necesita una llamada anónima: ¡ Pruébelo en línea! .05AB1E , 74 bytes
Puerto de la primera respuesta de Jelly de @NickKennedy . Estaba trabajando en un puerto de la respuesta CJam de @ jimmy23013 directamente , pero ya tenía 78 bytes y todavía tenía que corregir un error, por lo que habría sido más grande. Sin embargo, esto definitivamente todavía se puede jugar bastante.
Pruébelo en línea o verifique todos los casos de prueba .
Explicación:
Vea esta sugerencia mía 05AB1E (secciones ¿Cómo comprimir enteros grandes? Y ¿Cómo comprimir listas enteras? ) Para comprender por qué
Ƶf
es142
;•5›@¾ÂÌLìÁŒ©.ðǝš«YWǝŠ•
es29709448685778434533295690952203992295278432248
,ƵŠ
es239
; y•5›@¾ÂÌLìÁŒ©.ðǝš«YWǝŠ•ƵŠв
es[19,48,36,38,18,238,87,24,138,206,92,197,196,86,25,139,129,93,128,207]
.fuente