Provando que um padrão criptográfico russo é muito estruturado


92

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 256!21684 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 sque 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[x]16=s[x]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 llog discreto x. Esta função não está definida em 0, portanto, o caso especial correspondente à ifinstruçã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.XF2[X]X8+X4+X3+X2+1unsigned charaa=(a<<1)^(a>>7)*(256^29)aintl=1,a=2l=255x

Mais detalhes sobre as propriedades de psã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 pem 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 pcomo 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 xsã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!


12
Espero que alguém envie uma resposta no Seed.
Robin Ryder

6
Da mesma forma, pode, por exemplo, um código de merda cerebral ser classificado em 3 bits por caractere, se não tiver nops? E é 1683 bits at mostuma restrição estrita [sic?] Ou a meta?
alguém

31
" Se a permutação fosse a saída de um processo aleatório (como reivindicado por seus projetistas), então um programa tão curto não existiria " Eu discordo (embora isso não importe para o desafio). Se fosse a saída de um processo aleatório, seria improvável que esse programa existisse; ou seria difícil de encontrar
Luis Mendo

8
@ Grimy A afirmação de que um programa tão curto não existiria é conceitual (não de programação). Usando seus termos, ele pertence ao mundo da matemática pura, não ao mundo da programação
Luis Mendo

7
Isso já deve ter sido notado, mas apenas no caso: resulta em apenas 8 valores distintos: (começando com e assumindo ) . 1 , 10 , 68 , 79 , 146 , 153 , 220 , 221 i = 1 s 0 = 0sEu XOR sEu-11,10,68,79,146,153,220,221Eu=1s0 0=0 0
Arnauld

Respostas:


35

Conjunto AMD64 (78 bytes ou 624 bits de código de máquina)

uint8_t SubByte (uint8_t x) {
    uint8_t y, z;
    uint8_t s [] =
      {1,221,146,79,147,153,11,68,214,215,78,220,152,10,69};

    uint8_t k [] =
      {0,32,50,6,20,4,22,34,48,16,2,54,36,52,38,18,0};

    se (x) {
      Para (y = z = 1; (y = (y << 1) ^ ((y >> 7) * 29))! = x; z ++);
      x = (z% 17);
      z = (z / 17);
      x = (x)? k [x] ^ s [z]: k [z];
    }
    retornar x ^ 252;
}

Montagem x86 de 64 bits

    ; 78 bytes do conjunto AMD64
    ; odzhan
    bits 64

    % ifndef BIN
      subBytex global
    %fim se

