Доведення того, що російський криптографічний стандарт занадто структурований


92

Мета цього виклику - знайти неможливе коротке виконання наступної функції 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 та блок-шифру Кузнечика . У цій статті (і під час засідань ISO) дизайнери цих алгоритмів стверджували, що вони генерували масив pi, вибираючи випадкові 8-бітні перестановки.

"Неможливі" реалізації

Є перестановки на 8 біт. Отже, для даної випадкової перестановки програмі, що реалізує її, не слід очікувати менше 1683 біт.256!21684

Однак ми знайшли кілька аномально невеликих реалізацій (які ми тут перераховуємо ), наприклад наступну програму 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 біт. Натисніть тут, щоб побачити, що це працює.

Ми говоримо про "неможливу" коротку реалізацію, оскільки, якби перестановка була результатом випадкового процесу (як стверджують його дизайнери), програми такої короткої програми не існувало ( детальну інформацію див. На цій сторінці ).

Довідкова реалізація

Більш читаною версією попереднього коду є:

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), і де, як і в С, ^позначається XOR. Однак нам не вдалося скористатися цією властивістю, щоб скоротити реалізацію. Ми не знаємо жодної структури, sяка могла б дозволити більш просту реалізацію --- однак її вихід завжди знаходиться в підполі, тобто де експоненція проводиться в кінцевому полі. Звичайно, ви абсолютно вільні у використанні більш простого виразу, якщо знайдете його!s[x]16=s[x]s

Цикл while відповідає оцінці дискретного логарифму в кінцевому полі з 256 елементами. Це працює за допомогою простого пошуку грубої сили: фіктивна змінна aвстановлюється як генератор кінцевого поля, і вона множиться на цей генератор, поки результат не дорівнює x. У цьому випадку ми маємо, що lце дискретний журнал x. Ця функція не визначена в 0, звідси й окремий випадок, що відповідає ifвисловлюванню.

Множення на генератор можна розглядати як множення на в яке потім зменшується за модулем на поліном . Роль модуля полягає в тому, щоб змінна залишалася на 8 бітах. В якості альтернативи ми можемо використовувати , і в цьому випадку це може бути (або будь-який інший цілий тип). З іншого боку, потрібно починати з того , що нам потрібно мати, коли дорівнює 1.XF2[X]X8+X4+X3+X2+1unsigned charaa=(a<<1)^(a>>7)*(256^29)aintl=1,a=2l=255x

Більш детально про властивості pпредставлено в нашій роботі , з описом більшості наших оптимізацій для отримання попередньої короткої реалізації.

Правила

Запропонуйте програму, яка реалізує цю функцію pменше 1683 біт. Чим коротша програма, тим вона більш ненормальна, для даної мови коротше - краще. Якщо у вашій мові є Кузнечик, Стрибог або pяк вбудований, ви не можете їх використовувати.

Коефіцієнт, який ми використовуємо для визначення найкращої реалізації, - це довжина програми в байтах. Ми використовуємо біт-довжину в своїй академічній роботі, але ми дотримуємося байтів тут для простоти.

Якщо ваша мова не має чіткого уявлення про функції, аргумент або виведення, кодування до вас , щоб визначити, але прийоми , такі як кодує значення , pi[x]як x, очевидно , заборонені.

Ми вже подали дослідницький документ з нашими висновками з цієї теми. Він доступний тут . Однак, якщо вона буде опублікована на науковому майданчику, ми з радістю визнаємо авторів найкращими реалізаціями.

До речі, дякую xnor за допомогу при складанні цього питання!


12
Я сподіваюся, що хтось подає відповідь на насінні.
Робін Райдер

6
Аналогічно, чи можна, наприклад, код мозкового відбору набрати в 3 біти на символ, якщо він не має точок? І чи 1683 bits at mostсуворе обмеження [sic?] Чи мета?
хтось

31
" Якби перестановка була результатом випадкового процесу (як стверджують його дизайнери), такої програми не було б " Я не погоджуюся (хоча це не має значення для виклику). Якби це був результат випадкового процесу, навряд чи існувала б така програма; або важко було б знайти
Луїс Мендо

8
@Grimy Заява, що програма, яка цього короткого не існувала, є концептуальною (не програмовою). Використовуючи ваші умови, він належить до чисто математичного світу, а не до світу програмування
Луїс Мендо

