Chứng minh rằng một tiêu chuẩn mật mã của Nga quá cấu trúc


92

Mục đích của thách thức này là tìm ra một triển khai ngắn có thể thực hiện được của chức năng sau đây p, trong ngôn ngữ bạn chọn. Đây là mã C triển khai nó (xem liên kết TIO này cũng in các kết quả đầu ra của nó) và một trang wikipedia có chứa nó.

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

Những gì là p

plà một thành phần của hai tiêu chuẩn mật mã của Nga, cụ thể là hàm băm Streebog và mật mã khối Kuznyechik . Trong bài viết này (và trong các cuộc họp ISO), các nhà thiết kế của các thuật toán này tuyên bố rằng họ đã tạo ra mảng pibằng cách chọn các hoán vị 8 bit ngẫu nhiên.

Triển khai "không thể"

Có hoán vị trên 8 bit. Do đó, đối với một hoán vị ngẫu nhiên nhất định, một chương trình thực hiện nó sẽ không cần ít hơn 1683 bit.256!21684

Tuy nhiên, chúng tôi đã tìm thấy nhiều triển khai nhỏ bất thường (mà chúng tôi liệt kê ở đây ), ví dụ như chương trình C sau đây:

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

chỉ chứa 158 ký tự và do đó phù hợp với 1264 bit. Nhấn vào đây để thấy rằng nó hoạt động.

Chúng tôi nói về một triển khai ngắn "không thể tưởng tượng được" bởi vì, nếu hoán vị là đầu ra của một quá trình ngẫu nhiên (theo yêu cầu của các nhà thiết kế của nó), thì một chương trình ngắn này sẽ không tồn tại (xem trang này để biết thêm chi tiết).

Thực hiện tham khảo

Một phiên bản dễ đọc hơn của mã C trước đó là:

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

Bảng klà như vậy k[x] = L(16-x), nơi Llà tuyến tính theo nghĩa L(x^y)==L(x)^L(y), và ở đâu, như trong C, ^biểu thị XOR. Tuy nhiên, chúng tôi đã không quản lý để tận dụng tài sản này để rút ngắn việc thực hiện của chúng tôi. Chúng tôi không biết bất kỳ cấu trúc nào trong sđó có thể cho phép thực hiện đơn giản hơn --- mặc dù đầu ra của nó luôn ở trong trường con, tức là trong đó phép lũy thừa được thực hiện trong trường hữu hạn. Tất nhiên, bạn hoàn toàn tự do sử dụng một biểu thức đơn giản hơn nếu bạn tìm thấy!s[x]16=s[x]s

Vòng lặp while tương ứng với việc đánh giá một logarit rời rạc trong trường hữu hạn với 256 phần tử. Nó hoạt động thông qua một tìm kiếm brute-force đơn giản: biến giả ađược đặt thành một bộ tạo của trường hữu hạn và nó được nhân với bộ tạo này cho đến khi kết quả bằng x. Khi đó là trường hợp, chúng ta có đó llà nhật ký rời rạc của x. Hàm này không được định nghĩa bằng 0, do đó trường hợp đặc biệt tương ứng với ifcâu lệnh.

Phép nhân của trình tạo có thể được xem là phép nhân với trong sau đó được giảm modulo đa thức . Vai trò của là để đảm bảo rằng biến vẫn ở trên 8 bit. Ngoài ra, chúng ta có thể sử dụng , trong trường hợp đó có thể là một (hoặc bất kỳ loại số nguyên nào khác). Mặt khác, cần phải bắt đầu như chúng ta cần phải có khi bằng 1.XF2[X]X8+X4+X3+X2+1unsigned charaa=(a<<1)^(a>>7)*(256^29)aintl=1,a=2l=255x

Thông tin chi tiết về các thuộc tính pđược trình bày trong bài báo của chúng tôi , với phần viết về hầu hết các tối ưu hóa của chúng tôi để có được việc thực hiện ngắn trước đó.

Quy tắc

Đề xuất một chương trình thực hiện chức năng ptrong ít hơn 1683 bit. Chương trình càng ngắn thì càng bất thường, đối với một ngôn ngữ nhất định, ngắn hơn là tốt hơn. Nếu ngôn ngữ của bạn có Kuznyechik, Streebog hoặc pdưới dạng dựng sẵn, bạn không thể sử dụng chúng.

