证明俄罗斯密码标准过于结构化


92

这一挑战的目标是找到一个不可能短期实现以下功能p,在您选择的langage。这是实现它的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是这样的k[x] = L(16-x),其中L,在意义上是线性的L(x^y)==L(x)^L(y),并且像C中一样,^表示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位实现该功能。程序越短,对于给定的语言,越短越好。如果您的语言碰巧具有Kuznyechik,Streebog或p作为内置语言,则不能使用它们。

我们用来确定最佳实现的度量标准是程序长度(以字节为单位)。我们在学术论文中使用位长,但是为了简单起见,我们在这里坚持使用字节长。

如果你的语言不具备的功能,参数或输出一个清晰的概念,编码是由你来定义,但像编码值招数pi[x]作为x显然是禁止的。

我们已经提交了一份有关此主题研究结果的研究论文。在这里可用。但是,如果要在科学的地点发表,我们将很高兴地向您推荐最佳实现的作者。

顺便说一句,感谢xnor在起草这个问题时所提供的帮助!


12
我希望有人在“种子”中提交答案。
罗宾·赖德

6
同样,例如,如果没有脑筋,脑残代码是否可以按每个字符3位的评分?是1683 bits at most严格的限制还是“目标”?
某人

31
如果排列是随机过程的输出(如其设计者所声称的那样),那么这种简短的程序将不存在。 ”我不同意(尽管对挑战并不重要)。如果这是一个随机过程的输出,那么该程序就不可能存在。否则将很难找到
Luis Mendo

8
@Grimy 则该程序将不存在的声明是概念性的(不是编程性的)。用你的话说,它属于纯数学世界,不属于编程世界
Luis Mendo

7
它可能已经被注意到,但是以防万一:只能得到8个不同的值:(从并假设) 。 1 10 68 79 146 153 220 221 = 1 小号0 = 0si XOR si11,10,68,79,146,153,220,221i=1s0=0
Arnauld

Answers:


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

    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
      全局SubBytex
    %万一