7
Це, можливо, вже було помічено, але про всяк випадок: призводить до лише 8 різних значень: (починаючи з і припускаючи ) . 1 , 10 , 68 , 79 , 146 , 153 , 220 , 221 i = 1 s 0 = 0si XOR si11,10,68,79,146,153,220,221i=1s0=0
Арнольд

Відповіді:


35

AMD64 збірка (78 байт або 624 біт машинного коду)

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

    якщо (x) {
      for (y = z = 1; (y = (y << 1) ^ ((y >> 7) * 29)) = x; z ++);
      x = (z% 17);
      z = (z / 17);
      х = (х)? k [x] ^ s [z]: k [z];
    }
    повернення х ^ 252;
}

64-розрядна збірка x86

    ; 78 байт складання AMD64
    ; оджан
    біт 64

    % ifndef BIN
      глобальний SubBytex
    % закінчення

SubBytex:
    mov al, 252
    jecxz L2; якщо (x) {
    виклик 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; у = 1
    cdq; z = 0
L1:
    вкл дл; z ++
    додати al, al; y = y + y
    jnc $ + 4; пропустити XOR, якщо немає
    xor al, 29;
    cmp al, cl; якщо (у! = х) перейти до 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]
    тест dl, dl; якщо (x == 0) перейти до L2
    jz L2
    xchg eax, edx; al = x
    xlatb; al = k [x]
    xor al, cl; al ^ = s [z]
L2:
    рет

Розібраний 64-бітний код

00000000 B0FC mov al, 0xfc
00000002 67E348 jecxz 0x4d
00000005 E820000000 виклик 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 кдк
0000002E FEC2 в т.ч.
00000030 00C0 додати al, al
00000032 7302 jnc 0x36
00000034 341D xor al, 0x1d
00000036 38C8 cmp al, кл
00000038 75F4 jnz 0x2e
0000003A 92 xchg eax, edx
0000003B 99 кдк
0000003C B111 mov cl, 0x11
0000003E F7F1 div ecx
00000040 8A4C0311 mov cl, [rbx + rax + 0x11]
00000044 D7 xlatb
00000045 84D2 тест дл, дл
00000047 7404 jz 0x4d
00000049 92 xchg eax, edx
0000004A D7 xlatb
0000004B 30C8 xor al, кл
0000004D C3 рет

32-розрядна збірка x86

    ; 72 байти складання x86
    ; оджан
    біт 32

    % ifndef BIN
      глобальний SubBytex
      глобальний _SubBytex
    % закінчення