Số liệu chúng tôi sử dụng để xác định triển khai tốt nhất là độ dài chương trình tính bằng byte. Chúng tôi sử dụng độ dài bit trong bài báo học thuật của mình nhưng chúng tôi sử dụng byte ở đây vì mục đích đơn giản.

Nếu ngôn ngữ của bạn không có một khái niệm rõ ràng về chức năng, lập luận hoặc đầu ra, mã hóa là tùy thuộc vào bạn để xác định, nhưng thủ đoạn như mã hóa các giá trị pi[x]như xrõ ràng là bị cấm.

Chúng tôi đã gửi một bài nghiên cứu với những phát hiện của chúng tôi về chủ đề này. Nó có sẵn ở đây . Tuy nhiên, nếu nó được xuất bản ở một địa điểm khoa học, chúng tôi sẽ sẵn sàng thừa nhận các tác giả của các triển khai tốt nhất.

Nhân tiện, cảm ơn xnor vì sự giúp đỡ của anh ấy khi soạn thảo câu hỏi này!


12
Tôi hy vọng ai đó gửi câu trả lời trong Seed.
Robin Ryder

6
Tương tự, ví dụ, mã brainfuck có thể được ghi ở mức 3 bit cho mỗi ký tự nếu nó không có dấu chấm? Và là 1683 bits at mostmột hạn chế nghiêm ngặt [sic?] Hay mục tiêu?
ai đó

31
" Nếu hoán vị là đầu ra của một quá trình ngẫu nhiên (theo yêu cầu của các nhà thiết kế của nó), thì một chương trình ngắn này sẽ không tồn tại " Tôi không đồng ý (mặc dù nó không quan trọng đối với thử thách). Nếu đó là đầu ra của một quá trình ngẫu nhiên, thì chắc chắn chương trình đó không tồn tại; hoặc rất khó tìm
Luis Mendo

8
@Grimy Câu lệnh sau đó một chương trình ngắn này sẽ không tồn tại là một khái niệm (không phải là chương trình). Sử dụng thuật ngữ của bạn, nó thuộc về thế giới toán học thuần túy, không thuộc về thế giới lập trình
Luis Mendo

7
Nó có thể đã được chú ý, nhưng chỉ trong trường hợp: chỉ có 8 giá trị riêng biệt: (bắt đầu bằng và giả sử ) . 1 , 10 , 68 , 79 , 146 , 153 , 220 , 221 i = 1 s 0 = 0si XOR si11,10,68,79,146,153,220,221i=1s0=0
Arnauld

Câu trả lời:


35

Hội AMD64 (78 byte hoặc 624 bit mã máy)

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

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

    nếu (x) {
      for (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];
    }
    trả lại x ^ 252;
}

Lắp ráp x86 64 bit

    ; 78 byte lắp ráp AMD64
    ; odzhan
    bit 64

    % ifndef BIN
      SubBytex toàn cầu
    % endif

SubBytex:
    Mov al, 252
    jecxz L2; nếu (x) {
    gọi 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 ++
    thêm al, al; y = y + y
    jnc $ + 4; bỏ qua XOR nếu không mang
    xor al, 29;
    cmp al, cl; if (y! = x) goto L1
    jne L1    

    xchg eax, edx; ex = 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]
    kiểm tra dl, dl; if (x == 0) goto L2
    jz L2
    xchg eax, edx; al = x
    xlatb; al = k [x]
    xor al, cl; al ^ = s [z]
L2:
    nghỉ

Mã 64 bit đã phân tách

00000000 B0FC Mov al, 0xfc
00000002 67E348 jecxz 0x4d
00000005 E820000000 gọi 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 rbx pop 5B
0000002B B001 Mov al, 0x1
0000002D 99 cdq
0000002E FEC2 inc dl
00000030 00C0 thêm 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 B11 Mov cl, 0x11
0000003E F7F1 div ecx
00000040 8A4C0311 Mov cl, [rbx + rax + 0x11]
00000044 D7 xlatb
00000045 84D2 kiểm tra dl, dl
00000047 7404 jz 0x4d
00000049 92 xchg eax, edx
0000004A D7 xlatb
0000004B 30C8 xor al, cl
0000004D C3

