ロシアの暗号規格が構造化されすぎていることの証明


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は、2つのロシアの暗号化標準、つまりハッシュ関数Streebogとブロック暗号Kuznyechikのコンポーネントです。で、この記事(およびISO会議中)、これらのアルゴリズムの設計者は、彼らが、配列を生成したと主張しpiたランダム8ビットの順列を選ぶことで。

「不可能」な実装

256!21684あります2 1684年 8ビットに置換。したがって、与えられたランダムな順列に対して、それを実装するプログラムは、1683ビット未満しか必要としないことが予想されます。

しかし、たとえば次の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;
}

この表kk[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が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
同様に、たとえば、nopsがない場合、brainfuckコードは文字ごとに3ビットでスコアリングできますか?そして、それは1683 bits at most厳しい制限でしょうか?それとも目標ですか?
誰か

31
順列がランダムなプロセスの出力である場合(設計者が主張するように)、この短いプログラムは存在しません」私は同意しません(挑戦することは重要ではありませんが)。ランダムプロセスの出力である場合、そのようなプログラムが存在する可能性は低いでしょう。または見つけるのが難しいでしょう
ルイス・メンド

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
アーナウルド

回答:


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];
    }
    return x ^ 252;
}

64ビットx86アセンブリ

    ; AMD64アセンブリの78バイト
    ; オジャン
    ビット64

    %ifndef BIN
      グローバルSubBytex
    %endif

SubBytex:
    mov al、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:
    inc dl; z ++
    al、alを追加します。y = y + y
    jnc $ + 4; キャリーがない場合はXORをスキップします
    xor al、29;
    cmp al、cl; if(y!= x)goto 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)goto 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 call 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 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 ret

32ビットx86アセンブリ

    ; x86アセンブリの72バイト
    ; オジャン
    ビット32

    %ifndef BIN
      グローバルSubBytex
      グローバル_SubBytex
    %endif

SubBytex:
_SubBytex:
    mov al、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; if(y!= x)goto 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)goto L2
    xor al、[ebx + ecx + 17]; k [x] ^ = k [z]
L2:
    ret

逆アセンブルされた32ビットコード

00000000 B0FC mov al、0xfc
00000002 E345 jecxz 0x49
00000004 E820000000 call dword 0x29
; k [] = 0xfc、0xdc、0xce、0xfa、0xe8、0xf8、0xea、0xde、
; 0xcc、0xec、0xfe、0xca、0xd8、0xc8、0xda、0xee、0xfc
; s [] = 0x01、0xdd、0x92、0x4f、0x93、0x99、0x0b、0x44、
; 0xd6、0xd7、0x4e、0xdc、0x98、0x0a、0x45
00000029 5Bポップebx
0000002A B001 mov al、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 ret

1
素敵な答え!OPはビットカウントを探していたため、これ(85バイト)はバイトあたり8ビットを使用して680ビット、バイトあたり7ビットを使用して595ビットになります(すべての文字がASCIIであるため)。さらに制限の厳しい文字セットに圧縮すると、おそらく短くなる可能性があります。
カルブ

1
PPCGへようこそ。素敵な最初のソリューション。
シャギー

9
@Cullub私のポイントは、この回答のコードはコンパイルされるC / Assemblerであるため、バイトカウントは指定されたコードではなく、コンパイルされたコードのバイト数であるということです。そして、それはASCIIではありません。
ArBo

7
明確にするために、これがアセンブルされた後の84バイトはマシンコードのサイズですか?その場合、タイトルは、アセンブリの回答ではなくマシンコードの回答であることを反映するように更新する必要があります。
ポテト44

1
ところで、標準の呼び出し規約を使用する必要はありません。RBXがコールクラッバーされ、プッシュ/ポップ用に2バイトを節約するカスタム規則を使用できます。(また、uint8_targsはJRCXZの64ビットにゼロ拡張されます)。また、位置依存のコードを記述する場合mov ebx, imm32、6バイトのcall/ ではなく5バイトでテーブルアドレスをレジスタに入れることができますpop。またはdisp32in として使用しmov al, [table + rax]ますがxlatbmovすでに2つあるので、それは失われる可能性があります。retただし、call + popシェルコードトリックは、7バイトのRIP相対LEAに対して、の後のデータで勝ちます。
ピーターコーデス

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のフィラー値。
  • 配列s xor 220は、最後の要素、または反転前の最初の要素(文字列の先頭の221)を除き、反転しました。