SubBytex:
_SubBytex:
    mov al, 252
    jecxz L2; якщо (x) {
    виклик 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; у = 1
    cdq; z = 0
L1:
    inc edx; z ++
    додати al, al; y = y + y
    jnc $ + 4; пропустити XOR, якщо немає
    xor al, 29;
    cmp al, cl; якщо (у! = х) перейти до L1
    jne L1    
    xchg eax, edx; al = z
    аам 17; al | x = z% 17, ах | z = z / 17
    mov cl, ах; cl = z
    cmove eax, ecx; якщо (x == 0) al = z інакше al = x
    xlatb; al = k [z] або k [x]
    jz L2; якщо (x == 0) перейти до L2
    xor al, [ebx + ecx + 17]; k [x] ^ = k [z]
L2:
    рет

Розібраний 32-бітний код

00000000 B0FC mov al, 0xfc
00000002 E345 jecxz 0x49
00000004 E820000000 виклик слова 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 кдк
0000002D 42 inc edx
0000002E 00C0 додати al, al
00000030 7302 jnc 0x34
00000032 341D xor al, 0x1d
00000034 38C8 cmp al, кл
00000036 75F5 jnz 0x2d
00000038 92 xchg eax, edx
00000039 D411 aam 0x11
0000003B 88E1 mov cl, ах
0000003D 0F44C1 cmovz eax, ecx
00000040 D7 xlatb
00000041 7404 jz 0x47
00000043 32440B11 xor al, [ebx + ecx + 0x11]
00000047 C3 рет

1
Гарна відповідь! Оскільки ОР шукав підрахунок бітів, це (85 байт) виходить на 680 біт , використовуючи 8 біт на байт, або 595 біт, використовуючи 7 біт на байт (можливо, оскільки всі символи є ASCII). Можливо, ви могли б піти коротше, якби стиснути ще більш обмежувальний набір символів.
Cullub

1
Ласкаво просимо до PPCG; приємне перше рішення.
Кудлатий

9
@Cullub Моя думка полягала в тому, що код у цій відповіді - це просто C / Assembler, який збирається, тому кількість байтів не є заданим кодом, а зібраним кодом. І це не ASCII.
ArBo

7
Тільки для уточнення, 84 байти - це розмір машинного коду після його складання? Якщо так, заголовок слід оновити, щоб відобразити, що це відповідь машинного коду, а не відповідь на збірку.
Картопля44

1
І BTW, вам не доведеться використовувати стандартну угоду про виклики; Ви можете скористатися спеціальною конвенцією, де RBX є керованим викликом, економлячи 2 байти для push / pop. (І там, де uint8_tаргументи розширені до нуля до 64-розрядних для JRCXZ). Крім того, якщо ви пишете залежний від позиції код, ви можете помістити адресу таблиці в регістр з 5-байтовим mov ebx, imm32замість 6-байтовим call/ pop. Або використовувати його в якості disp32дюйма mov al, [table + rax], але це може втратити , так як у вас є два xlatbі movвже. Хоча виклик + поп-код оболонки, виграйте проти 7-байтного RIP-відносного LEA з даними після цього ret.
Пітер Кордес

23

CJam ,72 67 66 63 байти

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

es* щось повторюється за поточною міткою часу, що є великою кількістю, і закінчити це знадобиться занадто довго.

Насправді тестова версія, 64 байти:

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

Спробуйте в Інтернеті!

Спробуйте все в Інтернеті!

Щоб запустити цей код (на машині Linux), потрібно додати рядок en_US.ISO-8859-1 ISO-8859-1до /etc/locale.genта запустити locale-gen. Тоді ви можете використовувати:

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

Або ви можете спробувати цю еквівалентну 72-байтну версію UTF-8:

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

Пояснення

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.

Символи в рядку:

  • 221. Див. Нижче.
  • 48, 36, 38, 18. Це лінійне розкладання k на основі характеристик L у питанні.
  • 220, значення заповнення масиву s, коли використовується тільки масив k.
  • Масив s xor 220 обернено, за винятком останнього елемента або першого елемента перед зворотним, який є 221 на початку рядка.

Що б ви зробили з набором живлення? PS Я, мабуть, збираюся вкрасти цю хитрість робити операцію з кінцевим полем у зворотному напрямку. Дуже акуратно.
Пітер Тейлор

@PeterTaylor Ви можете отримати масив k, використовуючи набір потужності [48 36 38 18] (або його зворотну сторону) при найпростішому впорядкуванні, і зменшити кожен на xor. Але з мов для гольфу, які використовувались у цьому питанні дотепер, лише 05AB1E мав правильне замовлення. Джеллі і Стакс, мабуть, мали інше уявлення про те, що, на мою думку, повинно бути прямим.
jimmy23013

На даний момент я перебуваю у вашому відгуку на тему 05AB1E. Які цілі значення для вашого рядка "Ý0$&Ü™ÖD�’\n˜×EO“N"?
Кевін Кройсейсен

@KevinCruijssen Ви запитуєте про те, що вони мали на увазі, або їх числові значення (які ви могли бачити з шестигранного дампа)?
jimmy23013

@ jimmy23013 Ну, звичайно .. Забув про шестнадцятковий дамп ..
Кевін Круїйсен

19

Желе 71 59 байт

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

Спробуйте в Інтернеті!

Перевірте всі можливості

Тепер перепишіть, використовуючи перероблену версію розумного відповіді CJam jimmy23013, тому обов'язково підкажіть і цю! Використовує лише 472 біта (28,0% наївного підходу). @ jimmy23013 також врятував ще один байт!

Пояснення

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

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

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

Оригінальний підхід

Желе , 71 66 байт

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

Спробуйте в Інтернеті!

Перевірте всі можливості

Монадічна посилання або повна програма, яка приймає єдиний аргумент і повертає відповідне значення pi[x]. Це 536 біт, тобто менше третини наївного зберігання пі.

Збережено 3 байти, використовуючи метод пошуку lз відповіді CJam від jimmy23013, тому обов'язково підкреслить і цей!

Пояснення

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

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

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 байт

Помірне вдосконалення порівняно з прикладом С.

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

Спробуйте в Інтернеті!

C (gcc) , 150 142 127 126 байт

Це залежить від gcc та x86 та 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;}