Lắp ráp x86 32 bit

    ; 72 byte của x86 lắp ráp
    ; odzhan
    bit 32

    % ifndef BIN
      SubBytex toàn cầu
      toàn cầu _SubBytex
    % endif

SubBytex:
_SubBytex:
    Mov al, 252
    jecxz L2; nếu (x) {
    gọi 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 ++
    thêm al, al; y = y + y
    jnc $ + 4; bỏ qua XOR nếu không mang
    xor al, 29;
    cmp al, cl; if (y! = x) goto L1
    jne L1    
    xchg eax, edx; al = z
    sáng 17; al | x = z% 17, ah | z = z / 17
    Mov cl, ah; cl = z
    cmove eax, ecx; if (x == 0) al = z khác al = x
    xlatb; al = k [z] hoặc k [x]
    jz L2; if (x == 0) goto L2
    xor al, [ebx + ecx + 17]; k [x] ^ = k [z]
L2:
    nghỉ

Mã 32 bit đã phân tách

00000000 B0FC Mov al, 0xfc
00000002 E345 jecxz 0x49
00000004 E820000000 gọi từ 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 5x pop ebx
0000002A B001 Mov al, 0x1
0000002C 99 cdq
0000002D 42 inc edx
0000002E 00C0 thêm 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 sáng 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

1
Câu trả lời tốt đẹp! Vì OP đang tìm kiếm số bit, nên (85 byte) này có tới 680 bit , sử dụng 8 bit cho mỗi byte hoặc 595 bit sử dụng 7 bit mỗi byte (có thể vì tất cả các ký tự là ASCII). Bạn có thể có thể đi ngắn hơn nếu bạn nén thành một bộ ký tự thậm chí còn hạn chế hơn.
Cullub

1
Chào mừng đến với PPCG; giải pháp đầu tiên tốt đẹp.
Shaggy

9
@Cullub Quan điểm của tôi là, mã trong câu trả lời này chỉ là C / Trình biên dịch được biên dịch, do đó, số byte không phải là mã đã cho, mà là mã được biên dịch. Và đó không phải là ASCII.
ArBo

7
Chỉ cần làm rõ, 84 byte là kích thước của mã máy sau khi điều này đã được lắp ráp? Nếu vậy, tiêu đề nên được cập nhật để phản ánh rằng đây là câu trả lời mã máy chứ không phải là câu trả lời lắp ráp.
Khoai tây44

1
Và BTW, bạn không phải sử dụng quy ước gọi tiêu chuẩn; bạn có thể sử dụng quy ước tùy chỉnh trong đó RBX bị chặn cuộc gọi, tiết kiệm 2 byte cho việc đẩy / bật. (Và trong đó uint8_tcác đối số được mở rộng bằng 0 đến 64 bit cho JRCXZ). Ngoài ra, nếu bạn viết mã phụ thuộc vào vị trí, bạn có thể đặt địa chỉ bảng vào một thanh ghi với 5 byte mov ebx, imm32thay vì 6 byte call/ pop. Hoặc sử dụng nó như là một disp32trong mov al, [table + rax], nhưng điều đó có thể mất vì bạn đã có hai xlatbvà một mov. retMặc dù vậy, thủ thuật cuộc gọi + pop shellcode sẽ giành chiến thắng so với LEA tương đối RIP 7 byte với dữ liệu sau .
Peter Cordes

23

CJam ,72 67 66 63 byte

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

es* lặp lại một cái gì đó bởi dấu thời gian hiện tại, đây là một con số lớn và sẽ mất quá nhiều thời gian để hoàn thành.

Phiên bản thử nghiệm thực tế, 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

Hãy thử trực tuyến!

Hãy thử tất cả trực tuyến!

Để chạy mã này (trên máy Linux), bạn cần thêm dòng en_US.ISO-8859-1 ISO-8859-1vào /etc/locale.genvà chạy locale-gen. Sau đó, bạn có thể sử dụng:

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

Hoặc bạn có thể thử phiên bản UTF-8 tương đương 72 byte này:

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

Giải trình

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.

Các ký tự trong chuỗi là:

  • 221. Xem bên dưới.
  • 48, 36, 38, 18. Đó là phân rã tuyến tính của k dựa trên các đặc điểm của L trong câu hỏi.
  • 220, giá trị phụ của mảng s khi chỉ sử dụng mảng k.
  • Mảng s xor 220 bị đảo ngược, ngoại trừ phần tử cuối cùng hoặc phần tử đầu tiên trước khi đảo ngược, đó là phần tử thứ 221 ở đầu chuỗi.

Bạn sẽ làm gì với bộ điện? Tái bút: Có lẽ tôi sẽ đánh cắp mánh khóe đó để thực hiện thao tác trường hữu hạn theo chiều ngược lại. Rât gọn gang.
Peter Taylor

@PeterTaylor Bạn có thể nhận được mảng k bằng cách sử dụng bộ sức mạnh của [48 36 38 18] (hoặc ngược lại) với thứ tự đơn giản nhất và giảm từng xor. Nhưng trong các ngôn ngữ chơi gôn được sử dụng trong câu hỏi này cho đến nay, chỉ có 05AB1E có thứ tự đúng. Jelly và Stax rõ ràng có một ý tưởng khác nhau về những gì tôi nghĩ nên đơn giản.
jimmy23013

Tôi hiện đang trong quá trình đánh golf câu trả lời của bạn cho 05AB1E. Các giá trị số nguyên cho chuỗi của bạn là "Ý0$&Ü™ÖD�’\n˜×EO“N"gì?
Kevin Cruijssen

@KevinCruijssen Bạn có hỏi về ý nghĩa của chúng hoặc giá trị số của chúng (mà bạn có thể thấy từ kết xuất hex) không?
jimmy23013

@ jimmy23013 À, tất nhiên rồi .. Quên về hex-dump ..
Kevin Cruijssen

19

Jelly 71 59 byte

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

Hãy thử trực tuyến!

Xác nhận tất cả các khả năng

Bây giờ viết lại bằng cách sử dụng một phiên bản làm lại của câu trả lời thông minh CJam của jimmy23013, vì vậy hãy chắc chắn để nâng cao câu trả lời đó! Chỉ sử dụng 472 bit (28,0% của cách tiếp cận ngây thơ). @ jimmy23013 cũng đã lưu một byte khác!

Giải trình

Giai đoạn 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

Giai đoạn 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

Giai đoạn 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

Cách tiếp cận ban đầu

Thạch , 71 66 byte

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

Hãy thử trực tuyến!

Xác nhận tất cả các khả năng

Một liên kết đơn hoặc chương trình đầy đủ có một đối số duy nhất và trả về giá trị liên quan của pi[x]. Đây là 536 bit, do đó, dưới một phần ba lưu trữ ngây thơ của pi.

Đã lưu 3 byte bằng cách sử dụng phương pháp tìm kiếm ltừ câu trả lời CJam của jimmy23013, vì vậy hãy chắc chắn cũng nâng cấp nó!

Giải trình

Giai đoạn 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

Giai đoạn 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

Giai đoạn 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

Cải thiện khiêm tốn so với ví dụ 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;}

