러시아 암호화 표준이 너무 구조화되어 있음을 증명


92

이 과제의 목표는 사용자가 선택한 언어로 다음 기능을 구현할 수 없을 정도로 짧은 구현 을 찾는 것입니다 p. 여기에 그것을 구현하는 C 코드 ( 출력을 인쇄하는 TIO 링크 참조 )와 그것을 포함 하는 wikipedia 페이지 가 있습니다.

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 및 블록 암호 Kuznyechik 의 구성 요소입니다 . 에서 이 문서 (및 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 비트에 맞습니다. 작동하는지 확인 하려면 여기클릭하십시오 .

순열이 임의 프로세스의 출력 (디자이너가 주장한대로) 인 경우이 짧은 프로그램이 존재하지 않기 때문에 "불가능하게"짧은 구현에 대해 이야기합니다 ( 자세한 내용 은 이 페이지 참조).

참조 구현

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

k는 C에서와 같이 XOR을 나타내는 의미에서 선형 k[x] = L(16-x)인 위치 L에 있습니다. 그러나 구현을 단축하기 위해이 속성을 활용하지 못했습니다. 우리는 더 간단한 구현을 허용하는 구조를 알지 못합니다. 출력은 항상 서브 필드에 있습니다. 즉 유한 필드에서 지수가 수행되는 입니다. 물론, 당신은 당신이 하나를 발견하면 더 간단한 표현을 사용할 수 있습니다!L(x^y)==L(x)^L(y)^ss[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에 제시되어 우리의 종이 이전의 짧은 구현을 얻을 수 있도록 최적화 대부분의 작성자와.

규칙

p1683 비트 미만으로 기능을 구현하는 프로그램을 제안하십시오 . 프로그램이 짧을수록 주어진 언어에 대해 비정상 일수록 짧을수록 좋습니다. 귀하의 언어에 Kuznyechik, Streebog 또는 p내장 언어가있는 경우 해당 언어를 사용할 수 없습니다.

최상의 구현을 결정하기 위해 사용하는 메트릭은 바이트 단위의 프로그램 길이입니다. 우리는 학술 논문에서 비트 길이를 사용하지만 단순성을 위해 여기에 바이트를 사용합니다.

언어에 함수, 인수 또는 출력에 대한 명확한 개념이없는 경우 인코딩은 사용자가 정의해야하지만 값 pi[x]x분명히 인코딩하는 것과 같은 트릭 은 금지되어 있습니다.

우리는 이미이 주제에 대한 연구 결과를 포함한 연구 논문을 제출했습니다. 그것은 사용할 수 있습니다 여기에 . 그러나 과학적인 장소에 게시되면 최고의 구현을 만든 작가에게 기꺼이 인정할 것입니다.

그건 그렇고,이 질문을 작성할 때 도움을 주신 xnor에게 감사드립니다!


12
누군가가 Seed에 답변을 제출하기를 바랍니다.
로빈 라이더

6
비슷하게, 예를 들어, brainfuck 코드가 nops가없는 경우 문자 당 3 비트로 점수를 매길 수 있습니까? 그리고 1683 bits at most엄격한 제한 [sic?]입니까 아니면 목표입니까?
누군가

31
" 그 순열이 (디자이너가 주장한대로) 랜덤 프로세스의 결과라면,이 짧은 프로그램은 존재 하지 않을 것입니다." 랜덤 프로세스의 출력 인 경우, 그러한 프로그램이 존재 하지 않았을 것 입니다. 또는 찾기 어려울 것입니다
Luis Mendo

8
@Grimy 이 단락이 존재하지 않는 프로그램 은 개념적인 것이지 프로그래밍적인 것이 아닙니다 . 여러분의 용어를 사용하면 프로그래밍 세계가 아닌 순수한 수학 세계에 속합니다.
Luis Mendo

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
Arnauld

답변:


35

AMD64 어셈블리 (78 바이트 또는 624 비트의 머신 코드)

uint8_t 서브 바이트 (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};

    if (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];
    }
    x ^ 252를 반환;
}

64 비트 x86 어셈블리

    ; 78 바이트의 AMD64 어셈블리
    ; 오잔
    비트 64

    % ifndef BIN
      글로벌 서브 바이트
    % endif

