Dimostrando che uno standard crittografico russo è troppo strutturato


92

Lo scopo di questa sfida è trovare un'implementazione incredibilmente breve della seguente funzione p, nella lingua che hai scelto. Ecco il codice C che lo implementa (vedi questo link TIO che stampa anche i suoi output) e una pagina di Wikipedia che 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];
}

Cosa è p

pè un componente di due standard crittografici russi, vale a dire la funzione hash Streebog e la cifra di blocchi Kuznyechik . In questo articolo (e durante le riunioni ISO), i progettisti di questi algoritmi hanno affermato di aver generato l'arraypi selezionando permutazioni casuali a 8 bit.

Implementazioni "impossibili"

Ce ne sono 256!21684 permutazioni su 8 bit. Pertanto, per una determinata permutazione casuale, non è previsto che un programma che lo implementa abbia bisogno di meno di 1683 bit.

Tuttavia, abbiamo trovato più implementazioni anormalmente piccole (che elenchiamo qui ), ad esempio il seguente programma 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;}

che contiene solo 158 caratteri e si adatta quindi a 1264 bit. Clicca qui per vedere che funziona.

Parliamo di un'implementazione breve "impossibile" perché, se la permutazione fosse l'output di un processo casuale (come affermato dai suoi progettisti), allora un programma così corto non esisterebbe (vedi questa pagina per maggiori dettagli).

Implementazione di riferimento

Una versione più leggibile del precedente codice 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;
}

La tabella kè tale che k[x] = L(16-x), dove Lè lineare nel senso che L(x^y)==L(x)^L(y), e dove, come in C, ^indica lo XOR. Tuttavia, non siamo riusciti a sfruttare questa proprietà per abbreviare la nostra implementazione. Non siamo a conoscenza di alcuna struttura in sgrado di consentire un'implementazione più semplice --- il suo output è sempre nel sottocampo, cioè S[X]16=S[X] dove l'espiazione è fatta nel campo finito. Certo, sei assolutamente libero di usare un'espressione più semplice dis dovessi trovarne una!

Il ciclo while corrisponde alla valutazione di un logaritmo discreto nel campo finito con 256 elementi. Funziona tramite una semplice ricerca della forza bruta: la variabile fittizia aè impostata per essere un generatore del campo finito, e viene moltiplicata per questo generatore fino a quando il risultato è uguale x. Quando è il caso, abbiamo quello lè il registro discreto di x. Questa funzione non è definita in 0, quindi il caso speciale corrispondente ifall'istruzione.

La moltiplicazione per il generatore può essere vista come una moltiplicazione per X in che viene quindi ridotta nel modulo polinomiale . Il ruolo di è assicurarsi che la variabile rimanga su 8 bit. In alternativa, potremmo usare , nel qual caso potrebbe essere un (o qualsiasi altro tipo intero). D'altra parte, è necessario iniziare come abbiamo bisogno quando è uguale a 1.F2[X]X8+X4+X3+X2+1unsigned charaa=(a<<1)^(a>>7)*(256^29)aintl=1,a=2l=255x

Maggiori dettagli sulle proprietà di psono presentati nel nostro documento , con un riepilogo della maggior parte delle nostre ottimizzazioni per ottenere la precedente implementazione breve.

Regole

Propone un programma che implementa la funzione pin meno di 1683 bit. Poiché più breve è il programma, più è anormale, per una determinata lingua, più breve è meglio. Se la tua lingua sembra avere Kuznyechik, Streebog op come incorporato, non puoi usarli.

La metrica che utilizziamo per determinare la migliore implementazione è la lunghezza del programma in byte. Usiamo la lunghezza in bit nel nostro documento accademico ma ci atteniamo ai byte qui per motivi di semplicità.

Se la tua lingua non ha una chiara nozione di funzione, argomento o output, la codifica dipende da te definire, ma trucchi come codificare il valore pi[x]comex sono ovviamente proibito.

Abbiamo già presentato un documento di ricerca con i nostri risultati su questo argomento. È disponibile qui . Tuttavia, qualora fosse pubblicato in una sede scientifica, saremo lieti di riconoscere gli autori delle migliori implementazioni.

A proposito, grazie a xnor per il suo aiuto durante la stesura di questa domanda!


12
Spero che qualcuno invii una risposta in Seed.
Robin Ryder,