Hãy thử trực tuyến!

C (gcc) , 150 142 127 126 byte

Điều này phụ thuộc vào các quirks gcc và x86 và 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;}

Hãy thử trực tuyến!

Cảm ơn @XavierBonnetain cho -1 và ít hành vi không xác định.


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 nhờ @Grimy .

Hãy thử trực tuyến hoặc xác minh tất cả các trường hợp thử nghiệm .

Giải trình:

Chức năng C của cổng Xavier Bonnetain (phiên bản 1106 bit) từ đây , với cùng một cải tiến @ceilingcat được thực hiện trong câu trả lời C của anh ta để tiết kiệm 3 byte, vì vậy hãy đảm bảo nâng cấp anh ta!

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)

Xem mẹo 05AB1E này của tôi (phần Làm thế nào để nén các số nguyên lớn?Làm thế nào để nén danh sách số nguyên? ) Để hiểu tại sao •α">η≠ε∍$<Θγ\&@(Σα•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]; và Ƶ∞188.


@Grimy Cảm ơn, tôi luôn quên loại golf đó với danh sách số nguyên đã nén .. (PS: Bạn có thể bao quanh mã chứa `trong các nhận xét với hai trong số chúng ở cả hai bên. Tức là với 'thay vì`:' 'code'code ''.)
Kevin Cruijssen

Rất tiếc, xin lỗi về định dạng sai lầm. Tôi biết về việc nhân đôi backticks, nhưng tôi không nhận ra danh sách nén có backtick trong đó. Ngoài ra: s^=> ^(XOR là giao hoán). Trên thực tế, không s^_giống như Q?
Grimy

@Grimy Cảm ơn! Bạn thực sự đúng. Về cơ bản chúng tôi kiểm tra xem một trong ba điều sau đây có đúng không khi thoát khỏi vòng lặp : 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ü Ü╕║

Chạy và gỡ lỗi nó

Thật không may, chương trình này sử dụng một số hướng dẫn sử dụng nội bộ một số hướng dẫn không được chấp nhận. Tôi chỉ quên cập nhật thực hiện của họ. Điều này khiến một số cảnh báo giả xuất hiện, nhưng kết quả vẫn đúng.

Điều này được lấy cảm hứng từ câu trả lời tuyệt vời của jimmy23013 . Một số phần đã được thay đổi để phù hợp với stax tốt hơn.

Các chương trình Stax được viết bằng ASCII có thể in có biểu diễn thay thế giúp tiết kiệm hơn 1 bit mỗi byte một chút, vì chỉ có 95 ký tự ASCII có thể in được.

Đây là đại diện ascii của chương trình này được định dạng cho "khả năng đọc" với các bình luận.

{                       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

Chạy cái này

Phiên bản sửa đổi để chạy cho tất cả các đầu vào 0..255


Stax có Scho bộ điện. Bạn có thể nhận được tập hợp sức mạnh của [18 38 36 48], lập chỉ mục và giảm theo xor. (Tôi không biết Stax và tôi không chắc nó sẽ ngắn hơn.)
jimmy23013

Tôi nghĩ rằng việc đặt hàng của stax cho các tập hợp con do Snhà điều hành sản xuất không theo đúng thứ tự để nó hoạt động. ví dụ: "abc"SJpowerset của "abc" được nối với dấu cách) tạo ra "a ab abc ac b bc c".
đệ quy

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

Hãy thử trực tuyến!

Một hàm thực hiện hoán vị. Mã này chỉ sử dụng ký tự ASCII 7 bit.

Mã hóa kdưới dạng Python 3 bytestring, được chuyển ^64sang phạm vi có thể in được. Ngược lại, sđược mã hóa dưới dạng 256 chữ số cơ bản của hằng số và các chữ số được trích xuất là [number]>>[shift]*8&255. Thời gian này ngắn hơn mã hóa strong một chuỗi do số lượng ký tự thoát cần thiết này, ngay cả với một sự thay đổi tối ưu ^160để giảm thiểu những ký tự đó.

Việc tính toán nhật ký rời rạc được thực hiện ngược. Các x=x*2^x//128*285vòng lặp cập nhật chuyển tiếp trong nhóm tuần hoàn bằng cách mô phỏng nhân với tạo, cho đến khi nó đạt đến danh tính x=1. Chúng tôi bắt đầu nhật ký rời rạc tại l=255(độ dài chu kỳ) và giảm dần mỗi lần lặp. Để xử lý x=0trường hợp này và làm cho nó không lặp lại mãi mãi, chúng tôi cũng chấm dứt khi nào l=0, điều này làm cho x=0bản đồ l=0theo quy định.


Python 2 thua vì không có bytestrings đẹp, vì vậy chúng ta cần phải làm map(ord,...)(ArBo đã lưu một byte ở đây). Nó cho phép chúng ta sử dụng /chứ không phải //để phân chia số nguyên.

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

Hãy thử trực tuyến!


7

JavaScript (ES6), 139 byte

Tương tự như phiên bản Node.js, nhưng sử dụng các ký tự nằm ngoài phạm vi 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

Hãy thử trực tuyến!


JavaScript (Node.js) ,  149  148 byte

Dựa trên triển khai C của Xavier Bonnetain (được trình bày ở đây ).

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

Hãy thử trực tuyến!

Mã hóa

Trong câu trả lời ban đầu của Xavier, các bảng s[]k[]được lưu trong chuỗi sau:

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

17 ký tự đầu tiên là các đại diện ASCII k[i] XOR 64và 15 ký tự tiếp theo là các đại diện ASCII của s[i-17] XOR 173, hoặc s[i-17] XOR 64 XOR 17 XOR 252.

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

Đây là những gì chúng ta nhận được:

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: Đây chỉ là một ghi chú bên lề, không liên quan đến các câu trả lời trên.

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

Hãy thử trực tuyến!



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]