SubBytex:
    mov al, 252
    jecxz L2; se (x) {
    ligar para L0
k:
    db 0xfc, 0xdc, 0xce, 0xfa, 0xe8, 0xf8, 0xea, 0xde, 
    db 0xcc, 0xec, 0xfe, 0xca, 0xd8, 0xc8, 0xda, 0xee, 0xfc
s:
    db 0x01, 0xdd, 0x92, 0x4f, 0x93, 0x99, 0x0b, 0x44, 
    db 0xd6, 0xd7, 0x4e, 0xdc, 0x98, 0x0a, 0x45
L0:
    pop rbx
    mov 1; y = 1
    cdq; z = 0
L1:
    inc dl; z ++
    adicionar al, al; y = y + y
    jnc $ + 4; pule o XOR se não houver transporte
    xor al, 29;
    cmp al, cl; if (y! = x) vá para L1
    jne L1    

    xchg eax, edx; eax = z
    cdq; edx = 0
    mov cl, 17; al = z / 17, dl = z% 17
    div ecx

    mov cl, [rbx + rax + 17]; cl = s [z]
    xlatb; al = k [z]
    teste dl, dl; if (x == 0) vá para L2
    jz L2
    xchg eax, edx; al = x
    xlatb; al = k [x]
    xoral, cl; al ^ = s [z]
L2:
    ret

Código de 64 bits desmontado

00000000 B0FC mov al, 0xfc
00000002 67E348 jecxz 0x4d
00000005 E820000000 chamada qword 0x2a
; k [] = 0xfc, 0xdc, 0xce, 0xfa, 0xe8, 0xf8, 0xea, 0xde,
; 0xcc, 0xec, 0xfe, 0xca, 0xd8, 0xc8, 0xda, 0xee, 0xfc
; s [] = 0x01, 0xdd, 0x92, 0x4f, 0x93, 0x99, 0x0b, 0x44,
; 0xd6, 0xd7, 0x4e, 0xdc, 0x98, 0x0a, 0x45
0000002A 5B pop rbx
0000002B B001 mov, 0x1
0000002D 99 cdq
0000002E FEC2 inc dl
00000030 00C0 add al, al
00000032 7302 jnc 0x36
00000034 341D xor al, 0x1d
00000036 38C8 cmp al, cl
00000038 75F4 jnz 0x2e
0000003A 92 xchg eax, edx
0000003B 99 cdq
0000003C B111 mov cl, 0x11
0000003E F7F1 div ecx
00000040 8A4C0311 mov cl, [rbx + rax + 0x11]
00000044 D7 xlatb
00000045 84D2 teste dl, dl
00000047 7404 jz 0x4d
00000049 92 xchg eax, edx
0000004A D7 xlatb
0000004B 30C8 xor al, cl
0000004D C3 ret

Montagem x86 de 32 bits

    ; 72 bytes de montagem x86
    ; odzhan
    bits 32

    % ifndef BIN
      subBytex global
      global _SubBytex
    %fim se

SubBytex:
_SubBytex:
    mov al, 252
    jecxz L2; se (x) {
    ligar para L0
k:
    db 0xfc, 0xdc, 0xce, 0xfa, 0xe8, 0xf8, 0xea, 0xde, 
    db 0xcc, 0xec, 0xfe, 0xca, 0xd8, 0xc8, 0xda, 0xee, 0xfc
s:
    db 0x01, 0xdd, 0x92, 0x4f, 0x93, 0x99, 0x0b, 0x44, 
    db 0xd6, 0xd7, 0x4e, 0xdc, 0x98, 0x0a, 0x45
L0:
    pop ebx
    mov 1; y = 1
    cdq; z = 0
L1:
    inc edx; z ++
    adicionar al, al; y = y + y
    jnc $ + 4; pule o XOR se não houver transporte
    xor al, 29;
    cmp al, cl; if (y! = x) vá para L1
    jne L1    
    xchg eax, edx; al = z
    aam 17; al | x = z% 17, ah | z = z / 17
    mov cl, ah; cl = z
    cmove eax, ecx; se (x == 0) al = z else al = x
    xlatb; al = k [z] ou k [x]
    jz L2; if (x == 0) vá para L2
    xor al, [ebx + ecx + 17]; k [x] ^ = k [z]
L2:
    ret

Código de 32 bits desmontado

00000000 B0FC mov al, 0xfc
00000002 E345 jecxz 0x49
00000004 E820000000 chamada dword 0x29
; k [] = 0xfc, 0xdc, 0xce, 0xfa, 0xe8, 0xf8, 0xea, 0xde,
; 0xcc, 0xec, 0xfe, 0xca, 0xd8, 0xc8, 0xda, 0xee, 0xfc
; s [] = 0x01, 0xdd, 0x92, 0x4f, 0x93, 0x99, 0x0b, 0x44,
; 0xd6, 0xd7, 0x4e, 0xdc, 0x98, 0x0a, 0x45
00000029 5B pop ebx
0000002A B001 mov, 0x1
0000002C 99 cdq
0000002D 42 inc edx
0000002E 00C0 add al, al
00000030 7302 jnc 0x34
00000032 341D xor al, 0x1d
00000034 38C8 cmp al, cl
00000036 75F5 jnz 0x2d
00000038 92 xchg eax, edx
00000039 D411 am 0x11
0000003B 88E1 mov cl, ah
0000003D 0F44C1 cmovz eax, ecx
00000040 D7 xlatb
00000041 7404 jz 0x47
00000043 32440B11 xor al, [ebx + ecx + 0x11]
00000047 C3 ret

1
Boa resposta! Como o OP estava procurando a contagem de bits, isso (85 bytes) sai para 680 bits , usando 8 bits por byte, ou 595 bits usando 7 bits por byte (possível, pois todos os caracteres são ASCII). Você provavelmente poderia ficar mais curto se compactasse com um conjunto de caracteres ainda mais restritivo.
Cullub 07/06

1
Bem-vindo ao PPCG; boa primeira solução.
Shaggy

9
@Cullub Meu argumento foi que o código nesta resposta é apenas C / Assembler que é compilado, portanto a contagem de bytes não é a do código fornecido, mas o código compilado. E isso não é ASCII.
ArBo 07/06

7
Apenas para esclarecimento, os 84 bytes são o tamanho do código da máquina depois que ele foi montado? Nesse caso, o título deve ser atualizado para refletir que se trata de uma resposta de código de máquina em vez de uma resposta de montagem.
Potato44

1
E, BTW, você não precisa usar uma convenção de chamada padrão; você pode usar uma convenção personalizada em que o RBX é bloqueado por chamada, economizando 2 bytes para push / pop. (E onde uint8_targs são estendidos em zero para 64 bits para o JRCXZ). Além disso, se você escrever um código dependente da posição, poderá inserir o endereço da tabela em um registrador com 5 bytes em mov ebx, imm32vez de 6 bytes call/ pop. Ou usá-lo como um disp32em mov al, [table + rax], mas que pode perder, pois você tem dois xlatbe um movjá. O truque da chamada + pop-shellcode vence contra o LEA relativo a RIP de 7 bytes com os dados após o ret, no entanto.
Peter Cordes

23

CJam ,72 67 66. 63 bytes

ri{_2md142*^}es*]2#~Hmd{5\}e|Fm2b"Ý0$&Ü™ÖD�’
˜×EO“N".*Lts:^i

es* repete algo no registro de data e hora atual, que é um número grande, e levaria muito tempo para terminar.

Versão realmente testável, 64 bytes:

ri{_2md142*^}256*]2#~Hmd{5\}e|Fm2b"Ý0$&Ü™ÖD�’
˜×EO“N".*Lts:^i
00000000: 7269 7b5f 326d 6431 3432 2a5e 7d32 3536  ri{_2md142*^}256
00000010: 2a5d 3223 7e48 6d64 7b35 5c7d 657c 466d  *]2#~Hmd{5\}e|Fm
00000020: 3262 22dd 3024 2612 dc99 d644 0092 0b0a  2b".0$&....D....
00000030: 98d7 454f 934e 0122 2e2a 4c74 733a 5e69  ..EO.N.".*Lts:^i