Спробуйте в Інтернеті!

Завдяки @XavierBonnetain за -1 і менш невизначену поведінку.


10

05AB1E , 101 100 98 97 95 94 байт

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

-3 байти завдяки @Grimy .

Спробуйте в Інтернеті або перевірте всі тестові випадки .

Пояснення:

Функція C порта Xavier Bonnetain (версія 1106 біт) з таким же покращенням @ceilingcat зробила у своїй відповіді C, щоб зберегти 3 байти, тому обов'язково додайте йому нагоду!

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)

Ознайомтеся з цією підказкою 05AB1E (розділи Як стиснути великі цілі числа? І як стиснути цілі списки? ), Щоб зрозуміти, чому •α">η≠ε∍$<Θγ\&@(Σα•це 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]; і Ƶ∞є 188.


@Grimy Спасибі, я завжди забував такий тип гольфу зі стислими цілими списками .. (PS: Ви можете оточити код, що містить `у коментарях, з двома з обох сторін. Тобто з 'замість коду`:' ' ''.)
Кевін Круїссен

На жаль, вибачте за змішане форматування. Я знаю про подвоєння задників, але я не розумів, що стислий список мав зворотній зв'язок. Також: s^=> ^(XOR - комутативний). Насправді, це не s^_те саме, що Q?
Гримі

@Grimy Дякую! Ти справді правий. Ми в основному перевірити , якщо один з наступних трьох речей truthy для виходу з циклу: i==0 || X==0 || X==1.
Кевін Кройсейсен

10

Стакс , 65 64 62 59 58 байт

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

Запустіть і налагоджуйте його

На жаль, ця програма використовує деякі інструкції, які внутрішньо використовують деякі застарілі інструкції щодо stax. Я просто забув оновити їх реалізацію. Це призводить до появи помилкового попередження, але результати все-таки правильні.

На це надихає чудова відповідь jimmy23013 . Деякі частини були змінені так, щоб вони краще відповідали Стаксу.

Програми Stax, написані в друкованому ASCII, мають альтернативне подання, яке економить трохи більше 1 біта на байт, оскільки є лише 95 символів для друку ASCII.

Ось представлення ascii цієї програми, відформатоване для "читабельності" з коментарями.