6
Allo stesso modo, ad esempio, è possibile ottenere un codice di Brainfuck a 3 bit per carattere se non ha nops? Ed è 1683 bits at mostuna stretta restrizione [sic?] O l'obiettivo?
qualcuno il

31
" Se la permutazione fosse l'output di un processo casuale (come affermato dai suoi progettisti), allora un programma così corto non esisterebbe " Non sono d'accordo (anche se non importa per la sfida). Se fosse l'output di un processo casuale, sarebbe improbabile che esistesse tale programma; o sarebbe difficile da trovare
Luis Mendo il

8
@Grimy L'affermazione quindi che un programma così breve non esisterebbe è concettuale (non programmatica). Usando i termini, appartiene al mondo della matematica pura, non al mondo della programmazione
Luis Mendo,

7
Potrebbe essere già stato notato, ma per ogni evenienza: produce solo 8 valori distinti: 1 , 10 , 68 , 79 , 146 , 153 , 220 , 221 (a partire da i = 1 e assumendo s 0 = 0 ). Sio XOR Sio-11,10,68,79,146,153,220,221io=1S0=0
Arnauld

Risposte:


35

Assemblaggio AMD64 (78 byte o 624 bit di codice macchina)

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) {
      per (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];
    }
    ritorna x ^ 252;
}

Assembly x86 a 64 bit

    ; 78 byte di assembly AMD64
    ; odzhan
    bit 64

    % ifndef BIN
      SubBytex globale
    %finisci se

SubBytex:
    mov al, 252
    jecxz L2; se (x) {
    chiama 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 al, 1; y = 1
    cdq; z = 0
L1:
    inc dl; z ++
    aggiungi al, al; y = y + y
    jnc $ + 4; salta XOR se non lo porti
    xor al, 29;
    cmp al, cl; se (y! = x) vai a 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]
    test dl, dl; se (x == 0) vai a L2
    jz L2
    xchg eax, edx; al = x
    xlatb; al = k [x]
    xor al, cl; al ^ = s [z]
L2:
    macerare

Codice a 64 bit smontato

00000000 B0FC mov al, 0xfc
00000002 67E348 jecxz 0x4d
00000005 E820000000 chiama 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 al, 0x1
0000002D 99 cdq
0000002E FEC2 inc dl
00000030 00C0 aggiungi 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 test dl, dl
00000047 7404 jz 0x4d
00000049 92 xchg eax, edx
0000004A D7 xlatb
0000004B 30C8 xo al, cl
0000004D C3 ret

Assembly x86 a 32 bit

    ; 72 byte di assembly x86
    ; odzhan
    bit 32

    % ifndef BIN
      SubBytex globale
      globale _SubBytex
    %finisci se