Experimente online!

Experimente tudo online!

Para executar este código (em uma máquina Linux), você precisa adicionar a linha en_US.ISO-8859-1 ISO-8859-1em /etc/locale.gene executar locale-gen. Então você pode usar:

LANG=en_US.ISO-8859-1 java -jar cjam.jar <source file>

Ou você pode tentar esta versão UTF-8 equivalente a 72 bytes:

00000000: 7269 7b5f 326d 6431 3432 2a5e 7d32 3536  ri{_2md142*^}256
00000010: 2a5d 3223 7e48 6d64 7b35 5c7d 657c 466d  *]2#~Hmd{5\}e|Fm
00000020: 3262 22c3 9d30 2426 12c3 9cc2 99c3 9644  2b"..0$&.......D
00000030: 00c2 920b 0ac2 98c3 9745 4fc2 934e 0122  .........EO..N."
00000040: 2e2a 4c74 733a 5e69                      .*Lts:^i

Explicação

ri               e# Read input.
{_2md142*^}      e# Copy and divide by 2. If remainder is 1, xor 142.
es*]             e# Repeat a lot of times and wrap all results in an array.
2#               e# Find the first occurrence of 2, -1 if not found.
~                e# Increment and negate.
Hmd              e# Divmod 17. Both the quotient and remainder would be negated.
{5\}e|           e# If remainder = 0, return 5, quotient instead.
Fm2b             e# Subtract 15 from the remainder and expand in base 2.
                 e# In CJam, base conversion only applies to the absolute value.
"...".*          e# Filter 221, 48, 36, 38, 18 by the bits.
                 e# 221, the characters after 18
                 e#   and 18 itself in case quotient - 15 = -15 won't change.
Lt               e# Remove the item s[quotient] xor 220.
                 e# Quotient is 0, 5 or a negative integer from the end,
                 e#   which doesn't overlap with the previously filtered items.
s                e# Flatten, because some characters become strings after the process.
:^i              e# Reduce by xor and make sure the result is an integer.

Os caracteres na sequência são:

  • 221. Veja abaixo.
  • 48, 36, 38, 18. É a decomposição linear de k com base nas características de L na questão.
  • 220, o valor de preenchimento da matriz s quando apenas a matriz k é usada.
  • A matriz s xor 220 inverteu, exceto o último elemento, ou o primeiro elemento antes da reversão, que é o 221 no início da cadeia.

O que você faria com o conjunto de potência? PS: Provavelmente vou roubar esse truque de fazer a operação de campo finito ao contrário. Muito arrumado.
Peter Taylor

@PeterTaylor Você pode obter o array k usando o conjunto de potência de [48 36 38 18] (ou seu reverso) com a ordem mais direta e reduzir cada um deles por xor. Mas nas línguas de golfe usadas nesta pergunta até agora, apenas 05AB1E tinha a ordem correta. Jelly e Stax aparentemente tinham uma idéia diferente sobre o que eu achava que deveria ser direto.
jimmy23013