SubBytex :
    mov al, 252
    jecxz L2; if (x) {
    전화 L0
케이:
    db 0xfc, 0xdc, 0xce, 0xfa, 0xe8, 0xf8, 0xea, 0xde, 
    db 0xcc, 0xec, 0xfe, 0xca, 0xd8, 0xc8, 0xda, 0xee, 0xfc
에스:
    db 0x01, 0xdd, 0x92, 0x4f, 0x93, 0x99, 0x0b, 0x44, 
    db 0xd6, 0xd7, 0x4e, 0xdc, 0x98, 0x0a, 0x45
L0 :
    팝 RBX
    mov al, 1; y = 1
    cdq; z = 0
L1 :
    inc dl; z ++
    al, al 추가; y = y + y
    jnc $ + 4; 캐리가 없으면 XOR 건너 뛰기
    xor al, 29;
    cmp al, cl; if (y! = x) 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; if (x == 0) L2로 이동
    jz L2
    xchg eax, edx; al = x
    xlatb; al = k [x]
    xor al, cl; al ^ = s [z]
L2 :
    ret

분해 된 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 팝 RBX
0000002B B001 mov al, 0x1
0000002D 99 cdq
0000002E FEC2 inc dl
00000030 00C0 알, 알 추가
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 테스트 dl, dl
00000047 7404 jz 0x4d
00000049 92 xchg eax, edx
0000004A D7 xlatb
0000004B 30C8 xor al, cl
0000004D C3 리트

32 비트 x86 어셈블리

    ; 72 바이트의 x86 어셈블리
    ; 오잔
    비트 32

    % ifndef BIN
      글로벌 서브 바이트
      글로벌 _SubBytex
    % endif

SubBytex :
_SubBytex :
    mov al, 252
    jecxz L2; if (x) {
    전화 L0
케이:
    db 0xfc, 0xdc, 0xce, 0xfa, 0xe8, 0xf8, 0xea, 0xde, 
    db 0xcc, 0xec, 0xfe, 0xca, 0xd8, 0xc8, 0xda, 0xee, 0xfc
에스:
    db 0x01, 0xdd, 0x92, 0x4f, 0x93, 0x99, 0x0b, 0x44, 
    db 0xd6, 0xd7, 0x4e, 0xdc, 0x98, 0x0a, 0x45
L0 :
    팝 ebx
    mov al, 1; y = 1
    cdq; z = 0
L1 :
    Incedx; z ++
    al, al 추가; y = y + y
    jnc $ + 4; 캐리가 없으면 XOR 건너 뛰기
    xor al, 29;
    cmp al, cl; if (y! = x) L1로 이동
    jne L1    
    xchg eax, edx; al = z
    aam 17; al | x = z % 17, ah | z = z / 17
    mov cl, 아; cl = z
    cmove eax, ecx; if (x == 0) al = z else al = x
    xlatb; al = k [z] 또는 k [x]
    jz L2; if (x == 0) L2로 이동
    xor al, [ebx + ecx + 17]; k [x] ^ = k [z]
L2 :
    ret

분해 된 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 팝 ebx
0000002A B001 mov al, 0x1
0000002C 99 cdq
0000002D 42 INC EDX
0000002E 00C0 추가 알, 알
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 오전 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
좋은 대답입니다! OP가 비트 수를 찾고 있었기 때문에이 바이트 수는 바이트 당 8 비트를 사용하여 680 비트 또는 바이트 당 7 비트를 사용하는 595 비트로 나타납니다 (모든 문자가 ASCII이므로 가능). 더 제한적인 문자 세트로 압축하면 더 짧아 질 수 있습니다.
Cullub

1
PPCG에 오신 것을 환영합니다. 좋은 첫 번째 해결책.
얽히고 설킨

9
@Cullub 내 요점은이 답변의 코드는 컴파일 된 C / 어셈블러이므로 바이트 수는 주어진 코드의 수가 아니라 컴파일 된 코드의 바이트 수입니다. 그리고 그것은 ASCII가 아닙니다.
ArBo

7
명확히하기 위해 84 바이트는 이것이 조립 된 후 기계 코드의 크기입니까? 그렇다면 제목이 업데이트되어 어셈블리 답변이 아닌 기계 코드 답변임을 반영해야합니다.
감자 44시

1
그리고 BTW는 표준 전화 규칙을 사용할 필요가 없습니다. RBX가 호출 클로버되는 사용자 지정 규칙을 사용하여 푸시 / 팝에 2 바이트를 절약 할 수 있습니다. (그리고 uint8_tJRCXZ의 경우 args가 64 비트로 0 확장됩니다). 또한 위치 종속 코드를 작성하면 테이블 주소를 mov ebx, imm326 바이트 call/ 대신 5 바이트로 레지스터에 넣을 수 있습니다 pop. 아니면로 사용할 수 disp32있는 mov al, [table + rax],하지만 당신이 갖고 있기 때문에 그 잃을 수도 xlatbmov이미 있습니다. 콜 + 팝 셸 코드 트릭은 ret. 이후의 데이터로 7 바이트 RIP 상대 LEA보다 우수합니다 .
Peter Cordes

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. 그것은 문제에서 L의 특성에 기초한 k의 선형 분해입니다.
  • 도 220에서, 어레이 k만이 사용될 때 어레이 s의 필러 값.
  • 배열의 xor 220은 마지막 요소를 제외하고 반대의 첫 번째 요소 (문자열의 시작 부분에있는 221)를 제외하고 반전되었습니다.

전원 설정으로 무엇을 하시겠습니까? 추신 : 나는 아마도 유한 필드 연산을 수행하는 트릭을 훔칠 것입니다. 매우 깔끔합니다.
피터 테일러

@PeterTaylor 가장 간단한 순서로 [48 36 38 18]의 거듭 제곱 (또는 그 반대)을 사용하여 배열 k를 구하고 xor 씩 줄입니다. 그러나 지금까지이 질문에 사용 된 골프 언어에서는 05AB1E만이 올바른 순서를 가졌습니다. 젤리와 스탁스는 분명히 내가 생각했던 것에 대해 다른 생각을 가지고 있었다.
jimmy23013

저는 현재 05AB1E에 대한 귀하의 답변을 골프로 처리하고 있습니다. 문자열의 정수 값은 무엇입니까 "Ý0$&Ü™ÖD�’\n˜×EO“N"?
케빈 크루이 ssen

@KevinCruijssen 무엇을 의미하는지 또는 숫자 값 (16 진수 덤프에서 볼 수 있음)을 묻고 있습니까?
jimmy23013

@ jimmy23013 아, 물론 .. 16 진 덤프는 잊어
버렸어

19

젤리 71 59 바이트

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

온라인으로 사용해보십시오!

모든 가능성을 확인

이제 재 작업 된 jimmy23013의 영리한 CJam 답변을 사용하여 다시 작성 되었으므로 그 중 하나도 찬성 해야합니다! 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 비트이므로 pi의 순진 저장 용량의 1/3 미만입니다.

jimmy23013의 CJam 답변l 에서 찾은 방법을 사용하여 3 바이트 를 절약 했으므로 그중 하나도 찬성 해야합니다!

설명

스테이지 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) , 157148140139 바이트

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

온라인으로 사용해보십시오!

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

온라인으로 사용해보십시오!

-1 이하의 정의되지 않은 동작을위한 @XavierBonnetain 덕분입니다.


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è^

@Grimy 덕분에 -3 바이트 .

온라인으로 시도 하거나 모든 테스트 사례를 확인하십시오 .

설명:

Xavier Bonnetain의 C 함수 (1106 비트 버전)는 여기 에서 3 바이트를 절약하기 위해 C 응답 으로 만든 @ceilingcat 과 동일한 개선 기능을 사용 하므로 상향 조정 해야합니다!

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 고마워, 나는 항상 압축 된 정수 목록으로 그런 종류의 골프를 잊어 버렸다. ''.)
Kevin Cruijssen