{                       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

Виконати цей

Модифікована версія для запуску на всі входи 0..255


Stax має Sдля встановлення потужності. Ви можете отримати набір потужності [18 38 36 48], індексувати та зменшити на xor. (Я не знаю Стакса і не впевнений, що це буде коротше.)
jimmy23013

Я думаю, що замовлення Stax для підмножини, виробленої Sоператором, не в правильному порядку, щоб це працювало. наприклад "abc"SJ(powerset "abc", з'єднаний з пробілами), створює "ab abc ac b bc c".
рекурсивний

8

Python 3 , 151 байт

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

Спробуйте в Інтернеті!

Функція, яка реалізує перестановку. Код використовує лише 7-бітні символи ASCII.

Кодує kяк випробувальний Python 3, зміщений ^64у діапазон для друку. Навпаки, sкодується як основна-256 цифр числової константи, а цифри витягуються як [number]>>[shift]*8&255. Це було коротше, ніж кодування sв рядку через кількість необхідних символів, навіть для оптимального зрушення ^160для мінімізації цих символів .

Розрахунок дискретного журналу робиться назад. x=x*2^x//128*285Петлі оновлення вперед у циклічній групі, імітуючи множення на генеруючий генератор, поки не досягнуть ідентичності x=1. Ми починаємо дискретний журнал l=255(довжина циклу) і декрементуємо його кожну ітерацію. Щоб обробити цей x=0випадок і зробити його не циклічним назавжди, ми також припиняємо коли l=0, що робить x=0карту так, l=0як зазначено.


Python 2 програє, не маючи приємних балів, тому нам це потрібно зробити map(ord,...)(ArBo зберег байт тут). Це дозволяє використовувати нас, /а не //для цілого поділу.

Python 2 , 156 байт

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

Спробуйте в Інтернеті!


7

JavaScript (ES6), 139 байт

Схожий на версію Node.js, але з використанням символів, що виходять за рамки 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

Спробуйте в Інтернеті!


JavaScript (Node.js) ,  149  148 байт

На основі впровадження Xavier Bonnetain C (який представлений тут ).

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

Спробуйте в Інтернеті!

Кодування

У оригінальній відповіді Xavier таблиці s[]та k[]зберігаються у наступному рядку:

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

Перші 17 символів - це представлення ASCII, k[i] XOR 64а наступні 15 символів - представлення ASCII s[i-17] XOR 173або s[i-17] XOR 64 XOR 17 XOR 252.

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

Ось що ми отримуємо:

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

NB: Це лише бічна записка, не пов'язана з вищезазначеними відповідями.

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

Спробуйте в Інтернеті!



3

Пітон 3 , 182 байти

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]

Спробуйте в Інтернеті!

Python тут не збирається виграти перший приз, але це все-таки 10-байтний гольф найкращої програми Python тут .

Пітон 3 , 176 байт

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

Спробуйте в Інтернеті!

Як лямбда, вона ще на шість байтів коротша. Мені боляче потрібно використовувати if... else, але я не бачу іншого варіанту короткого замикання, враховуючи, наскільки 0 можлива відповідь.

Пітон 3 , 173 байти

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

Спробуйте в Інтернеті!

Ще коротше в байтах (хоча я не впевнений у бітах, тому що це вже не чистий ASCII), люб’язно надавши овець.


На 3 байти коротше , використовуючи буквальні символи замість \x..втечі
ов


@ovs Дякую! Можливо, дещо збільшується кількість розрядів (не впевнений, що є найважливішим для ОП), тому я також зберігатиму свою стару відповідь.
ArBo

2

Іржа , 170 163 байт

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

Спробуйте в Інтернеті!

Це порт мого рішення в C, дещо інший рядок, який не потребує xor 17. Я вважаю, що більшість рішень засновані на рядку "@` rFTDVbpPBvdtfR @ \ xacp? \ Xe2> 4 \ xa6 \ xe9 {z \ xe3q5 \ xa7 \ xe8 "також можна покращити (просто змініть рядок, видаліть xor 17 і xor 173 замість 188).

Я вилучив один із пошукових запитів, умовно додавши 17*17до цього l, як ми (більш-менш) це робили в машинному кодовому рішенні ARM.

У іржі є умовивід і закриття типу, але її заклики (навіть для булевих або між цілими числами) завжди явні, мутабельність має бути позначена, у неї немає потрійного оператора, цілих операцій, за замовчуванням, паніки при переповненні та мутаційних операціях ( l+=1) повертає одиницю. Мені не вдалося отримати коротше рішення з ітераторами, оскільки закриття + відображення все ще досить багатослівне.

Це, здається, робить Руста досить поганим вибором для гри в гольф. Тим не менш, навіть мовою, яка підкреслює читабельність та безпеку над стислістю, ми занадто короткі.

Оновлення: використовується анонімна функція, з пропозиції манатурки.


1
За винятком випадків, коли викликаються рекурсивно, анонімні функції / лямбда є прийнятними, тому ви можете перейти let p=до заголовка і не рахувати його. Не впевнені в тому ;, що для анонімного дзвінка не потрібно: Спробуйте його онлайн! .
манатура

1

05AB1E , 74 байти

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

Порт першої відповіді Желі на @NickKennedy . Я працював над портом відповіді CJam @ jimmy23013 безпосередньо , але я вже був у 78 байтах і все-таки довелося виправити помилку, так що він був би більшим. Це, безумовно, все ще може бути гольф зовсім небагато, хоча.

Спробуйте в Інтернеті або перевірте всі тестові випадки .

Пояснення:

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)

Ознайомтеся з цією підказкою 05AB1E (розділи Як стиснути великі цілі числа? І як стиснути цілі списки? ), Щоб зрозуміти, чому Ƶfце 142; •5›@¾ÂÌLìÁŒ©.ðǝš«YWǝŠ•є 29709448685778434533295690952203992295278432248, ƵŠє 239; і •5›@¾ÂÌLìÁŒ©.ðǝš«YWǝŠ•ƵŠвє [19,48,36,38,18,238,87,24,138,206,92,197,196,86,25,139,129,93,128,207].

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.