Atualmente, estou no processo de jogar sua resposta para 05AB1E. Quais são os valores inteiros para sua string "Ý0$&Ü™ÖD�’\n˜×EO“N"?
Kevin Cruijssen 13/06

@KevinCruijssen Você está perguntando sobre o que eles queriam dizer ou seus valores numéricos (que você pode ver no hex dump)?
jimmy23013 13/06

@ jimmy23013 Ah, é claro .. Esqueci o hex-dump ..
Kevin Cruijssen

19

Geléia 71 59 bytes

H^142ƊHḂ?Ƭi2d17U⁸⁴;$Ḣ?$CµṪạ⁴B¬T;;6ị“Œ0$&ØŀWð⁺Ṫ\ḢĠVı⁻¹]°Ẇ‘^/

Experimente online!

Verifique todas as possibilidades

Agora reescrito usando uma versão reformulada da resposta inteligente do CJam de jimmy23013, portanto, não deixe de votar também nessa! Usa apenas 472 bits (28,0% da abordagem ingênua). @ jimmy23013 também salvou outro byte!

Explicação

Estágio 1

         Ƭ        | Repeat the following until no new entries:
       Ḃ?         | - If odd:
     Ɗ            |   - Then following as a monad:
H                 |     - Halve
 ^142             |     - Xor 142
      H           |   - Else: Halve
          i2      | Index of 2 in this list

Etapa 2

d17           | Divmod 17
          $   | Following as a monad:
   U          | - Reverse order
        Ḣ?    | - If head (remainder from divmod) non-zero:
    ⁸         |   - Then: left argument [quotient, remainder]
     ⁴;$      |   - Else: [16, quotient]
           C  | Complement (1 minus)
            µ | Start a new monadic chain

Etapa 3

Ṫ                   | Tail (Complement of remainder or quotient from stage 2)
 ạ⁴                 | Absolute difference from 16
   B                | Convert to binary
    ¬               | Not
     T              | Indices of true values
      ;             | Concatenate (to the other value from stage 2)
       ;6           | Concatenate to 6
         ị“Œ...Ẇ‘   | Index into [19,48,36,38,18,238,87,24,138,206,92,197,196,86,25,139,129,93,128,207]
                 ^/ | Reduce using xor

Abordagem original

Geléia , 71 66 bytes

H^142ƊHḂ?Ƭi2d:j@“ÐḌ‘ɗ%?17ị"“p?Ä>4ɼḋ{zẉq5ʂċ¡Ḅ“`rFTDVbpPBvdtfR@‘^ƒ17

Experimente online!

Verifique todas as possibilidades

Um link monádico ou programa completo que utiliza um único argumento e retorna o valor relevante de pi[x]. São 536 bits, portanto menos de um terço do armazenamento ingênuo de pi.

Salvo 3 bytes utilizando o método para encontrar la partir resposta CJam de jimmy23013 isso não deixe de upvote que um também!

Explicação

Estágio 1

         Ƭ        | Repeat the following until no new entries:
       Ḃ?         | - If odd:
     Ɗ            |   - Then following as a monad:
H                 |     - Halve
 ^142             |     - Xor 142
      H           |   - Else: Halve
          i2      | Index of 2 in this list

Etapa 2

         %?17  | If not divisible by 17
d              | - Then divmod 17
        ɗ      | - Else following as a dyad (using 17 as right argument)
 :             |   - Integer divide by 17
  j@“ÐḌ‘       |   - Join the list 15,173 by the quotient

Etapa 3

ị"“p...Ḅ“`...@‘     | Index into two compressed lists of integers using the output from stage 2 (separate list for each output from stage 2). The third output from stage 2 will be left untouched
               ^ƒ17 | Finally reduce using Xor and 17 as the first argument


15

C (gcc) , 157 148 140 139 bytes

Modesta melhoria em relação ao exemplo C.

l,b=17,*k=L"@`rFTDVbpPBvdtfR@";p(x){for(l=256;--l*~-x;)x=2*x^x/128*285;return l%b?k[l%b]^"\xcer?\4@6\xc8\t{|\3q5\xc7\n"[l/b]-b:k[l/b]^188;}

Experimente online!

C (gcc) , 150 142 127 126 bytes

Este depende das peculiaridades gcc e x86 e UTF-8.

l,*k=L"@`rFTDVbpPBvdtfR@";p(x){for(l=256;--l*~-x;)x=2*x^x/128*285;x=l%17?k[l%17]^L"½a.ó/%·øjkò`$¶ù"[l/17]:k[l/17]^188;}

Experimente online!