Hãy thử trực tuyến!

Python sẽ không giành được giải nhất ở đây, nhưng đây vẫn là một golf 10 byte của chương trình Python tốt nhất ở đây .

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

Hãy thử trực tuyến!

Là một lambda, nó vẫn ngắn hơn sáu byte. Tôi cảm thấy đau đớn khi phải sử dụng if... else, nhưng tôi không thấy một tùy chọn khác cho việc đoản mạch, với lý do 0 là một câu trả lời có thể.

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

Hãy thử trực tuyến!

Thậm chí ngắn hơn về byte (mặc dù tôi không chắc chắn về bit, vì đây không còn là ASCII thuần túy nữa), lịch sự của các lò nướng.


\x..
Ngắn


@ovs Cảm ơn! Có lẽ làm tăng số bit một chút (không chắc chắn điều gì là quan trọng nhất đối với OP), vì vậy tôi cũng sẽ giữ câu trả lời cũ của mình.
ArBo

2

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

Hãy thử trực tuyến!

Đây là một cổng giải pháp của tôi trong C, với một chuỗi hơi khác, không yêu cầu xor 17. Tôi hy vọng rằng hầu hết các giải pháp dựa trên chuỗi "@` rFTDVbpPBvdtfR @ \ xacp? \ Xe2> 4 \ xa6 \ xe9 {z \ xe3q5 \ xa7 \ xe8 "cũng có thể được cải thiện (chỉ cần thay đổi chuỗi, xóa xor 17 và xor 173 thay vì 188).