SubBytex:
_SubBytex:
    mov al, 252
    jecxz L2; se (x) {
    chiama 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 al, 1; y = 1
    cdq; z = 0
L1:
    inc edx; z ++
    aggiungi al, al; y = y + y
    jnc $ + 4; salta XOR se non lo porti
    xor al, 29;
    cmp al, cl; se (y! = x) vai a 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; if (x == 0) al = z else al = x
    xlatb; al = k [z] o k [x]
    jz L2; se (x == 0) vai a L2
    xor al, [ebx + ecx + 17]; k [x] ^ = k [z]
L2:
    macerare

Codice a 32 bit smontato

00000000 B0FC mov al, 0xfc
00000002 E345 jecxz 0x49
00000004 E820000000 call 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 al, 0x1
0000002C 99 cdq
0000002D 42 inc edx
0000002E 00C0 aggiungi 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 aam 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
Bella risposta! Dato che l'OP stava cercando il conteggio dei bit, questo (85 byte) risulta a 680 bit , usando 8 bit per byte o 595 bit usando 7 bit per byte (possibile poiché tutti i caratteri sono ASCII). Probabilmente potresti andare più breve se compresso in un set di caratteri ancora più restrittivo.
Cullub

1
Benvenuti in PPCG; bella prima soluzione.
Shaggy

9
@Cullub Il mio punto era che il codice in questa risposta è solo C / Assembler che viene compilato, quindi il conteggio dei byte non è quello del codice dato, ma del codice compilato. E questo non è ASCII.
ArBo

7
Solo per chiarimento, gli 84 byte è la dimensione del codice macchina dopo che questo è stato assemblato? In tal caso, il titolo deve essere aggiornato per riflettere che si tratta di una risposta del codice macchina piuttosto che di una risposta dell'assieme.
Potato44

1
E a proposito, non è necessario utilizzare una convenzione di chiamata standard; puoi usare una convenzione personalizzata in cui RBX è chiamato clobber, salvando 2 byte per push / pop. (E dove gli uint8_targ sono estesi da zero a 64 bit per JRCXZ). Inoltre, se si scrive un codice dipendente dalla posizione, è possibile inserire l'indirizzo della tabella in un registro con un 5 byte mov ebx, imm32anziché un 6 byte call/ pop. Oppure usalo come disp32in mov al, [table + rax], ma ciò potrebbe perdere poiché ne hai già due xlatbe uno mov. Tuttavia, il trucco call + pop shellcode vince contro LEA relativo a RIP a 7 byte con i dati dopo il ret, però.
Peter Cordes,

23

CJam ,72 67 66 63 byte

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

es* ripete qualcosa in base al timestamp corrente, che è un numero elevato, e impiegherebbe troppo tempo per terminare.

Versione attualmente testabile, 64 byte:

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

Provalo online!

Prova tutto online!

Per eseguire questo codice (su una macchina Linux), è necessario aggiungere la riga en_US.ISO-8859-1 ISO-8859-1in /etc/locale.gene correre locale-gen. Quindi puoi usare:

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

Oppure potresti provare questa versione UTF-8 equivalente a 72 byte:

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

Spiegazione

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.

I caratteri nella stringa sono:

  • 221. Vedi sotto.
  • 48, 36, 38, 18. È la decomposizione lineare di k in base alle caratteristiche di L nella domanda.
  • 220, il valore di riempimento dell'array s quando viene utilizzato solo l'array k.
  • L'array xo 220 è invertito, ad eccezione dell'ultimo elemento o del primo elemento prima di invertito, che è il 221 all'inizio della stringa.

Cosa faresti con il set di alimentazione? PS Probabilmente ruberò quel trucco di fare l'operazione sul campo finito al contrario. Molto pulito.
Peter Taylor,

@PeterTaylor È possibile ottenere l'array k utilizzando la serie di potenze di [48 36 38 18] (o il suo contrario) con l'ordinamento più semplice, e ridurre ciascuno di xor. Ma nelle lingue del golf utilizzate finora in questa domanda, solo 05AB1E aveva il giusto ordine. Jelly e Stax apparentemente avevano un'idea diversa di ciò che pensavo dovesse essere semplice.
jimmy23013,

Attualmente sto giocando a golf la tua risposta a 05AB1E. Quali sono i valori interi per la tua stringa"Ý0$&Ü™ÖD�’\n˜×EO“N" ?
Kevin Cruijssen,

@KevinCruijssen Stai chiedendo cosa significano o i loro valori numerici (che puoi vedere dalla discarica esadecimale)?
jimmy23013,

@ jimmy23013 Ah, certo .. Ho dimenticato la discarica esadecimale ..
Kevin Cruijssen,

19

Gelatina 71 59 byte

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

Provalo online!

Verifica tutte le possibilità

Ora riscritto usando una versione rielaborata della risposta intelligente di CJam di jimmy23013, quindi assicurati di votare anche quella! Utilizza solo 472 bit (28,0% dell'approccio ingenuo). @ jimmy23013 ha anche salvato un altro byte!

Spiegazione

Fase 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

Fase 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

Fase 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

Approccio originale

Gelatina , 71 66 byte

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

Provalo online!

Verifica tutte le possibilità

Un collegamento monadico o un programma completo che accetta un singolo argomento e restituisce il valore rilevante di pi[x]. Questo è 536 bit, quindi sotto un terzo della memoria ingenua di pi.

Salvato 3 byte utilizzando il metodo per la ricerca l di risposta CJam di jimmy23013 in modo da essere sicuri di upvote anche quello!

Spiegazione

Fase 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

Fase 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

Fase 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 byte

Miglioramento modesto rispetto all'esempio 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;}

Provalo online!

C (gcc) , 150 142 127 126 byte

Questo dipende dalle stranezze di 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;}

Provalo online!

Grazie a @XavierBonnetain per -1 e comportamento meno indefinito.


10

05AB1E , 101 100 98 97 95 94 byte

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

-3 byte grazie a @Grimy .

Provalo online o verifica tutti i casi di test .

Spiegazione:

La funzione C di Port of Xavier Bonnetain (versione 1106 bit) da qui , con lo stesso miglioramento che @ceilingcat ha apportato nella sua risposta C per salvare 3 byte, quindi assicurati di votarlo!

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)