엉망이 된 형식에 대해 죄송합니다. 두 배의 백틱에 대해 알고 있지만 압축 된 목록에 백틱이 있다는 것을 알지 못했습니다. 또한 : s^=> ^(XOR은 교환 형입니다). 실제로, s^_단지 같지 Q않습니까?
그리미

트윗 담아 가기 당신은 정말로 옳습니다. 우리는 기본적으로 다음 세 가지 중 하나가 루프를 종료하는 것이 진실인지 확인합니다 i==0 || X==0 || X==1.
Kevin Cruijssen

10

Stax , 65 64 62 59 58 바이트

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

실행 및 디버깅

불행하게도,이 프로그램은 내부적으로 더 이상 사용되지 않는 stax 명령어를 사용하는 일부 명령어를 사용합니다. 방금 구현을 업데이트하는 것을 잊었습니다. 이로 인해 약간의 경고가 표시되지만 결과는 여전히 정확합니다.

이것은 jimmy23013의 탁월한 답변 에서 영감을 얻었습니다 . 일부 부품은 Stax에 더 적합하도록 변경되었습니다.

인쇄 가능한 ASCII로 작성된 Stax 프로그램에는 95 개의 인쇄 가능한 ASCII 문자 만 있기 때문에 바이트 당 1 비트보다 약간 더 많은 대체 표현이 있습니다.