パワーセットで何をしますか?PSおそらく、逆に有限体操作を行うというトリックを盗もうとしています。とてもきちんとした。
ピーターテイラー

@PeterTaylor [48 36 38 18](またはその逆)のべき乗セットを使用して、最も単純な順序で配列kを取得し、それぞれをxorずつ減らすことができます。しかし、これまでこの質問で使用されていたゴルフ言語では、正しい順序を持っていたのは05AB1Eだけでした。ゼリーとスタックスは、私が簡単だと思うことについて異なる考えを持っていたようです。
jimmy23013

現在、私は05AB1Eに対するあなたの答えをゴルフで話している最中です。あなたの文字列の整数値は何"Ý0$&Ü™ÖD�’\n˜×EO“N"ですか?
ケビンクルーイッセン

@KevinCruijssenあなたはそれらが何を意味したのか、それともその数値(16進ダンプから見ることができる)について尋ねていますか?
jimmy23013

@ jimmy23013ああ、もちろん。.16進ダンプを忘れた..
ケビン・クルーッセン

19

Jelly 71 59バイト

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

オンラインでお試しください!

すべての可能性を検証する

今、jimmy23013の巧妙なCJam回答の改訂版を使用して書き直されたので、必ずそれも支持してください!472ビットのみを使用します(単純なアプローチの28.0%)。@ jimmy23013はもう1バイトも節約しました!

説明

ステージ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の単純なストレージの3分の1未満です。

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)157 148 140 139バイト

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

@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ありがとう、私はいつも圧縮整数リストでその種のゴルフを忘れていました。 ''。)
ケビン・クルーッセン

おっと、めちゃくちゃフォーマットにごめんなさい。バックティックを2倍にすることは知っていますが、圧縮リストにバックティックが含まれていることに気付きませんでした。また:s^=> ^(XORは可換です)。実際にはs^_、と同じではありませんQか?
グリミー

@Grimyありがとう!あなたは確かに正しいです。基本的に、ループを終了するには、次の3つのいずれかが真実かどうかを確認します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の優れた回答に触発されています。いくつかの部品は、税金に適したものに変更されました。

印刷可能な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で実行するように修正されたバージョン


スタックスにSはパワーセットがあります。[18 38 36 48]のべき乗セットを取得し、xorでインデックスを作成して削減できます。(私はStaxを知りません、しかし、それがより短くなるかどうか確信がありません。)
jimmy23013

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文字のみを使用します。

kPython 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、whenを終了し、指定されたとおりに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文字は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

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

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

オンラインでお試しください!

ラムダとして、さらに6バイト短くなっています。を使用しなけれ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])

オンラインでお試しください!

ovsのおかげで、バイト単位でさらに短くなります(これはもう純粋なASCIIではないため、ビットについてはわかりません)。


\x..エスケープの代わりにリテラル文字を使用することで3バイト短縮
ovs


@ovsありがとう!おそらくビット数をいくらか増やします(OPにとって最も重要なものは不明です)ので、古い答えも保持します。
ArBo

2

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

オンラインでお試しください!

これは、xまたは17を必要としない、わずかに異なる文字列を持つCでの私のソリューションのポートです。ほとんどのソリューションは、文字列「@ `rFTDVbpPBvdtfR @ \ xacp?\ xe2> 4 \ xa6 \ xe9 {z \ xe3q5 \ xa7 \ xe8 "も改善できます(文字列を変更し、188ではなくxor 17とxor 173を削除します)。

ARMマシンコードソリューションで(多かれ少なかれ)行った17*17ようにl、条件付きでに追加することで、ルックアップの1つを削除しました。

Rustには型推論とクロージャがありますが、そのキャスト(ブール値または整数間でも)は常に明示的であり、可変性をマークする必要があり、三項演算子、整数演算、デフォルトではオーバーフロー、パニック、および変異演算(l+=1)ユニットを返します。クロージャとマッピングはまだかなり冗長なので、イテレータで短いソリューションを取得することはできませんでした。

これは、Rustをゴルフにかなり悪い選択にしているようです。それにもかかわらず、簡潔さよりも読みやすさと安全性を重視する言語であっても、私たちはあまりにも短すぎます。

更新:manatworkの提案から、匿名関数を使用しました。


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の最初の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のヒントを参照してください(セクション大きな整数を圧縮する方法は?整数のリストを圧縮する方法は?理由を理解することƵ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.