Obrigado a @XavierBonnetain pelo comportamento -1 e menos indefinido.


10

05AB1E , 101 100 98 97 95 94 bytes

U•α">η≠ε∍$<Θγ\&@(Σα•₅вV₁[<ÐX*Q#X·₁%Xžy÷Ƶ¹*₁%^₁%U}D17©%DĀiYsès®÷•¾#kôlb¸ù,-ó"a·ú•₅вë\Ƶ∞s®÷Y}sè^

-3 bytes graças a @Grimy .

Experimente online ou verifique todos os casos de teste .

Explicação:

Porta da função C de Xavier Bonnetain (versão de 1106 bits) a partir daqui , com o mesmo aprimoramento @ceilingcat feito em sua resposta C para economizar 3 bytes, por isso não deixe de votar nele!

U                    # Store the (implicit) input in variable `X`
•α">η≠ε∍$<Θγ\&@(Σα• "# Push compressed integer 20576992798525946719126649319401629993024
 ₅в                  # Converted to base-255 as list:
                     #  [64,96,114,70,84,68,86,98,112,80,66,118,100,116,102,82,64]
   V                 # Pop and store this list in variable `Y`
                    # Push `i` = 256
 [                   # Start an infinite loop:
  <                  #  Decrease `i` by 1
   Ð                 #  Triplicate `i`
    X*Q              #  If `i` multiplied by `X` is equal to `i` (i==0 or X==1):
       #             #   Stop the infinite loop
  X·₁%               #  Calculate double(`X`) modulo-256
                     #  (NOTE: all the modulo-256 are to mimic an unsigned char in C)
  Xžy÷               #  Calculate `X` integer-divided by builtin integer 128,
      Ƶ¹*            #  multiplied by compressed integer 285,
         ₁%          #  modulo-256
  ^                  #  Bitwise-XOR those together
   ₁%                #  And take modulo-256 again
  U                  #  Then pop and store it as new `X`
 }D                  # After the loop: Duplicate the resulting `i`
   17©               # Push 17 (and store it in variable `®` without popping)
      %              # Calculate `i` modulo-17
       D             # Duplicate it
        Āi           # If it's NOT 0:
          Ysè        #  Index the duplicated `i` modulo-17 into list `Y`
          s®÷        #  Calculate `i` integer-divided by 17
          •¾#kôlb¸ù,-ó"a·ú•
                    "#  Push compressed integer 930891775969900394811589640717060184
           ₅в        #  Converted to base-255 as list:
                     #   [189,97,46,243,47,37,183,248,106,107,242,96,36,182,249]
         ë           # Else (`i` == 0):
          \          #  Discard the duplicated `i` modulo-17, since we don't need it
          Ƶ∞         #  Push compressed integer 188
          s®÷        #  Calculate `i` integer-divided by 17
          Y          #  Push list `Y`
         }s          # After the if-else: swap the integer and list on the stack
           è         # And index the `i` modulo/integer-divided by 17 into the list
            ^        # Then Bitwise-XOR the top two together
                     # (after which the top of the stack is output implicitly as result)