다음은 주석이 포함 된 "가독성"으로 형식화 된이 프로그램의 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로 인덱스 및 삭감 할 수 있습니다. (나는 Stax를 모르고 그것이 더 짧을 지 확신하지 못한다.)
jimmy23013

S운영자가 생성 한 하위 집합에 대한 stax의 순서 는 올바른 순서가 아니라고 생각합니다. 예를 들어 "abc"SJ(공백과 결합 된 "abc"의 파워 세트)는 "abbar abc b b c"를 생성합니다.
재귀

8

파이썬 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는 identity에 도달 할 때까지 generate로 곱하기를 시뮬레이션하여 순환 그룹에서 앞으로 루프 x=1됩니다. 이산 로그를 l=255(사이클 길이) 에서 시작하고 각 반복마다 감소시킵니다. 핸들하는 x=0경우를 때 그렇지 않은 루프 영원히, 우리는 또한 종료하게 l=0만드는, x=0에지도 l=0규정을.


파이썬 2는 멋진 map(ord,...)바이트 문자열 이 없기 때문에 잃어 버렸습니다 (ArBo는 여기에 바이트를 저장했습니다). 정수 나누기 /보다는 사용할 수 있습니다 //.

파이썬 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

자바 스크립트 (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

온라인으로 사용해보십시오!


자바 스크립트 (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

주의 : 이것은 위의 답변과 관련이없는 단지 참고입니다.

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]

온라인으로 사용해보십시오!

파이썬은 여기에서 일등상을 이기기 위하여려고하고 있지 않다, 그러나 이것은 여전히 최고의 파이썬 프로그램의 10 바이트 골프입니다 여기 .

파이썬 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])

온라인으로 사용해보십시오!

람다로서 여전히 6 바이트 더 짧습니다. 를 사용해야하는 것은 고통 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가 아니기 때문에 비트에 대해서는 확실하지 않지만).


\x..이스케이프 대신 리터럴 문자를 사용하여 3 바이트 단축
ovs


@ovs 감사합니다! 아마도 비트 수를 어느 정도 증가시킬 것이므로 (OP에 가장 중요한 것이 확실하지 않음) 이전 답변도 유지하겠습니다.
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을 제거하고 188 대신 xor 173을 제거하십시오).

나는 조건에 추가하여 조회 중 하나를 제거 17*17l우리는 (다소)는 ARM 기계 코드 용액에했던 것처럼.

Rust에는 형식 유추와 클로저가 있지만 캐스트 (부울 또는 정수 사이의 경우에도)는 항상 명시 적이며, 변경 가능성은 표시해야하며, 삼항 연산자, 정수 연산, 기본적으로 오버플로 및 패닉 연산이 없습니다 ( l+=1)는 단위를 반환합니다. 클로저 + 매핑이 여전히 장황하기 때문에 반복자를 사용하여 더 짧은 솔루션을 얻을 수 없었습니다.

이것은 Rust가 골프를하기에 아주 나쁜 선택 인 것처럼 보입니다. 그럼에도 불구하고 간결성에 대한 가독성과 안전성을 강조하는 언어로도 우리는 너무 짧습니다.

업데이트 : manatwork의 제안에서 익명의 기능을 사용했습니다.


1
재귀 적으로 호출되는 경우를 제외하고 익명 함수 / 람다가 허용되므로 let p=헤더 로 이동 하여 계산할 수 없습니다. ;익명의 전화가 필요하지 않으므로 에 대해 확실하지 않은 경우 : 온라인으로 시도하십시오! .
manatwork

1

05AB1E , 74 바이트

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

@NickKennedy 의 첫 번째 젤리 답변 포트 . 나는 @ jimmy23013 의 CJam 답변 포트에서 직접 작업 하고 있었지만 이미 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].

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.