子字节:
    电影252
    jecxz L2; if(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:
    流行rbx
    mov al,1; y = 1
    cdq; z = 0
L1:
    公司dl; z ++
    添加al,al; y = y + y
    jnc $ + 4; 如果没有进位则跳过XOR
    xor al,29岁;
    cmp al,cl;如果(y!= x)转到L1
    珍妮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
    吉士L2
    xchg eax,edx; 等于
    xlatb; al = k [x]
    xor al,cl; al ^ = s [z]
L2:
    退回

反汇编的64位代码

00000000 B0FC动画,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电影,0x1
0000002D 99 cdq
0000002E FEC2 inc dl
00000030 00C0加al,al
00000032 7302 jnc 0x36
00000034 341D xor al,0x1d
00000036 38C8 cmp al,cl
00000038 75F4 jnz 0x2e
0000003A 92 xchg eax,edx
0000003B 99 cdq
0000003C B111 mov cl,0x11
0000003E F7F1 div ECX
00000040 8A4C0311 mov cl,[rbx + rax + 0x11]
00000044 D7 xlatb
00000045 84D2测试dl,dl
00000047 7404 jz 0x4d
00000049 92 xchg eax,edx
0000004A D7 xlatb
0000004B 30C8 xor al,cl
0000004D C3退出

32位x86汇编

    ; x86汇编的72个字节
    ; 奥赞
    位32

    %ifndef BIN
      全局SubBytex
      全局_SubBytex
    %万一

子字节:
_SubBytex:
    电影252
    jecxz L2; if(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:
    流行ebx
    mov al,1; y = 1
    cdq; z = 0
L1:
    inc edx; z ++
    添加al,al; y = y + y
    jnc $ + 4; 如果没有进位则跳过XOR
    xor al,29岁;
    cmp al,cl;如果(y!= x)转到L1
    珍妮L1    
    xchg eax,edx; 等于
    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; 如果(x == 0)转到L2
    xor al,[ebx + ecx + 17]; k [x] ^ = k [z]
L2:
    退回

反汇编的32位代码

00000000 B0FC动画,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电影,0x1
0000002C 99 cdq
0000002D 42 inc edx
0000002E 00C0添加al,al
00000030 7302 jnc 0x34
00000032 341D xor al,0x1d
00000034 38C8 cmp al,cl
00000036 75F5 jnz 0x2d
00000038 92 xchg eax,edx
00000039 D411 aam 0x11
0000003B 88E1 mov cl,ah
0000003D 0F44C1 cmovz eax,ecx
00000040 D7 xlatb
00000041 7404 jz 0x47
00000043 32440B11 xor al,[ebx + ecx + 0x11]
00000047 C3退出

1
好答案!由于OP一直在寻找位数,所以这(85个字节)为680位(每字节使用8位),或595位(每字节使用7位)(可能是因为所有字符都是ASCII)。如果压缩为更具限制性的字符集,则可能会更短一些。
Cullub

1
欢迎来到PPCG;不错的第一个解决方案。
毛茸茸的

9
@Cullub我的意思是,此答案中的代码只是要编译的C / Assembler,因此字节数不是给定代码的字节数,而是已编译代码的字节数。那不是ASCII。
ArBo

7
为了澄清起见,汇编后的机器代码的大小为84个字节吗?如果是这样,则应该更新标题以反映这是机器代码的答案,而不是汇编的答案。
Potato44

1
顺便说一句,您不必使用标准的调用约定。您可以使用自定义约定,在该约定中RBX被呼叫,其中2个字节用于push / pop。(并且uint8_t对于JRCXZ ,args零扩展到64位)。另外,如果编写位置相关的代码,则可以将表地址放入5字节mov ebx, imm32而不是6字节call/ 的寄存器中pop。或将其用作disp32in mov al, [table + rax],但由于您已有两个xlatb和一个,因此可能会丢失mov。但是,call + pop shellcode技巧在后面的数据的确赢得了7字节相对RIP的LEA ret
Peter Cordes

23

CJam72 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的填充值。
  • 数组异或220取反,但最后一个元素或取反之前的第一个元素除外,即字符串开头的221。

您将如何设置电源?PS:我可能会偷走反向进行有限域运算的技巧。很整齐。
Peter Taylor

@PeterTaylor您可以使用[48 36 38 18]的幂集(或其反函数)以最简单的顺序获得数组k,并通过xor减少每个数组。但是到目前为止,在此问题中使用的高尔夫语言中,只有05AB1E具有正确的排序。对于我认为简单明了的内容,Jelly和Stax显然有不同的想法。
jimmy23013

我目前正在寻求您对05AB1E的答复。您的字符串的整数值是"Ý0$&Ü™ÖD�’\n˜×EO“N"多少?
凯文·克鲁伊森

@KevinCruijssen您是否在询问它们的含义或它们的数值(可以从十六进制转储中看到)?
jimmy23013

@ jimmy23013当然,嗯..忘记了十六进制转储..
Kevin Cruijssen

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

第二阶段

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

第三阶段

Ṫ                   | 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的三分之一。

通过使用ljimmy23013的CJam答案中查找的方法节省了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

第二阶段

         %?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

第三阶段

ị"“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;}

在线尝试!

感谢@XavierBonnetain提供-1和更少的未定义行为。


10

05AB1E101个 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

在线尝试验证所有测试用例

说明:

Xavier Bonnetain的C函数(1106位版本)的端口从这里开始在他的C答案中进行了@ceilingcat的相同改进以节省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:您可以在注释中包含包含`的代码,并且在注释的两边都加上。即用'而不是`:``code'code ''。)
Kevin Cruijssen

糟糕,对格式搞乱感到抱歉。我知道反引号加倍,但是我没有意识到压缩列表中有反引号。另外:s^=> ^(XOR是可交换的)。其实不s^_只是一样Q吗?
格林尼(Grimy)

@肮脏的谢谢!你确实是对的。我们基本上检查以下三件事之一是否正确,以退出循环:i==0 || X==0 || X==1
凯文·克鲁伊森

10

Stax65 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

我认为stax对S操作员产生的子集的排序不正确,无法正常工作。例如"abc"SJ(带空格的“ abc”的幂集)产生“ a 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&255s由于需要此转义字符的数量,因此此编码的时间比编码为字符串的时间短,即使使用最佳移位^160以最小化这些字符也是如此。

离散对数计算是向后进行的。x=x*2^x//128*285通过模拟生成乘以更新,直到循环到达恒等,更新在循环组中向前循环x=1。我们从l=255(周期长度)开始离散日志,并在每次迭代时将其递减。为了处理这种x=0情况并使其不会永远循环,我们还在时终止l=0,这使得x=0map 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) 149148 字节

基于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 6415个字符是s[i-17] XOR 173或的ASCII表示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

Python 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不会在这里赢得一等奖,但这仍然是这里最好的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])

在线尝试!

作为lambda,它还短了六个字节。不得不使用if... else,这让我很痛苦,但是鉴于0可能的答案,我没有看到另一种短路选择。

Python 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),由ovs提供。


通过使用文字字符而不是\x..转义


@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语言解决方案的端口,带有稍微不同的字符串,不需要xor17。我希望大多数解决方案都基于字符串“ @`rFTDVbpPBPBvdtfR @ \ xacp?\ xe2> 4 \ xa6 \ xe9 {z \ xe3q5 \ xa7 \ xe8“也可以得到改进(只需更改字符串,删除xor 17,然后删除xor 173而不是188)。

我有条件地添加17*17l,从而删除了其中一个查找,就像(或多或少)在ARM机器代码解决方案中所做的那样。

Rust具有类型推断和闭包,但其强制转换(即使是布尔值或整数之间)始终是显式的,必须标记为可变性,它没有三元运算符,整数运算(默认情况下),溢出时发生恐慌以及变异运算(l+=1)返回单位。我没有设法用迭代器获得一个更短的解决方案,因为闭包+映射仍然相当冗长。

这似乎使Rust成为打高尔夫球的不错选择。但是,即使使用强调简洁性而不是强调可读性和安全性的语言,我们也太短了。

更新:根据manatwork的建议使用了匿名功能。


1
除了可以递归调用之外,匿名函数/ lambda都是可以接受的,因此您可以移至let p=Header而不进行计数。;无需确定,因为不需要匿名呼叫:在线尝试!
manatwork

1

05AB1E,74 个字节

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

@NickKennedy的第一个Jelly答案端口。我直接@ 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尖端(部分如何压缩大的整数?以及如何压缩整数列表?理解为什么Ƶf142; •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]

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.