Tôi đã gỡ bỏ một trong những tra cứu bằng cách có điều kiện bổ sung 17*17để l, như chúng ta (nhiều hơn hoặc ít hơn) đã làm trong các giải pháp ARM mã máy.

Rust có kiểu suy luận và đóng, nhưng các phôi của nó (ngay cả đối với booleans hoặc giữa các số nguyên) luôn rõ ràng, tính biến đổi phải được đánh dấu, nó không có toán tử ternary, hoạt động số nguyên, theo mặc định, hoảng loạn khi tràn và hoạt động đột biến ( l+=1) trả về đơn vị. Tôi đã không quản lý để có được một giải pháp ngắn hơn với các trình vòng lặp, vì các bao đóng + ánh xạ vẫn còn khá dài.

Điều này dường như làm cho Rust trở thành một lựa chọn khá tệ cho việc chơi golf. Tuy nhiên, ngay cả trong một ngôn ngữ nhấn mạnh tính dễ đọc và an toàn hơn sự đồng nhất, chúng ta vẫn còn quá ngắn.

Cập nhật: đã sử dụng một chức năng ẩn danh, từ đề xuất của manatwork.


1
Ngoại trừ khi được gọi đệ quy, các hàm ẩn danh / lambdas có thể được chấp nhận, vì vậy bạn có thể di chuyển let p=đến Tiêu đề và không tính nó. Không chắc chắn về ;, như đối với cuộc gọi nặc danh là không cần thiết: Hãy thử trực tuyến! .
manatwork

1

05AB1E , 74 byte

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

Cổng câu trả lời Jelly đầu tiên của @NickKennedy . Tôi đã làm việc trực tiếp trên một cổng của câu trả lời CJam của @ jimmy23013 , nhưng tôi đã ở mức 78 byte và vẫn phải sửa một lỗi, vì vậy nó sẽ lớn hơn. Điều này chắc chắn vẫn có thể được chơi golf khá nhiều, mặc dù.

Hãy thử trực tuyến hoặc xác minh tất cả các trường hợp thử nghiệm .

Giải trình:

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)

Xem mẹo 05AB1E này của tôi (phần Làm thế nào để nén các số nguyên lớn?Làm thế nào để nén danh sách số nguyên? ) Để hiểu tại sao Ƶf142; •5›@¾ÂÌLìÁŒ©.ðǝš«YWǝŠ•29709448685778434533295690952203992295278432248, ƵŠ239; và •5›@¾ÂÌLìÁŒ©.ðǝš«YWǝŠ•ƵŠв[19,48,36,38,18,238,87,24,138,206,92,197,196,86,25,139,129,93,128,207].

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.