Consulte esta dica 05AB1E (seções Como compactar números inteiros grandes? E Como compactar listas de números inteiros? ) Para entender por que •α">η≠ε∍$<Θγ\&@(Σα•é 20576992798525946719126649319401629993024; •α">η≠ε∍$<Θγ\&@(Σα•₅вé [64,96,114,70,84,68,86,98,112,80,66,118,100,116,102,82,64]; Ƶ¹é 285; •¾#kôlb¸ù,-ó"a·ú•é 930891775969900394811589640717060184; •¾#kôlb¸ù,-ó"a·ú•₅вé [189,97,46,243,47,37,183,248,106,107,242,96,36,182,249]; e Ƶ∞é 188.


@ Grimy Obrigado, eu sempre esqueci esse tipo de golfe com as listas inteiras compactadas .. (PS: Você pode colocar um código contendo `nos comentários com dois deles nos dois lados. Ou seja, com 'em vez de': '' code'code ''.)
Kevin Cruijssen

Opa, desculpe pela formatação desarrumada. Eu sei sobre dobrar backticks, mas não sabia que a lista compactada tinha um backtick nela. Também: s^=> ^(XOR é comutativo). Na verdade, não é s^_o mesmo que Q?
Grimy 07/06

@ Grimy Thanks! Você está realmente certo. Nós, basicamente, verificar se uma das seguintes três coisas é truthy para sair do loop: i==0 || X==0 || X==1.
Kevin Cruijssen

10

Stax , 65 64 62 59 58 bytes

ç∙¼≥2▼Uó╤áπ╙º┐╩♫∟öv◘≥δ♦Θ╫»─kRWÑâBG")≥ö0╥kƒg┬^S ΔrΩ►╣Wü Ü╕║

Execute e depure

Infelizmente, este programa usa algumas instruções que usam internamente algumas instruções stax obsoletas. Esqueci de atualizar a implementação deles. Isso faz com que algum aviso falso apareça, mas os resultados ainda estão corretos.

Isso é inspirado na excelente resposta de jimmy23013 . Algumas peças foram alteradas para se adequar melhor ao stax.

Os programas Stax gravados em ASCII imprimível têm uma representação alternativa que economiza um pouco mais de 1 bit por byte, pois existem apenas 95 caracteres ASCII imprimíveis.

Aqui está a representação ascii deste programa formatada para "legibilidade" com comentários.

{                       begin block
  2|%142*S              given n, calculate (n/2)^(n%2*142)
                         - this seems to be the inverse of the operation in the while loop
gu                      use block to generate distinct values until duplicate is found
                         - starting from the input; result will be an array of generated values
2I^                     1-based index of 2 in the generated values
17|%                    divmod 17
c{Us}?                  if the remainder is zero, then use (-1, quotient) instead
~                       push the top of the main stack to the input stack for later use
"i1{%oBTq\z^7pSt+cS4"   ascii string literal; will be transformed into a variant of `s`
./o{H|EF                interpret ascii codes as base-94 integer
                         - this is tolerant of digits that exceed the base
                        then encode big constant as into base 222 digits
                         - this produces an array similar to s
                         - 0 has been appended, and all elements xor 220
@                       use the quotient to index into this array
"jr+R"!                 packed integer array literal [18, 38, 36, 48]
F                       for each, execute the rest of the program
  ;                     peek from the input array, stored earlier
  v                     decrement
  i:@                   get the i-th bit where i is the iteration index 0..3
  *                     multiply the bit by value from the array literal
  S                     xor with result so far
                        after the loop, the top of the stack is printed implicitly

Execute este

Versão modificada para executar para todas as entradas 0..255


Stax tem Spara poder definido. Você pode obter o conjunto de potências de [18 38 36 48], indexar e reduzir em xor. (Eu não conheço Stax e não tenho certeza se seria mais curto.)
jimmy23013

Acho que a encomenda da stax para subconjuntos produzidos pelo Soperador não está na ordem certa para que isso funcione. por exemplo "abc"SJ(conjunto de potências de "abc" associado a espaços) produz "a ab abc ac b bc c".
recursivo em

8

Python 3 , 151 bytes

f=lambda x,l=255,k=b'@`rFTDVbpPBvdtfR@':f(x*2^x//128*285,l-1)if~-x*l else[k[l%17]^(0x450a98dc4ed7d6440b99934f92dd01>>l//17*8&255),k[l//17]][l%17<1]^188

Experimente online!

Uma função que implementa a permutação. O código usa apenas caracteres ASCII de 7 bits.

Codifica kcomo um bytestring do Python 3, alterado ^64para o intervalo imprimível. Por outro lado, sé codificado como os dígitos base-256 de uma constante numérica e os dígitos são extraídos como [number]>>[shift]*8&255. Isso foi mais curto do que a codificação sem uma sequência devido ao número de caracteres de escape necessário, mesmo com uma mudança ideal ^160para minimizá-los.

O cálculo do log discreto é feito ao contrário. A atualização faz um x=x*2^x//128*285loop no grupo cíclico, simulando a multiplicação pela geração, até atingir a identidade x=1. Iniciamos o log discreto em l=255(a duração do ciclo) e o diminuímos a cada iteração. Para lidar com o x=0caso e fazer com que ele não fique em loop para sempre, também encerramos quando l=0, que faz o x=0mapa l=0como especificado.


O Python 2 perde por não ter boas map(ord,...)seqüências de bytes , por isso precisamos fazer (o ArBo salvou um byte aqui). Permite usar em /vez de //para a divisão inteira.

Python 2 , 156 bytes

f=lambda x,l=255,k=map(ord,'@`rFTDVbpPBvdtfR@'):f(x*2^x/128*285,l-1)if~-x*l else[k[l%17]^(0x450a98dc4ed7d6440b99934f92dd01>>l/17*8&255),k[l/17]][l%17<1]^188

Experimente online!


7

JavaScript (ES6), 139 bytes

Semelhante à versão do Node.js, mas usando caracteres além do intervalo ASCII.

f=(x,l=256,b=17,k=i=>"@`rFTDVbpPBvdtfR@¬p?â>4¦é{zãq5§è".charCodeAt(i))=>--l*x^l?f(x+x^(x>>7)*285,l):l%b?k(l%b)^k(b+l/b)^b:k(l/b)^188

Experimente online!


JavaScript (Node.js) ,  149  148 bytes

Com base na implementação C de Xavier Bonnetain (que é apresentada aqui ).

f=(x,l=256,b=17,k=i=>Buffer("@`rFTDVbpPBvdtfR@,p?b>4&i{zcq5'h")[~~i]|3302528>>i-b&128)=>--l*x^l?f(x+x^(x>>7)*285,l):l%b?k(l%b)^k(b+l/b)^b:k(l/b)^188

Experimente online!

Codificação

Na resposta original de Xavier, as tabelas s[]e k[]são armazenadas na seguinte sequência:

"@`rFTDVbpPBvdtfR@\xacp?\xe2>4\xa6\xe9{z\xe3q5\xa7\xe8"
 \_______________/\__________________________________/
         k                         s

Os primeiros 17 caracteres são as representações ASCII de k[i] XOR 64e os próximos 15 caracteres são as representações ASCII de s[i-17] XOR 173, ou s[i-17] XOR 64 XOR 17 XOR 252.

k[i] XOR 64s[i-17] XOR 173126128

Aqui está o que temos:

original value : 172 112  63 226  62  52 166 233 123 122 227 113  53 167 232
subtract 128?  :   1   0   0   1   0   0   1   1   0   0   1   0   0   1   1
result         :  44 112  63  98  62  52  38 105 123 122  99 113  53  39 104
as ASCII       : "," "p" "?" "b" ">" "4" "&" "i" "{" "z" "c" "q" "5" "'" "h"

11001001100100125801

128

| 3302528 >> i - b & 128

s

Nota: Esta é apenas uma nota lateral, não relacionada às respostas acima.

s

{1,11,79,146}

console.log(
  [ 0b0001, 0b1100, 0b1000, 0b0100, 0b1001, 0b1010, 0b0010, 0b0110,
    0b1110, 0b1111, 0b0101, 0b1101, 0b1011, 0b0011, 0b0111
  ].map(x =>
    [ 1, 11, 79, 146 ].reduce((p, c, i) =>
      p ^= x >> i & 1 && c,
      0
    )
  )
)

Experimente online!



3

Python 3 , 182 bytes

def p(x,t=b'@`rFTDVbpPBvdtfR@\xacp?\xe2>4\xa6\xe9{z\xe3q5\xa7\xe8',l=255,d=17):
 if x<2:return 252-14*x
 while~-x:x=x*2^(x>>7)*285;l-=1
 return(188^t[l//d],d^t[l%d]^t[d+l//d])[l%d>0]

Experimente online!

O Python não ganhará o primeiro prêmio aqui, mas este ainda é um golfe de 10 bytes do melhor programa Python daqui .

Python 3 , 176 bytes

p=lambda x,l=255,t=b'@`rFTDVbpPBvdtfR@\xacp?\xe2>4\xa6\xe9{z\xe3q5\xa7\xe8',d=17:2>x<l>254and-14*x+252or(p(x*2^(x>>7)*285,l-1)if~-x else(188^t[l//d],d^t[l%d]^t[d+l//d])[l%d>0])

Experimente online!

Como lambda, é seis bytes mais curto ainda. Dói-me ter de usar if... else, mas não vejo outra opção para curtos-circuitos, dado que 0 é uma resposta possível.

Python 3 , 173 bytes

p=lambda x,l=255,t=bytes('@`rFTDVbpPBvdtfR@¬p?â>4¦é{zãq5§è','l1'),d=17:2>x<l>254and-14*x+252or(p(x*2^(x>>7)*285,l-1)if~-x else(188^t[l//d],d^t[l%d]^t[d+l//d])[l%d>0])

Experimente online!

Ainda mais curto em bytes (embora eu não tenha certeza sobre bits, porque isso não é mais puro ASCII), cortesia de ovs.


3 bytes mais curtos usando os caracteres literais em vez de \x..escapes
ovs


@ovs Obrigado! Provavelmente aumenta um pouco a contagem de bits (não tenho certeza do que é mais importante para o OP), então vou manter minha resposta antiga também.
ArBo 07/06

2

Ferrugem , 170 163 bytes

|mut x|{let(k,mut l)=(b"QqcWEUGsaASguewCQ\xacp?\xe2>4\xa6\xe9{z\xe3q5\xa7\xe8",255);while l*x!=l{x=2*x^x/128*285;l-=1}(if l%17>0{l+=289;k[l%17]}else{173})^k[l/17]}

Experimente online!

Esta é uma porta da minha solução em C, com uma sequência ligeiramente diferente, que não requer o xor 17. Espero que a maioria das soluções baseie a sequência "@` rFTDVbpPBvdtfR @ \ xacp? \ Xe2> 4 \ xa6 \ xe9 {z \ xe3q5 \ xa7 \ xe8 "também pode ser aprimorado (apenas altere a string, remova o xor 17 e xor 173 em vez de 188).

Eu removi uma das pesquisas adicionando condicionalmente 17*17a l, como fizemos (mais ou menos) na solução de código de máquina do ARM.

A ferrugem possui inferência e fechamento de tipo, mas seus lançamentos (mesmo para booleanos ou entre números inteiros) são sempre explícitos, a mutabilidade deve ser marcada, ela não possui um operador ternário, operações inteiras, por padrão, pânico no estouro e operações de mutação ( l+=1) retorna a unidade. Não consegui obter uma solução mais curta com os iteradores, pois o fechamento + o mapeamento ainda são bastante detalhados.

Isso parece fazer do Rust uma péssima escolha para o golfe. No entanto, mesmo em uma linguagem que enfatiza a legibilidade e a segurança em detrimento da concisão, somos muito curtos.

Atualização: usou uma função anônima, por sugestão da manatwork.


1
Exceto quando chamado recursivamente, funções anônimas / lambdas são aceitáveis, portanto você pode passar let p=para o Cabeçalho e não contá-lo. Não tem certeza sobre a ;chamada anônima: não é necessário: Experimente on-line! .
manatwork 17/06

1

05AB1E , 74 bytes

₄FÐ;sÉiƵf^])2k>17‰Dθ_i¦16š}<(Rć16α2в≠ƶ0Kì6ª•5›@¾ÂÌLìÁŒ©.ðǝš«YWǝŠ•ƵŠвs<è.«^

Porto da primeira resposta de Jelly de @NickKennedy . Eu estava trabalhando diretamente em uma porta da resposta CJam de @ jimmy23013 , mas já tinha 78 bytes e ainda precisava corrigir um erro, para que fosse maior. Definitivamente, isso ainda pode ser bastante praticado.

Experimente online ou verifique todos os casos de teste .

Explicação:

F              # Loop 1000 times:
  Ð             #  Triplicate the current value
                #  (which is the implicit input in the first iteration)
   ;            #  Halve it
    s           #  Swap to take the integer again
     Éi         #  If it's odd:
       Ƶf^      #   Bitwise-XOR it with compressed integer 142
]               # Close the if-statement and loop
 )              # Wrap all values on the stack into a list
  2k            # Get the 0-based index of 2 (or -1 if not found)
    >           # Increase it by 1 to make it 1-based (or 0 if not found)
     17        # Take the divmod-17 of this
Dθ_i    }       # If the remainder of the divmod is 0:
    ¦16š        #  Replace the quotient with 16
         <      # Decrease both values by 1
          (     # And then negate it
R               # Reverse the pair
 ć              # Extract head; push head and remainder-list
  16α           # Get the absolute difference between the head and 16
     2в         # Convert it to binary (as digit-list)
               # Invert booleans (0 becomes 1; 1 becomes 0)
        ƶ       # Multiply all by their 1-based indices
         0K     # And remove all 0s
           ì    # And prepend this in front of the remainder-list
            6ª  # And also append a trailing 6
5›@¾ÂÌLìÁŒ©.ðǝš«YWǝŠ•
                # Push compressed integer 29709448685778434533295690952203992295278432248
  ƵŠв           # Converted to base-239 as list:
                #  [19,48,36,38,18,238,87,24,138,206,92,197,196,86,25,139,129,93,128,207]
     s          # Swap to take the earlier created list again
      <         # Subtract each by 1 to make them 0-based
       è        # And index them into this list
.«^             # And finally reduce all values by bitwise XOR-ing them
                # (after which the result is output implicitly)

Consulte esta dica 05AB1E (seções Como compactar números inteiros grandes? E Como compactar listas de números inteiros? ) Para entender por que Ƶfé 142; •5›@¾ÂÌLìÁŒ©.ðǝš«YWǝŠ•é 29709448685778434533295690952203992295278432248, ƵŠé 239; e •5›@¾ÂÌLìÁŒ©.ðǝš«YWǝŠ•ƵŠвé [19,48,36,38,18,238,87,24,138,206,92,197,196,86,25,139,129,93,128,207].

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.