Vedere questo suggerimento 05AB1E mio (sezioni Come comprimere grandi numeri interi? E Come comprimere liste di interi? ) Per capire perché •α">η≠ε∍$<Θγ\&@(Σα•è 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]; ed Ƶ∞è 188.


@Grimy Grazie, ho sempre dimenticato quel tipo di golf con gli elenchi di numeri interi compressi. (PS: puoi circondare il codice contenente `nei commenti con due di essi su entrambi i lati. Cioè con 'invece di`:' 'code'code ''.)
Kevin Cruijssen

Spiacenti, mi dispiace per la formattazione incasinata. So di raddoppiare i backtick, ma non mi rendevo conto che l'elenco compresso avesse un backtick in esso. Inoltre: s^=> ^(XOR è commutativo). In realtà, non è s^_lo stesso di Q?
Grimy

@Grimy Grazie! Hai davvero ragione. Fondamentalmente controlliamo se una delle seguenti tre cose è vera per uscire dal ciclo:i==0 || X==0 || X==1 .
Kevin Cruijssen,

10

Stax , 65 64 62 59 58 byte

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

Esegui ed esegui il debug

Sfortunatamente, questo programma utilizza alcune istruzioni che utilizzano internamente alcune istruzioni obsolete su Stax. Ho appena dimenticato di aggiornare la loro implementazione. Ciò fa apparire alcuni avvisi spuri, ma i risultati sono ancora corretti.

Questo si ispira alla risposta eccellente di jimmy23013 . Alcune parti sono state cambiate per adattarsi meglio allo stax.

I programmi Stax scritti in ASCII stampabile hanno una rappresentazione alternativa che consente di risparmiare leggermente più di 1 bit per byte, poiché ci sono solo 95 caratteri ASCII stampabili.

Ecco la rappresentazione ASCII di questo programma formattato per "leggibilità" con commenti.

{                       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

Esegui questo

Versione modificata per l'esecuzione per tutti gli ingressi 0..255


Stax ha Sper il set di alimentazione. È possibile ottenere il set di potenza di [18 38 36 48], indicizzare e ridurre di xor. (Non conosco Stax e non sono sicuro che sarebbe più breve però.)
jimmy23013

Penso che l'ordinamento di Stax per i sottoinsiemi prodotti Sdall'operatore non siano nell'ordine giusto per farlo funzionare. es. "abc"SJ(powerset di "abc" unito con spazi) produce "a ab abc ac b bc c".
ricorsivo l'

8

Python 3 , 151 byte

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

Provalo online!

Una funzione che implementa la permutazione. Il codice utilizza solo caratteri ASCII a 7 bit.

Codifica kcome Python 3 bytestring, spostato ^64nell'intervallo stampabile. Al contrario, sviene codificato come base-256 cifre di una costante numerica e le cifre vengono estratte come [number]>>[shift]*8&255. Questo era più breve della codifica sin una stringa a causa del numero di caratteri di escape richiesti, anche con uno spostamento ottimale^160 per minimizzarli.

Il calcolo del registro discreto viene eseguito al contrario. L'aggiornamento si x=x*2^x//128*285sposta in avanti nel gruppo ciclico simulando la moltiplicazione per la generazione, fino a raggiungere l'identità x=1. Iniziamo il log discreto a l=255(la lunghezza del ciclo) e lo diminuiamo ad ogni iterazione. Per gestire il x=0caso e renderlo non continuo per sempre, terminiamo anche quando l=0, il che rende la x=0mappa l=0come specificato.


Python 2 perde di non avere dei bei bytestring, quindi dobbiamo farlo map(ord,...)(ArBo ha salvato un byte qui). Ci permette di usare /piuttosto che //per la divisione di numeri interi.

Python 2 , 156 byte

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

Provalo online!


7

JavaScript (ES6), 139 byte

Simile alla versione Node.js, ma utilizza caratteri oltre l'intervallo 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

Provalo online!


JavaScript (Node.js) ,  149  148 byte

Basato sull'implementazione C di Xavier Bonnetain (che è presentata qui ).

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

Provalo online!

Codifica

Nella risposta originale di Xavier, le tabelle s[]e k[]sono archiviate nella seguente stringa:

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

I primi 17 caratteri sono le rappresentazioni ASCII di k[i] XOR 64 e i successivi 15 caratteri sono le rappresentazioni ASCII di s[i-17] XOR 173, o s[i-17] XOR 64 XOR 17 XOR 252.

Tutti i codici ASCII per k[i] XOR 64corrispondono ai caratteri stampabili, ma 7 codici ASCII per s[i-17] XOR 173sono oltre 126 e devono essere salvati. Li forziamo nella gamma stampabile sottraendo128

Ecco cosa otteniamo:

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"

Invertendo la maschera di bit formata dalla seconda riga nella tabella sopra, si ottiene 110010011001001in binario, o 25801 in decimale.

Questa maschera di bit viene moltiplicata per 128 , in modo da poter eseguire direttamente un OR bit a bit con il bit estratto. Da qui la formula:

| 3302528 >> i - b & 128

S

NB: Questa è solo una nota a margine, non correlata alle risposte di cui sopra.

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
    )
  )
)

Provalo online!



3

Python 3 , 182 byte

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]

Provalo online!

Python non vincerà il primo premio qui, ma questo è ancora un golf a 10 byte del miglior programma Python qui .

Python 3 , 176 byte

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])

Provalo online!

Come lambda, è ancora più breve di sei byte. Mi fa male dover usare if... else, ma non vedo un'altra opzione per il corto circuito, dato che 0 è una possibile risposta.

Python 3 , 173 byte

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])

Provalo online!

Anche più breve in byte (anche se non sono sicuro dei bit, perché questo non è più puro ASCII), per gentile concessione di ovs.


3 byte più brevi in base alle caratteri letterali invece di \x..fughe
ovs


@ovs Grazie! Probabilmente aumenta un po 'il conteggio dei bit (non sono sicuro di quale sia la cosa più importante per l'OP), quindi terrò anche la mia vecchia risposta.
ArBo

2

Ruggine , 170 163 byte

|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]}

Provalo online!

Questa è una porta della mia soluzione in C, con una stringa leggermente diversa, che non richiede xor 17. Mi aspetto che la maggior parte delle soluzioni si basi sulla stringa "@` rFTDVbpPBvdtfR @ \ xacp? \ Xe2> 4 \ xa6 \ xe9 Anche {z \ xe3q5 \ xa7 \ xe8 "può essere migliorato (basta cambiare la stringa, rimuovere xor 17 e xor 173 invece di 188).

Ho rimosso una delle ricerche da condizionale aggiunta 17*17a l, come abbiamo (più o meno) fatto nella soluzione ARM codice macchina.

Rust ha inferenza e chiusure di tipo, ma i suoi cast (anche per valori booleani o tra numeri interi) sono sempre espliciti, la mutabilità deve essere contrassegnata, non ha un operatore ternario, operazioni su numeri interi, per impostazione predefinita, panico su overflow e operazioni di mutazione (l+=1 ) restituisce unità. Non sono riuscito a ottenere una soluzione più breve con gli iteratori, poiché chiusure + mapping è ancora abbastanza prolisso.

Questo sembra rendere Rust una pessima scelta per il golf. Tuttavia, anche in un linguaggio che sottolinea la leggibilità e la sicurezza sulla concisione, siamo troppo corti.

Aggiornamento: utilizzato una funzione anonima, dal suggerimento di manatwork.


1
Tranne quando chiamato in modo ricorsivo, le funzioni anonime / lambda sono accettabili, quindi puoi passare let p=a Header e non contarlo. Non sei sicuro del ;, come per le chiamate anonime non è necessario: provalo online! .
arte

1

05AB1E , 74 byte

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

La prima risposta di Jelly di Port of @NickKennedy . Stavo lavorando su una porta della risposta CJam di @jimmy23013 direttamente , ma ero già a 78 byte e dovevo ancora correggere un bug, quindi sarebbe stato più grande. Tuttavia, questo può sicuramente essere ancora giocato a golf.

Provalo online o verifica tutti i casi di test .

Spiegazione:

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)

Vedere questo suggerimento 05AB1E mio (sezioni Come comprimere grandi numeri interi? E Come comprimere liste di interi? ) Per capire perché Ƶfè 142;•5›@¾ÂÌLìÁŒ©.ðǝš«YWǝŠ•è 29709448685778434533295690952203992295278432248, ƵŠè 239; ed •5›@¾ÂÌLìÁŒ©.ðǝš«YWǝŠ•ƵŠвè [19,48,36,38,18,238,87,24,138,206,92,197,196,86,25,139,129,93,128,207].

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.