计算Adler-32校验和


32

背景

Adler-32是Mark Adler在1995年发明的32位校验和,它是被广泛使用的zlib库的一部分(也由Adler开发)。Adler-32不像32位循环冗余校验那样可靠,但是–至少在软件中–它更快,更容易实现。

定义

B = [b 1,,b n ]为字节数组。

B的Adler-32校验和定义为low + 65536×high的结果,其中:

  • 低:=(((1 + b 1 +⋯+ b n)mod 65521)

  • 高:=((((1 + b 1)+(1 + b 1 + b 2)+⋯(1 + b 1 +⋯+ b n))mod 65521)

任务

给定一个字节数组作为输入,请遵循以下条件计算并返回其Adler-32校验和。

  • 您可以将输入作为字节或整数数组或字符串。

    在这两种情况下,输入中只会出现对应于可打印ASCII字符的字节。

    您可以假设输入的长度将满足0 <length≤4096

  • 如果选择打印输出,则可以使用不超过256的正数。

    如果选择一元,确保解释器可处理多达2个32 - 983056字节输出的机器上的RAM 16吉布。

  • 禁止计算Adler-32校验和的内置函数。

  • 适用标准规则。

测试用例

String:     "Eagles are great!"
Byte array: [69, 97, 103, 108, 101, 115, 32, 97, 114, 101, 32, 103, 114, 101, 97, 116, 33]
Checksum:   918816254

String:     "Programming Puzzles & Code Golf"
Byte array: [80, 114, 111, 103, 114, 97, 109, 109, 105, 110, 103, 32, 80, 117, 122, 122, 108, 101, 115, 32, 38, 32, 67, 111, 100, 101, 32, 71, 111, 108, 102]
Checksum:   3133147946

String:     "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
Byte array: [126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126]
Checksum:   68095937

String:     <1040 question marks>
Byte array: <1040 copies of 63>
Checksum:   2181038080

7
我将注意到,由于将模运算推迟到计算总和之后,当它们使32位或64位整数和溢出时,对于大的或非常大的输入序列,此处的许多答案都将失败。一个真正合规的实现将需要至少定期进行模运算,以防止总和溢出。只有4096 0xff之后,一个32位带符号整数将溢出。在256 MiB的0xff之后,一个64位带符号整数将溢出。
Mark Adler

@MarkAdler嗯,很公平。由于我没有指定解决方案必须适用于任意长的字符串,并且我不想使现有答案无效,因此我将设置输入长度的限制。
丹尼斯

@MarkAdler我不认为这很重要。我相当确定,溢出(带符号的32位整数)只能在输入4104或更多字节时发生,因为在取模之前,high的最大值是n *(n + 1)/ 2 * 255 + n。最重要的是,挑战将输入限制为与可打印ASCII字符相对应的字节。
丹尼斯

我们还可以允许语言溢出其数字类型,并且仅要求返回的结果与正确的结果相等(考虑到溢出)。
英里

1
@PeterCordes是的,32位整数数组完全可以。至少在我看来,提交的内容应侧重于算法的开发,而应尽可能少地关注I / O。
丹尼斯,

Answers:


3

果冻,19 17字节

+\,S‘S€%65521ḅ⁹²¤

在线尝试!

+\,S‘S€%65521ḅ⁹²¤    Main monadic chain. Takes array as only argument.

                     The array is shown here as [b1 b2 ... bn].
+\                   Reduce by addition (+) while returning immediate results.
                         yields [b1 b1+b2 ... b1+b2+...+bn].

  ,                  Concatenate with...
   S                 the sum of the argument.
                         yields [[b1 b1+b2 ... b1+b2+...+bn] b1+b2+...+bn].

    ‘                Increment [each].
                         yields [[1+b1 1+b1+b2 ... 1+b1+b2+...+bn] 1+b1+b2+...+bn].

     S€              Sum each list.
                         yields [[1+b1+1+b1+b2+...+1+b1+b2+...+bn] 1+b1+b2+...+bn].

       %65521        Modulo [each] by 65521.

             ḅ⁹²¤    Convert from base    65536    to integer.
              ⁹                        256
               ²                           squared

更好的是:⁹²¤
Dennis

1
@Dennis我已经超出了您的18字节。
Leaky Nun

1
好吧,已经超越了..
Leaky Nun

64

Mathematica,46个字节

{1,4^8}.Fold[##+{0,#&@@#}&,{1,0},#]~Mod~65521&

匿名函数,它使用整数数组并返回Adler-32,并对Miles和Martin进行了一些改进(请参见注释)。

miles'也为46个字节,但速度更快:

{1,4^8}.{Tr@#+1,Tr[Accumulate@#+1]}~Mod~65521&

37
...您只是打高尔夫自己的著名算法吗?
Mego

25
如果我有点明星,请原谅我。在我们不起眼的小型站点上,您每天都不会在软件工程中看到如此大的称号。欢迎上车!
Mego

6
不是那么大。
Mark Adler

3
如果您是说我,这是我第一次想到在Mathematica中实现Adler-32。
Mark Adler

9
也许自从加入Code Golf以来,您已经准备好此解决方案,只是在等待提出要求。“最后!” ;-)
Antti Haapala's

13

朱莉娅73 46字节

x->[sum(x)+1;sum(cumsum(x)+1)]%65521⋅[1;4^8]

这是一个匿名函数,它接受一个数组并返回一个整数。要调用它,请将其分配给变量。

我们结合sum(x) + 1sum(cumsum(x) + 1)成阵列,其中x是输入阵列,并采取每个模65521.然后,我们计算点积与图1和4 8,这使我们(sum(x) + 1) + 4^8 * sum(cumsum(x) + 1),而这正是阿德勒-32式。

在线尝试!(包括所有测试用例)

由于Sp3000和Dennis节省了27个字节!


哇,那真的很聪明。

@cat我有Sp3000和Dennis,感谢他们的聪明才智。:)
Alex A.

11

x86-64机器码功能:33 32字节(或使用输入代替的31 30字节)int[]char[]

x86-32机器码功能:31个字节

作为GNU C内联汇编代码片段:保存2B 1B(仅retinsn)。

在github上评论了源代码和测试驱动程序

可使用标准System V x86-64 ABI从C直接调用64位版本(使用2个虚拟args在我想要的regs中获取args)。自定义调用约定在asm代码中并不罕见,因此这是一项奖励功能。

32位机器代码可节省1B,因为合并高和低半部分push16/push16 => pop32仅适用于32位模式。32位函数将需要自定义调用约定。我们不应该反对这一点,但是从C调用需要一个包装函数。

处理4096 ~(ASCII 126)字节后,high = 0x3f040000, low = 0x7e001。因此,high尚未设置的最高位。我的代码利用了这一点,符号扩展eaxedx:eaxcdq作为归零的方式edx

# See the NASM source below
0000000000401120 <golfed_adler32_amd64>:
  401120:       31 c0                   xor    eax,eax
  401122:       99                      cdq    
  401123:       8d 7a 01                lea    edi,[rdx+0x1]
0000000000401126 <golfed_adler32_amd64.byteloop>:
  401126:       ac                      lods   al,BYTE PTR ds:[rsi]
  401127:       01 c7                   add    edi,eax
  401129:       01 fa                   add    edx,edi
  40112b:       e2 f9                   loop   401126 <golfed_adler32_amd64.byteloop>
000000000040112d <golfed_adler32_amd64.end>:
  40112d:       66 b9 f1 ff             mov    cx,0xfff1
  401131:       92                      xchg   edx,eax
  401132:       99                      cdq    
  401133:       f7 f1                   div    ecx
  401135:       52                      push   rdx
  401136:       97                      xchg   edi,eax
  401137:       99                      cdq    
  401138:       f7 f1                   div    ecx
  40113a:       66 52                   push   dx      # this is the diff from last version: evil push/pop instead of shift/add
  40113c:       58                      pop    rax
  40113d:       66 5a                   pop    dx
  40113f:       c3                      ret    
0000000000401140 <golfed_adler32_amd64_end>:

0x40 - 0x20 = 32字节。


NASM评论来源:

技巧:

  • xchg eax, r32是一个字节;比mov便宜。8086需要斧头中的数据比> = 386更多的东西,因此他们决定在现在很少使用的代码上花费大量的操作码空间xchg ax, r16

  • 混合使用push64和push16将高低合并为一个寄存器,可将reg-reg数据移动指令保存大约2 divs。此技巧的32位版本效果更好:push16 / push16 / pop32总共只有5B,而不是6B。

由于我们推送/弹出,因此这对于SysV amd64 ABI(带有红色区域)中的内联汇编是不安全的

golfed_adler32_amd64_v3:   ; (int dummy, const char *buf, int dummy, uint64_t len)

    ;; args: len in rcx,  const char *buf in rsi
    ;; Without dummy args, (unsigned len, const char *buf),  mov ecx, edi is the obvious solution, costing 2 bytes

    xor     eax,eax         ; scratch reg for loading bytes
    cdq                     ; edx: high=0
    lea     edi, [rdx+1]    ; edi: low=1
    ;jrcxz  .end            ; We don't handle len=0.  unlike rep, loop only checks rcx after decrementing
.byteloop:
    lodsb                   ; upper 24b of eax stays zeroed (no partial-register stall on Intel P6/SnB-family CPUs, thanks to the xor-zeroing)
    add     edi, eax        ; low += zero_extend(buf[i])
    add     edx, edi        ; high += low
    loop   .byteloop
.end:
    ;; exit when ecx = 0, eax = last byte of buf
    ;; lodsb at this point would load the terminating 0 byte, conveniently leaving eax=0

    mov     cx, 65521       ; ecx = m = adler32 magic constant.  (upper 16b of ecx is zero from the loop exit condition.  This saves 1B over mov r32,imm32)
    ;sub    cx, (65536 - 65521) ; the immediate is small enough to use the imm8 encoding.  No saving over mov, though, since this needs a mod/rm byte

    xchg    eax, edx        ; eax = high,  edx = buf[last_byte]
    cdq                     ; could be removed if we could arrange things so the loop ended with a load of the 0 byte

    div     ecx             ; div instead of idiv to fault instead of returning wrong answers if high has overflowed to negative.  (-1234 % m is negative)
    push    rdx             ; push high%m and 6B of zero padding

    xchg    eax, edi        ; eax=low
    cdq
    div     ecx             ; edx = low%m

    ;; concatenate the two 16bit halves of the result by putting them in contiguous memory
    push    dx              ; push low%m with no padding
    pop     rax             ; pop  high%m << 16 | low%m   (x86 is little-endian)

    pop     dx              ; add rsp, 2 to restore the stack pointer

    ;; outside of 16bit code, we can't justify returning the result in the dx:ax register pair
    ret
golfed_adler32_amd64_end_v3:

我还考虑了使用rcx数组索引,而不是使用两个循环计数器,而是adler32(s)!= adler32(reverse(s))。所以我们不能使用loop。从-len向上计数到零,然后使用会占用movzx r32, [rsi+rcx]太多字节。

如果我们想考虑自己增加指针,则可能需要32位代码。甚至x32 ABI(32位指针)也不足够,因为inc esiamd64上为2B,而i386上为1B。似乎很难击败xor eax,eax// lodsb/ loop:总共4B才能使每个元素依次零扩展为eax。 inc esi/ movzx r32, byte [esi]/ loop是5B。

scas是在64位模式下使用1B指令递增指针的另一种选择。(rdi/ edi代替rsi,因此我们将指针arg放入rdi)。但是,我们不能将标记结果来自scas作为循环条件,因为我们不想将eax保持为零。循环后,不同的寄存器分配可能会节省一个字节。


int[] 输入

采取全部功能uint8_t[]是“主要”答案,因为这是一个更有趣的挑战。int[]要求我们的呼叫者使用这种语言解包是不合理的事情,但这确实节省了2B。

如果将输入作为32位整数的解压缩数组,则可以轻松保存一个字节(使用lodsd并替换xor eax,eax / cdq为just xor edx,edx)。

我们可以通过使用lodsd/ cdq将edx归零来保存另一个字节,然后重新安排循环,以便在退出前加载终止的0元素。(即使它是的数组int,也不是字符串,我们仍然假设它存在)。

; untested: I didn't modify the test driver to unpack strings for this
golfed_adler32_int_array:
    ; xor   edx,edx
    lodsd                   ; first element. only the low byte non-zero
    cdq                     ; edx: high=0
    lea     edi, [rdx+1]    ; edi: low=1
    ;jrcxz  .end            ; handle len=0?  unlike rep, loop only checks rcx after decrementing
.intloop:
    add     edi, eax        ; low += buf[i]
    add     edx, edi        ; high += low
    lodsd                   ; load buf[i+1] for next iteration
    loop   .intloop
.end:
    ;; exit when ecx = 0, eax = terminating 0

    xchg    eax, edx
    ;cdq               ; edx=0 already, ready for div
    ; same as the char version

我还制作了一个未经测试的版本,该版本使用scasd(的的1B版本add edi,4)而add eax, [rdi]不是lodsd,但它也是30个字节。high循环末尾的eax 节省可以通过其他地方的较大代码来平衡。但是,它的优点是不依赖0输入中的终止元素,这对于未打包的数组可能是不合理的,在该数组中我们也明确给出了长度。


C ++ 11测试驱动程序

参见github链接。这个答案变得太大了,并且测试驱动程序通过更大的代码获得了更多功能。


2
我允许使用整数而不是字节,主要是因为许多语言甚至没有字节类型。32位整数对于汇编来说可能是不自然的选择,但是代码高尔夫是要在遵守规则的情况下挤出最后一个字节。如果“不自然”的选择导致字节数减少,我会说。
丹尼斯

@丹尼斯:我了解某些语言需要该规则。我希望有一种方法可以让该规则仅int[]在必要时使用,或者保存4个字节以上的代码或其他内容。提出问题的解决方案没有问题adler32(int[]),但是我觉得这个adler32(char[])问题更有趣,因为它是真正的adler32函数。这就是我真正想要在asm中打高尔夫球的东西。(而且,我非常想以某种方式节省一个字节,因为在现实生活中,如果下一个函数使用asm,则33字节= 48字节ALIGN 16)。我想我都会继续打高尔夫球。
彼得·科德斯

@Dennis:另外,我们是否需要处理len = 0的情况?我通过使用do{}while(--len)循环样式而不是来保存2B while(len--){}
彼得·科德斯

4
说到解释,越详细越好。
丹尼斯

3
@cat:不,我没有发现asm痛苦。我不会花时间写关于asm /性能问题的Stackoverflow答案,如果我这样做的话,也不会更新x86标签wiki:P如果您想知道为什么代码运行得慢还是快,您必须查看并理解asm。一旦执行了一段时间,您就会开始看到编译器何时可以编写更快的代码...最终,您开始考虑编译器在编写代码时如何编译代码。有时优化代码大小而不是性能是一个有趣的变化。
彼得·科德斯

8

MATL,22字节

tsQwYsQsh16W15-\l8Mh*s

输入可以是数字数组或相应的ASCII字符串。

在线尝试!

说明

t       % Take array or string as input. Duplicate
sQ      % Sum all its values, and add 1
wYsQs   % Swap. Cumulative sum, add 1, sum
h       % Concatenate horizontally
16W     % 2^16: gives 65536
15-     % Subtract 15: gives 65521
\       % Element-wise modulo operation
l       % Push 1
8M      % Push 65536 again
h       % Concatenate horizontally: gives array [1, 65535]
*s      % Element-wise multiplication and sum. Display

7

其实36个位元组

;Σu@;╗lR`╜HΣu`MΣk`:65521@%`M1#84ⁿ@q*

在线尝试!

说明:

;Σu@;╗lR`╜HΣu`MΣk`:65521@%`M1#84ⁿ@q*
;Σu                                   sum(input)+1
   @;╗lR                              push a copy of input to reg0, push range(1, len(input)+1)
        `╜HΣu`M                       map over range: sum(head(reg0,n))+1
               Σk                     sum, combine lower and upper into a list
                 `:65521@%`M          modulo each by 65521
                            1#84ⁿ@q*  dot product with [1,4**8]

7

Java,84个字节

long a(int[]i){long a=1,b=0;for(int p:i)b=(b+(a=(a+p)%(p=65521)))%p;return b<<16|a;}

如果始终认为Java解决方案是完整的可编译代码,请告诉我。

不打高尔夫球

long a(int[] i) {
    long a = 1, b = 0;
    for (int p : i) b = (b + (a = (a + p) % (p = 65521))) % p;
    return b << 16 | a;
}

注意

您将不得不将输入转换Stringint[]int[]byte[]或短一个字节char[])。

输出量

String:     "Eagles are great!"
Byte Array: [69, 97, 103, 108, 101, 115, 32, 97, 114, 101, 32, 103, 114, 101, 97, 116, 33]
Checksum:   918816254
Expected:   918816254

String:     "Programming Puzzles & Code Golf"
Byte Array: [80, 114, 111, 103, 114, 97, 109, 109, 105, 110, 103, 32, 80, 117, 122, 122, 108, 101, 115, 32, 38, 32, 67, 111, 100, 101, 32, 71, 111, 108, 102]
Checksum:   3133147946
Expected:   3133147946

String:     "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
Byte Array: [126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126]
Checksum:   68095937
Expected:   68095937

String:     "?????????...?"
Byte Array: [63, 63, 63, 63, 63, 63, 63, 63, 63, ...,63]
Checksum:   2181038080
Expected:   2181038080

1
很好的答案,欢迎光临本站!而且,除非挑战明确指出它应该是完整程序,否则不完整和可编译的解决方案就可以了。这是一个完整的功能,因此很重要。
DJMcMayhem

6

Piet,120个法典 codelsize 1

使用codelsize 20:

codelsize 20

注释/如何运作?

  • 由于不可能使用数组或字符串作为输入,因此该程序通过将一系列整数(表示ascii字符)作为输入来工作。我最初考虑使用字符输入,但一直在努力寻找一种很好的终止解决方案,因此现在当输入小于1的任何数字时终止。最初它只是终止的负值,但是在编写程序后我不得不更改初始化,所以现在我不能满足要求的值2,只能是1(跟踪图像上的26/45)。但这并不重要,因为根据挑战规则,仅允许可打印的ASCII字符。

  • 在重新进入循环过程中苦苦挣扎了很长时间,尽管最后我发现了相当优雅的解决方案。否pointer或没有switch操作,只有解释器碰壁,直到它转换回绿色编码解码器以读取输入(跟踪图像上的43-> 44)。

  • 通过首先复制输入,加1,然后检查它是否大于1,来实现循环终止。如果是,则触发编解码器选择器,并在较低的路径上继续执行。如果不是,程序将继续向左继续(明亮的黄色代码,跟踪图像上的31/50)。

  • 支持的输入大小取决于解释器实现,尽管可以使用正确的解释器来支持任意大的输入(例如,使用BigInteger内部解释器的Java解释器)

  • 刚才看到的设置包括一个不必要的DUPCC(在跟踪图像7-> 8-> 9)。不知道那是怎么回事。这实际上是一个noop,它将代码选择器切换16次,这不会导致更改。

Npiet跟踪图像

设置和第一个循环:

开始跟踪

循环终止,输出和退出:

尾迹

产出

如果我只包含一个输出,请原谅我,输入它很长时间:^)

String: "Eagles are great!"

PS B:\Marvin\Desktop\Piet> .\npiet.exe adler32.png
? 69
? 97
? 103
? 108
? 101
? 115
? 32
? 97
? 114
? 101
? 32
? 103
? 114
? 101
? 97
? 116
? 33
? -1
918816254

[65,-1]的Npiet跟踪

trace: step 0  (0,0/r,l nR -> 1,0/r,l dR):
action: push, value 4
trace: stack (1 values): 4

trace: step 1  (1,0/r,l dR -> 2,0/r,l dB):
action: duplicate
trace: stack (2 values): 4 4

trace: step 2  (2,0/r,l dB -> 3,0/r,l nM):
action: multiply
trace: stack (1 values): 16

trace: step 3  (3,0/r,l nM -> 4,0/r,l nC):
action: duplicate
trace: stack (2 values): 16 16

trace: step 4  (4,0/r,l nC -> 5,0/r,l nY):
action: duplicate
trace: stack (3 values): 16 16 16

trace: step 5  (5,0/r,l nY -> 6,0/r,l nM):
action: duplicate
trace: stack (4 values): 16 16 16 16

trace: step 6  (6,0/r,l nM -> 7,0/r,l nC):
action: duplicate
trace: stack (5 values): 16 16 16 16 16

trace: step 7  (7,0/r,l nC -> 8,0/r,l nY):
action: duplicate
trace: stack (6 values): 16 16 16 16 16 16

trace: step 8  (8,0/r,l nY -> 9,0/r,l lB):
action: switch
trace: stack (5 values): 16 16 16 16 16
trace: stack (5 values): 16 16 16 16 16

trace: step 9  (9,0/r,l lB -> 10,0/r,l dM):
action: multiply
trace: stack (4 values): 256 16 16 16

trace: step 10  (10,0/r,l dM -> 11,0/r,l nR):
action: multiply
trace: stack (3 values): 4096 16 16

trace: step 11  (11,0/r,l nR -> 12,0/r,l lY):
action: multiply
trace: stack (2 values): 65536 16

trace: step 12  (12,0/r,l lY -> 13,0/r,l lM):
action: duplicate
trace: stack (3 values): 65536 65536 16

trace: step 13  (13,0/r,l lM -> 14,0/r,l nM):
action: push, value 3
trace: stack (4 values): 3 65536 65536 16

trace: step 14  (14,0/r,l nM -> 15,0/r,l dM):
action: push, value 2
trace: stack (5 values): 2 3 65536 65536 16

trace: step 15  (15,0/r,l dM -> 16,0/r,l lC):
action: roll
trace: stack (3 values): 16 65536 65536

trace: step 16  (16,0/r,l lC -> 17,0/r,l nB):
action: sub
trace: stack (2 values): 65520 65536

trace: step 17  (17,0/r,l nB -> 18,0/r,l dB):
action: push, value 1
trace: stack (3 values): 1 65520 65536

trace: step 18  (18,0/r,l dB -> 19,0/r,l dM):
action: add
trace: stack (2 values): 65521 65536

trace: step 19  (19,0/r,l dM -> 19,1/d,r dC):
action: duplicate
trace: stack (3 values): 65521 65521 65536

trace: step 20  (19,1/d,r dC -> 18,1/l,l lC):
action: push, value 1
trace: stack (4 values): 1 65521 65521 65536

trace: step 21  (18,1/l,l lC -> 17,1/l,l nC):
action: push, value 1
trace: stack (5 values): 1 1 65521 65521 65536

trace: step 22  (17,1/l,l nC -> 16,1/l,l dB):
action: sub
trace: stack (4 values): 0 65521 65521 65536

trace: step 23  (16,1/l,l dB -> 15,1/l,l lB):
action: push, value 1
trace: stack (5 values): 1 0 65521 65521 65536

trace: step 24  (15,1/l,l lB -> 13,2/l,l dG):
action: in(number)
? 65
trace: stack (6 values): 65 1 0 65521 65521 65536

trace: step 25  (13,2/l,l dG -> 12,2/l,l dR):
action: duplicate
trace: stack (7 values): 65 65 1 0 65521 65521 65536

trace: step 26  (12,2/l,l dR -> 11,2/l,l lR):
action: push, value 1
trace: stack (8 values): 1 65 65 1 0 65521 65521 65536

trace: step 27  (11,2/l,l lR -> 10,2/l,l lY):
action: add
trace: stack (7 values): 66 65 1 0 65521 65521 65536

trace: step 28  (10,2/l,l lY -> 9,2/l,l nY):
action: push, value 1
trace: stack (8 values): 1 66 65 1 0 65521 65521 65536

trace: step 29  (9,2/l,l nY -> 8,1/l,r nB):
action: greater
trace: stack (7 values): 1 65 1 0 65521 65521 65536

trace: step 30  (8,1/l,r nB -> 7,1/l,r lY):
action: switch
trace: stack (6 values): 65 1 0 65521 65521 65536
trace: stack (6 values): 65 1 0 65521 65521 65536

trace: step 31  (7,1/l,l lY -> 6,2/l,l nY):
action: push, value 2
trace: stack (7 values): 2 65 1 0 65521 65521 65536

trace: step 32  (6,2/l,l nY -> 5,3/l,l dB):
action: pointer
trace: stack (6 values): 65 1 0 65521 65521 65536

trace: step 33  (5,3/r,l dB -> 7,4/r,l dM):
action: add
trace: stack (5 values): 66 0 65521 65521 65536

trace: step 34  (7,4/r,l dM -> 8,4/r,l dC):
action: duplicate
trace: stack (6 values): 66 66 0 65521 65521 65536

trace: step 35  (8,4/r,l dC -> 9,3/r,l lC):
action: push, value 3
trace: stack (7 values): 3 66 66 0 65521 65521 65536

trace: step 36  (9,3/r,l lC -> 10,3/r,l nC):
action: push, value 2
trace: stack (8 values): 2 3 66 66 0 65521 65521 65536

trace: step 37  (10,3/r,l nC -> 11,3/r,l dY):
action: roll
trace: stack (6 values): 0 66 66 65521 65521 65536

trace: step 38  (11,3/r,l dY -> 12,3/r,l dG):
action: add
trace: stack (5 values): 66 66 65521 65521 65536

trace: step 39  (12,3/r,l dG -> 13,3/r,l lG):
action: push, value 2
trace: stack (6 values): 2 66 66 65521 65521 65536

trace: step 40  (13,3/r,l lG -> 14,3/r,l nG):
action: push, value 1
trace: stack (7 values): 1 2 66 66 65521 65521 65536

trace: step 41  (14,3/r,l nG -> 15,3/r,l dR):
action: roll
trace: stack (5 values): 66 66 65521 65521 65536
trace: white cell(s) crossed - continuing with no command at 17,3...

trace: step 42  (15,3/r,l dR -> 17,3/r,l lB):

trace: step 43  (17,3/r,l lB -> 13,2/l,l dG):
action: in(number)
? -1
trace: stack (6 values): -1 66 66 65521 65521 65536

trace: step 44  (13,2/l,l dG -> 12,2/l,l dR):
action: duplicate
trace: stack (7 values): -1 -1 66 66 65521 65521 65536

trace: step 45  (12,2/l,l dR -> 11,2/l,l lR):
action: push, value 1
trace: stack (8 values): 1 -1 -1 66 66 65521 65521 65536

trace: step 46  (11,2/l,l lR -> 10,2/l,l lY):
action: add
trace: stack (7 values): 0 -1 66 66 65521 65521 65536

trace: step 47  (10,2/l,l lY -> 9,2/l,l nY):
action: push, value 1
trace: stack (8 values): 1 0 -1 66 66 65521 65521 65536

trace: step 48  (9,2/l,l nY -> 8,1/l,r nB):
action: greater
trace: stack (7 values): 0 -1 66 66 65521 65521 65536

trace: step 49  (8,1/l,r nB -> 7,1/l,r lY):
action: switch
trace: stack (6 values): -1 66 66 65521 65521 65536
trace: stack (6 values): -1 66 66 65521 65521 65536

trace: step 50  (7,1/l,r lY -> 6,1/l,r dY):
action: pop
trace: stack (5 values): 66 66 65521 65521 65536

trace: step 51  (6,1/l,r dY -> 4,1/l,r lY):
action: push, value 3
trace: stack (6 values): 3 66 66 65521 65521 65536

trace: step 52  (4,1/l,r lY -> 3,1/l,r nY):
action: push, value 2
trace: stack (7 values): 2 3 66 66 65521 65521 65536

trace: step 53  (3,1/l,r nY -> 2,1/l,r nM):
action: duplicate
trace: stack (8 values): 2 2 3 66 66 65521 65521 65536

trace: step 54  (2,1/l,r nM -> 1,1/l,r dG):
action: pointer
trace: stack (7 values): 2 3 66 66 65521 65521 65536

trace: step 55  (1,1/r,r dG -> 2,2/r,r lR):
action: roll
trace: stack (5 values): 65521 66 66 65521 65536

trace: step 56  (2,2/r,r lR -> 2,3/d,l nR):
action: push, value 1
trace: stack (6 values): 1 65521 66 66 65521 65536

trace: step 57  (2,3/d,l nR -> 2,4/d,l lC):
action: switch
trace: stack (5 values): 65521 66 66 65521 65536
trace: stack (5 values): 65521 66 66 65521 65536

trace: step 58  (2,4/d,r lC -> 2,5/d,r nM):
action: mod
trace: stack (4 values): 66 66 65521 65536

trace: step 59  (2,5/d,r nM -> 4,5/r,r dM):
action: push, value 3
trace: stack (5 values): 3 66 66 65521 65536

trace: step 60  (4,5/r,r dM -> 6,5/r,r lM):
action: push, value 2
trace: stack (6 values): 2 3 66 66 65521 65536

trace: step 61  (6,5/r,r lM -> 7,5/r,r nC):
action: roll
trace: stack (4 values): 65521 66 66 65536

trace: step 62  (7,5/r,r nC -> 8,5/r,r dM):
action: mod
trace: stack (3 values): 66 66 65536

trace: step 63  (8,5/r,r dM -> 11,5/r,r lM):
action: push, value 3
trace: stack (4 values): 3 66 66 65536

trace: step 64  (11,5/r,r lM -> 12,5/r,r nM):
action: push, value 1
trace: stack (5 values): 1 3 66 66 65536

trace: step 65  (12,5/r,r nM -> 13,5/r,r dC):
action: roll
trace: stack (3 values): 66 65536 66

trace: step 66  (13,5/r,r dC -> 14,5/r,r nB):
action: multiply
trace: stack (2 values): 4325376 66

trace: step 67  (14,5/r,r nB -> 15,5/r,r nM):
action: add
trace: stack (1 values): 4325442

trace: step 68  (15,5/r,r nM -> 16,5/r,r dB):
action: out(number)
4325442
trace: stack is empty
trace: white cell(s) crossed - continuing with no command at 19,5...

trace: step 69  (16,5/r,r dB -> 19,5/r,r nM):

5

C89,70字节

h,l,m=65521;A(char*B){h=0;l=1;while(*B)h+=l+=*B++;return h%m<<16|l%m;}

要测试(用编译gcc -std=c89 -lm golf.c):

#include <stdio.h>
int main(int argc, char** argv) {
    printf("%u\n", A("Eagles are great!"));
    printf("%u\n", A("Programming Puzzles & Code Golf"));
    printf("%u\n", A("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"));
    return 0;
}

那是zlib来源的样子吗?嗯...

1
此实现为我的x86 asm版本提供了一个很好的起点。
彼得·科德斯

可以使用for而不是保存1个字节whilefor(h=0,l=1;*B;)h+=l+=*B++;
ninjalj '16

5

迷宫37 36 32 31字节

}?"{655:}21:}%=}){%{{36*+!
:++)

在线尝试!

输入为整数列表。程序以错误终止(其错误消息发送到STDERR)。

说明

迷宫底漆:

  • 迷宫有两堆任意精度的整数,即mainaux(iliary),它们最初都填充了(隐式)无限数量的零。
  • 源代码类似于一个迷宫,在该迷宫中,指令指针(IP)可以(甚至在拐角处)跟随走廊。代码从阅读顺序的第一个有效字符开始,即在这种情况下从左上角开始。当IP到达任何形式的联结时(即,除了它来自的联结单元外,还有几个相邻的单元),它将根据主堆栈的顶部选择一个方向。基本规则是:负数时左转,零时继续前进,正数时右转。如果由于存在隔离墙而无法实现其中之一,那么IP将采取相反的方向。当遇到死胡同时,IP也将转过来。
  • 通过将主堆栈的顶部乘以10,然后加上数字来处理数字。要开始新的数字,您可以使用来将其推为零_

尽管代码以4x2“房间”开头,但实际上是两个单独的2 x 2循环被挤压在一起。由于堆栈值的缘故,IP恰好一次只停留一个循环。

因此,代码以2x2(顺时针)循环开始,该循环在计算前缀和时读取输入:

}   Move last prefix sum over to aux.
?   Read an integer from STDIN or push 0 on EOF, which exits the loop.
+   Add current value to prefix sum.
:   Duplicate this prefix sum.

现在,我们已经在aux堆栈上获得了所有前缀和,以及所有值的和的副本以及main上的0 EOF 的副本。这样,我们进入另一个2x2(顺时针)循环,该循环将所有前缀总和求和。HIGH

"   No-op. Does nothing.
{   Pull one prefix sum over from aux. When we're done, this fetches a 0,
    which exits the loop.
)   Increment prefix sum.
+   Add it to HIGH.

现在,主堆栈具有LOW - 1HIGH和0,除了我们还没有取模。代码的其余部分是完全线性的:

655      Turn the zero into 655.
:}       Make a copy and shift it over to aux.
21       Turn the copy on main into 65521.
:}       Make a copy and shift it over to aux.
%        Take HIGH mod 65521.
=        Swap HIGH with the other copy of 65521 on aux.
}){      Move 65521 back to aux, increment LOW-1 to LOW, 
         move 65521 back to main.
%        Take LOW mod 65521.
{        Move HIGH back to main.
{        Move the other copy of 655 back to main.
36       Turn it into 65536.
*        Multiply HIGH by that.
+        Add it to LOW.
!        Print it.

IP现在陷入僵局并转身。的+*基本上是空操作,由于在堆叠底部的零。在36现在变成顶部主要63,但两人{{拉从两个零辅助在它的上面。然后%尝试除以零,这将终止程序。

请注意,迷宫使用任意精度的整数,因此将模数推迟到总和的末尾不会引起整数溢出的问题。


5

Python 2中,60 58个字节

H=h=65521
l=1
for n in input():l+=n;h+=l
print h%H<<16|l%H

一个非常简单的方法。这是一个完整的程序,它通过STDIN获取整数列表,例如[72, 105, 33]

(感谢@xnor提供了惊人的别名/初始化技巧)


2
您可以在别名6521时H=h=65521进行初始化h。–
xnor

4

J,30个字节

+/(+65536&*)&(65521|+/)&:>:+/\

这可能可以用另一列火车来浓缩。

用法

在此处x $ y创建带有的x副本的列表y

   f =: +/(+65536&*)&(65521|+/)&:>:+/\
   f 69 97 103 108 101 115 32 97 114 101 32 103 114 101 97 116 33
918816254
   f 80 114 111 103 114 97 109 109 105 110 103 32 80 117 122 122 108 101 115 32 38 32 67 111 100 101 32 71 111 108 102
3133147946
   f (32 $ 126)
68095937
   f (1040 $ 63)
2181038080
   f (4096 $ 255)
2170679522

说明

+/(+65536&*)&(65521|+/)&:>:+/\
f (           g           ) h     Monad train (f g h) y = (f y) g (h y)
+/                                Sum the input list
                           +/\    Sum each prefix of the input, forms a list
x     f   &   g   &:   h    y     Composed verbs, makes (g (h x)) f (g (h y))
                         >:       Increment the sum and increment each prefix sum
               (m f g) y          Hook, makes m f (g y)
                    +/            Sum the prefix sums
              65521|              Take the sum and prefix total mod 65521
    (f g) y                       Hook again
    65536&*                       Multiply the prefix total by 65536
                                  This is a bonded verb, it will only multiply
                                  using a fixed value now
   +                              Add the sum and scaled prefix total

4

八度,52 50字节

@LuisMendo节省了2个字节

@(B)mod([sum(S=cumsum(B)+1),S(end)],65521)*[4^8;1]

将整数数组作为输入。

low是取自high的最后一个元素(求和之前),而不是显式计算总和,从而节省了总计... 1个字节

样品在亚乙基酮上运行。


@LuisMendo Ooh,我忘了+B。我猜输入规范确实说可以接受整数,所以也许我会这样做。
烧杯

3

CJam,30个 29字节

q~{1$+}*]:)_W>]1fb65521f%2G#b

输入为整数列表。

在这里测试。

说明

q~       e# Read and evaluate input.
{        e# Fold this block over the list, computing prefix sums.
  1$+    e#   Copy the last prefix and add the current element.
}*
]        e# Wrap the prefix sums in an array.
:)       e# Increment each. This will sum to HIGH.
_W>      e# Copy the list and truncate to only the last element, i.e.
         e# the sum of the entire input plus 1. This is LOW.
]        e# Wrap both of those lists in an array.
1fb      e# Sum each, by treating it as base 1 digits.
65521f%  e# Take each modulo 65521.
2G#b     e# Treat the list as base 65536 digits, computing 65536*HIGH + LOW.

3

Perl 6,60个字节

{(.sum+1)%65521+65536*((sum(1,*+.shift...->{!$_})-1)%65521)}

说明:

{
  # $_ is the implicit parameter for this lambda because this block doesn't have
  # an explicit parameter, and @_ isn't seen inside of it.
  # ( @_ takes precedence over $_ when it is seen by the compiler )

  # .sum is short for $_.sum
  ( .sum + 1 ) % 65521 + 65536
  *
  (
    (
      sum(

        # generate a sequence:

        1,         # starting with 1
        * + .shift # lambda that adds previous result (*) with $_.shift
        ...        # generate until:
        -> { !$_ } # $_ is empty

        # ^ I used a pointy block with zero parameters
        # so that the block doesn't have an implicit parameter
        # like the surrounding block

        # this is so that $_ refers to the outer $_

      ) - 1        # remove starting value
    ) % 65521
  )
}

测试:

#! /usr/bin/env perl6
use v6.c;
use Test;

# give the lambda a name
my &Adler32 = {(.sum+1)%65521+65536*((sum(1,*+.shift...->{!$_})-1)%65521)}

my @tests = (
  (  918816254,  'Eagles are great!'),
  ( 3133147946,  'Programming Puzzles & Code Golf'),
  (   68095937,  '~' x 32,     "'~' x 32"),
  ( 2181038080,  63 xx 1040,   "'?' x 1040"),
);

plan +@tests;

for @tests -> ($checksum, $input, $gist? ) {
  my @array := do given $input {
    when Str { .encode.Array }
    default { .Array }
  }

  is Adler32(@array), $checksum, $gist // $input.perl
}
1..4
ok 1 - "Eagles are great!"
ok 2 - "Programming Puzzles \& Code Golf"
ok 3 - '~' x 32
ok 4 - '?' x 1040

3

Python 3(79字节)

基于R. Kap的解决方案。

lambda w,E=65521:(1+sum(w))%E+(sum(1+sum(w[:i+1])for i in range(len(w)))%E<<16)

我用一个移位代替了乘法,并删除了一对括号。

因为我无法发表评论,所以我做了一个新的答案。


3

方案,195字节

(define(a b)(+(let L((b b)(s 1))(if(=(length b)0)s(L(cdr b)(modulo(+ s(car b))65521))))(* 65536(let H((b b)(s 1)(t 0))(if(=(length b)0)t(let((S(+ s(car b))))(H(cdr b)S(modulo(+ t S)65521))))))))

如果不是所有这些括号...


3

Haskell,54个 50字节

m=(`mod`65521).sum
g x=m(-1:scanl(+)1x)*4^8+m(1:x)

用法示例:g [69,97,103,108,101,115,32,97,114,101,32,103,114,101,97,116,33]-> 918816254

scanl将起始值(-> 1)包含在列表(-> [1,1+b1,1+b1+b2,..])中,因此,通过sum将off 设置为off 1,可以通过-1在求和之前添加到列表中来固定它。

编辑:感谢@xnor 4个字节。


看起来您可以将汇总提取到mm=(`mod`65521).sum g x=m(-1:scanl(+)1x)*4^8+m(1:x)。固定总和可能是比前置更好的方法。
xnor

3

JavaScript(ES7),52 50字节

a=>a.map(b=>h+=l+=b,h=0,l=1)&&l%65521+h%65521*4**8

ES6占用51个字节(用65536替换4 ** 8)。如果要使用字符串版本,则为69个字节:

s=>[...s].map(c=>h+=l+=c.charCodeAt(),h=0,l=1)&&l%65521+h%65521*65536

编辑:由于@ user81655,节省了2个字节。


3

接受ARM Thumb-2函数uint8_t[]:40字节(对于非标准ABI和,为36B int[]

特点:无延迟模,所以任意大小的输入都可以。实际上并不使用除法指令,因此它并不慢。(错误,至少不是出于这个原因:P)

遵循不太严格的规则可以节省成本:

  • -2B(如果我们在使用寄存器之前不必保存寄存器)。
  • -2B,用于要求调用者将字节解包到uint32_t[]数组中。

因此,最佳情况是36B。

// uint8_t *buf in r0,  uint32_t len in r1
00000000 <adler32arm_golf2>:
   0:   b570            push    {r4, r5, r6, lr} //
   2:   2201            movs    r2, #1          // low
   4:   2300            movs    r3, #0          // high
   6:   f64f 75f1       movw    r5, #65521      ; 0xfff1 = m
0000000a <adler32arm_golf2.byteloop>:
   a:   f810 4b01       ldrb.w  r4, [r0], #1    // post-increment byte-load
   e:   4422            add     r2, r4          // low += *B
  10:   4413            add     r3, r2          // high += low
  12:   42aa            cmp     r2, r5          // subtract if needed instead of deferred modulo
  14:   bf28            it      cs
  16:   1b52            subcs   r2, r2, r5
  18:   42ab            cmp     r3, r5
  1a:   bf28            it      cs              // Predication in thumb mode is still possible, but takes a separate instruction
  1c:   1b5b            subcs   r3, r3, r5
  1e:   3901            subs    r1, #1          // while(--len)
  20:   d1f3            bne.n   a <.byteloop2>
  22:   eac2 4003       pkhbt   r0, r2, r3, lsl #16   // other options are the same size: ORR or ADD.
  26:   bd70            pop     {r4, r5, r6, pc}  // ARM can return by popping the return address (from lr) into the pc; nifty
00000028 <adler32arm_end_golf2>:

0x28 = 40个字节


笔记:

而不是log%m最后,我们if(low>=m) low-=m在循环内进行。如果我们先做低然后高,我们知道两者都不可能超过2*m,因此模只是减法或减法的问题。在Thumb2模式下,A cmp和谓词sub仅为6B。 在Thumb2模式下,标准用法%是8B:

UDIV R2, R0, R1         // R2 <- R0 / R1
MLS  R0, R1, R2, R0     // R0 <- R0 - (R1 * R2 )

隐式长度的adler(char *)版本与显式length的代码大小相同adler(uint8_t[], uint32_t len)。我们可以使用任何一种2B指令为循环退出条件设置标志。

隐式长度版本的优点是可以正确处理空字符串,而不是尝试循环2 ^ 32次。


汇编/编译:

arm-linux-gnueabi-as --gen-debug -mimplicit-it=always -mfloat-abi=soft -mthumb adler32-arm.S

要么

arm-linux-gnueabi-g++ -Wa,-mimplicit-it=always -g -static -std=gnu++14 -Wall -Wextra -Os -march=armv6t2 -mthumb -mfloat-abi=soft test-adler32.cpp -fverbose-asm adler32-arm.S -o test-adler32
qemu-arm ./test-adler32

没有-static,在其下运​​行的进程qemu-arm找不到它的动态链接器。(是的,我安装ARM交叉devel的设置只是为了这个答案,因为我认为我的预测-减想法是整齐的。)在amd64的Ubuntu,安装gcc-arm-linux-gnueabig++-arm-linux-gnueabi。我发现gdb-arm-none-eabi几乎无法连接到qemu-arm -g port

评论来源:

// There's no directive to enable implicit-it=always

// gcc uses compiler uses these in its output
.syntax unified
.arch armv8-a
.fpu softvfp

.thumb      @ aka .code 16

.p2align 4
.globl adler32arm_golf    @ put this label on the one we want to test

.thumb_func
adler32arm_golf:
adler32arm_golf2:   @ (uint8_t buf[], uint32_t len)
        @ r0 = buf
        @ r1 = len
        push    {r4, r5, r6, lr}   @ even number of regs keeps the stack aligned.  Good style? since there's no code-size saving

        movs    r2, #1          @ r2: low
        movs    r3, #0          @ r3: high
                                @ r4 = tmp for loading bytes
        movw    r5, #65521      @ r5: modulo constant

adler32arm_golf2.byteloop2:
        ldrb    r4, [r0], #1    @ *(buf++) post-increment addressing.  4B encoding
        @ldrb    r4, [r0, r1]   @ 2B encoding, but unless we make the caller pass us buf+len and -len, it needs extra code somewhere else
        @ldmia   r0!, {r4}      @ int[] version:  r4 = [r0]; r0+=4;  post-increment addressing.  2B encoding.

        add     r2, r2, r4      @ low += tmp
        add     r3, r3, r2      @ high += low;   // I think it's safe to do this before the modulo range-reduction for low, but it would certainly work to put it after.

        cmp     r2, r5
        subhs   r2, r5          @ if(low>=m) low-=m;   @ 6B total for %.  predicated insns require an IT instruction in thumb2

        cmp     r3, r5
        subhs   r3, r5          @ if(high>=m) high-=m;  // equivalent to high %= m.

        @sub    r1, #1          @ 4B encoding: sub.w to not set flags with immediate
        subs    r1, #1          @ len-- and set flags.  2B encoding
        @cmp    r4, #0          @ null-termination check. 2B encoding
        bne     adler32arm_golf2.byteloop2

@        udiv    r0, r2, r5            @ normal way to do one of the modulos
@        mls     r2, r5, r0, r2         @ r2 = low % m.  8B total for %

        PKHBT   r0, r2, r3, lsl #16     @ 4B   r0 = [ high%m <<16  |   low%m  ]
        @orr     r0, r0, r4, lsl #16    @ 4B
        @orr     r0, r0, r4             @ 4B
        @add     r0, r2, r3, lsl #16    @ 4B
        @add     r0, r0, r4             @ 2B
        pop     {r4, r5, r6, pc}        @ ARM can return by popping the return address (saved from lr) into pc.  Nifty
adler32arm_end_golf2:

test-adler32.cpp具有与main()我的x86-64答案相同的测试用例,但以这种方式开始:

#include <stdint.h>
uint32_t adler32_simple(const uint8_t *B) {
  const uint32_t m=65521;

  uint32_t h=0, l=1;
  do {
    l += *B++;        // Borrowed from orlp's answer, as a simple reference implementation
    h += l;
    l %= m; h %= m;   // with non-deferred modulo if this is uncommented
  } while(*B);

  return h%m<<16|l%m;
}


#include <stdio.h>
//#include <zlib.h>
#include <string.h>
#include <assert.h>
#include <string>   // useful for the memset-style constructors that repeat a character n times


extern "C" {
    unsigned golfed_adler32_amd64(int /*dummy1*/, const char *buf, int /*dummy2*/, unsigned len);
    unsigned adler32arm_golf(const char *buf, unsigned len);
}
#ifdef __amd64__
#define golfed_adler32(buf, len)   golfed_adler32_amd64(1234, buf, 1234, len)
#elif  __arm__
#define golfed_adler32(buf, len)   adler32arm_golf(buf, len)
#else
#error "no architecture"
#endif

static void test_adler(const char *str)
{
    unsigned len = strlen(str);
//    unsigned zlib = zlib_adler(len, str);
    unsigned reference = adler32_simple((const uint8_t*)str);
    unsigned golfed = golfed_adler32(str, len);

    printf("%s: c:%u asm:%u\n", str, reference, golfed);
    assert(reference == golfed);
}

// main() to call test_adler() unchanged from my amd64 answer, except that the comments about length limits don't apply

3

x86 16位机器码功能:使用自定义调用约定的32个字节

寄存器中的Arg,而不保留bp(和sp)以外的reg。

在16位代码中,我们在dx:ax寄存器对中返回32位值。这意味着我们不必花费任何指令融合highlow进入eax。(这也将节省32和64位代码中的字节,但是我们只能证明以16位代码将这项工作分流给调用者是合理的。)

在github上评论了源代码和测试驱动程序(适用于x86 16、32和64位以及ARM)。

### const char *buf in SI,  uint16_t len in CX
## returns in dx:ax
## also clobbers bx and di.
00000100 <adler32_x16_v6>:
 100:   31 c0                   xor    ax,ax         # set up for lods
 102:   99                      cwd                  # dx= high=0
 103:   bf 01 00                mov    di,0x1        # di= low=0
 106:   bb f1 ff                mov    bx,0xfff1     # bx= m
00000109 <adler32_x16_v6.byteloop>:
 109:   ac                      lods
 10a:   01 c7                   add    di,ax         # low+=buf[i]. modulo-reduce on carry, or on low>=m
 10c:   72 04                   jc     112 <adler32_x16_v6.carry_low>
 10e:   39 df                   cmp    di,bx
 110:   72 02                   jb     114 <adler32_x16_v6.low_mod_m_done>
00000112 <adler32_x16_v6.carry_low>:
 112:   29 df                   sub    di,bx
00000114 <adler32_x16_v6.low_mod_m_done>:
 114:   01 fa                   add    dx,di         # high+=low
 116:   0f 92 d0                setb   al            # store the carry to set up a 32bit dividend.
 119:   92                      xchg   dx,ax
 11a:   f7 f3                   div    bx            # high (including carry) %= m, in dx.  ax=0 or 1 (so we're set for lods next iteration)                                                         
 11c:   e2 eb                   loop   109 <adler32_x16_v6.byteloop>
 11e:   97                      xchg   di,ax         # 
 11f:   c3                      ret    
00000120 <adler32_x16_v6_end>:

0x120-0x100 = 32字节

通过为32位模式组装相同的代码进行了测试,因此我可以使用C编译的C调用它(带有包装函数)-m32。对我来说,16位模式有点有趣,而DOS系统调用却没有。除了loop和以外,所有指令都有明确的操作数,lodsb因此在32位模式下的汇编使用操作数大小的前缀。相同的指令,不同的编码。但是lodsb在32位模式下将使用[esi],因此此测试版本适用于32位指针(因为我们不执行任何地址运算或指针增量/比较)。

没有错配。如果不匹配,我的测试工具会打印一条消息。

$ yasm -felf32 -Worphan-labels -gdwarf2 adler32-x86-16.asm -o adler32-x86-16+32.o &&
   g++ -DTEST_16BIT -m32 -std=gnu++11 -O1 -g -Wall -Wextra -o test-adler32-x16  adler32-x86-16+32.o  test-adler32.cpp -lz &&
   ./test-adler32-x16
Eagles are great! (len=17): zlib:0x36c405fe  c:0x36c405fe golfed:0x36c405fe
Programming Puzzles & Code Golf (len=31): zlib:0xbac00b2a  c:0xbac00b2a golfed:0xbac00b2a
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ (len=32): zlib:0x040f0fc1  c:0x040f0fc1 golfed:0x040f0fc1
?????????????????????????????????????????????????? (len=1040): zlib:0x82000000  c:0x82000000 golfed:0x82000000
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ (len=4096): zlib:0xb169e06a  c:0xb169e06a golfed:0xb169e06a
(0xFF repeating) (len=4096): zlib:0x8161f0e2  c:0x8161f0e2 golfed:0x8161f0e2
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ (len=5837): zlib:0x5d2a398c  c:0x5d2a398c golfed:0x5d2a398c
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ (len=5838): zlib:0x97343a0a  c:0x97343a0a golfed:0x97343a0a
(0xFF repeating) (len=9999): zlib:0xcae9ea2c  c:0xcae9ea2c golfed:0xcae9ea2c
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ (len=65535): zlib:0x33bc06e5  c:0x33bc06e5 golfed:0x33bc06e5

使用16位寄存器,我们不能将模减少推迟到循环之后。有16位和其他操作数的大小之间的有趣的差异: m = 655210xFFF1)是一半以上65536减法m的进位上保持低于2 * m的值,就算high=0xFFF0 + 0xFFF0。循环之后,将使用比较和减法(而不是)来解决问题div

我想出了一种新颖的技术,可以在产生进位的加法之后对寄存器进行模减少。相反归零输入的上半部分为div,使用setc dl创建一个32位的红利持有未截断的附加结果(dh已经是零)。(div做32b / 16b => 16bit除法。)

setcc(3字节)是386引入的。要在286或更早版本上运行,最好的方法是使用未记录的salc指令(从进位设置AL)。它是的一字节操作码sbb al,al,因此我们可以在执行(仍然需要)之前使用salc/ 。没有,则有一个4B序列:/ 。我们不能使用3B / ,因为它将模拟而不是。neg alxchg ax, dxsalcsbb dx,dxneg dxsbb dx,dxinc dxsetncsetc


我尝试使用32位操作数大小而不是处理进位,但这不仅仅是add需要操作数大小前缀的指令。设置常量等的指令也需要操作数大小的前缀,因此最终并不是最小的。



2

Perl 5,43个字节

42个字节,加上1 -aE代替-e

输入为十进制整数,以空格分隔。

map$h+=$.+=$_,@F;say$.%65521+$h%65521*4**8

我对Sp3000表示敬意,并从中获得了有关此答案的想法。

怎么运行的:

  1. 由于-a,所以$. 从1开始,@F是输入数组。$h从0开始。$_用作map数组的每个元素的占位符。
  2. map$h+=$.+=$_,@F表示对于其中的每个元素,@F将其添加到中$.,然后添加$.到中$h
  3. 然后,我们进行模块化算术运算$.%65521+$h%65521*4**8(即($. % 65521) + ( ($h % 65521) * (4**8) )say(打印)结果)。

1

因子,112个 109 103字节

现在,这是问题中算法的字面翻译...现在,我知道了,实际上是正确的。

[ [ sum 1 + ] [ [ dup length [1,b] reverse v. ] [ length ] bi + ] bi [ 65521 mod ] bi@ 16 shift bitor ]

取消高尔夫:

: adler-32 ( seq -- n )
  [ sum 1 + ] 
  [ 
    [ dup length [1,b] reverse v. ] 
    [ length ] bi + 
  ] bi 
  [ 65521 mod ] bi@ 
  16 shift bitor 
  ;

期望任何数字序列或字符串(虽然在技术上并不相同,但差别不大)。

我不知道在给定的使用32位字长编译的Factor版本的给定限制下将如何执行,但是在我的6GB 64位2.2GHz机器上:

IN: scratchpad 1040 63 <array>

--- Data stack:
{ 63 63 63 63 63 63 63 63 63 63 63 63 63 63 ~1026 more~ }
IN: scratchpad [ adler-32 ] time
Running time: 7.326900000000001e-05 seconds

--- Data stack:
2181038080
IN: scratchpad 10,000 63 <array> 

--- Data stack:
2181038080
{ 63 63 63 63 63 63 63 63 63 63 63 63 63 63 ~9986 more~ }
IN: scratchpad [ adler-32 ] time
Running time: 0.000531669 seconds

1

Ruby,91个字节

->s{b=s.bytes;z=i=b.size
b.inject(1,:+)%65521+b.map{|e|e*(1+i-=1)}.inject(z,:+)%65521*4**8}

1

Clojure,109个字节

基于@Mark Adler的解决方案

(fn f[s](->> s(reduce #(mapv + %(repeat %2)[0(first %)])[1 0])(map #(rem % 65521))(map *[1 65536])(apply +)))

不打高尔夫球

(fn f [s]
  (->> s
       (reduce #(mapv + % (repeat %2) [0 (first %)]) [1 0])
       (map #(rem % 65521))
       (map * [1 65536])
       (apply +)))

用法

=> (def f (fn f[s](->> s(reduce #(mapv + %(repeat %2)[0(first %)])[1 0])(map #(rem % 65521))(map *[1 65536])(apply +))))
=> (f [69 97 103 108 101 115 32 97 114 101 32 103 114 101 97 116 33])
918816254
=> (f [80 114 111 103 114 97 109 109 105 110 103 32 80 117 122 122 108 101 115 32 38 32 67 111 100 101 32 71 111 108 102])
3133147946
=> (f (repeat 32 126))
68095937
=> (f (repeat 1040 63))
2181038080
=> (f (repeat 4096 255))
2170679522

1

Javascript(打过​​130个字符)

不打高尔夫球

function a(b)
{
    c=1
    for(i=0;i<b.length;i++)
    {
        c+=b[i]
    }
    d=c%65521
    f=""
    e=0
    k=""
    for(j=0;j<b.length;j++)
    {
        k+= "+"+b[j]
        f+= "(1"+k+")"
        e= ((eval(f)))
        if(j!=b.length-1){f+="+"}
    }
    g=e%65521
    h=d+65536*g
    console.log(h)
}

打高尔夫球

a=b=>{for(c=1,k=f="",y=b.length,i=0;i<y;i++)c+=x=b[i],f+="(1"+(k+="+"+x)+")",i<y-1&&(f+="+");return z=65521,c%z+65536*(eval(f)%z)}

粘贴到Developers Console中,然后为其提供字节数组EG:

[69, 97, 103, 108, 101, 115, 32, 97, 114, 101, 32, 103, 114, 101, 97, 116, 33]

它将校验和返回到控制台


1

TMP,55字节

3a1.3b0.1;4+a>T8%a>xFFF14+b>a8%b>xFFF11~5<b>164|b>a2$b$

可以在以下位置找到Lua中的实施:http : //preview.ccode.gq/projects/TMP.lua


1
欢迎来到编程难题和Code Golf!这种语言是否满足我们对编程语言的定义

@cat我相信它可以,但是我不确定它是否真的支持“元组”?
brianush1年

BrainFuck也没有,所以您可能还不错。如果它已经完成,可以找到质数,并且可以完成任何其他语言可以(并且可以)完成的基本工作,那么它将起作用:) CSS本身不是一种编程语言,也不是HTML,而是CSS3 + HTML是图灵完整的,可以找到素数。

因此,可以在CodeGolf中使用吗?
brianush1年

我想是的-我既不知道TMP也不知道Lua,所以对这段代码的解释会很有帮助(并且将成为一个很好的答案)。:D

1

Python 3.5,82个字节:

-1字节多亏尼尔

-1字节感谢mathmandan

-4个字节感谢Dennis

lambda w:((1+sum(w))%65521)+4**8*(sum(1+sum(w[:i+1])for i in range(len(w)))%65521)

匿名lambda函数。接受一个字节数组,将整个算法应用于该数组,然后输出结果。已经成功地为所有测试用例工作。您可以通过为其分配一个变量来调用它,然后像调用普通函数一样调用该变量。如果您使用的是外壳,则应在没有打印功能的情况下输出。但是,如果不是这样,则必须将函数调用包装在函数中print()才能实际看到输出。

在线尝试!(爱迪生)


(E+15)实际上比的字节长65536
尼尔

@Neil感谢您的提示。现在已修复。
R. Kap

@ Sp3000那么?他们添加一些字节会很重要,但是我添加任何字节这一事实是可以接受的。
R. Kap

4**865536。短一个字节。
mathmandan '16

您可以通过将生成器放在括号之间并从0迭代到len(w)来节省4个字节。利用运算符优先级可以节省另外6个字节。
丹尼斯

1

裂变,324字节

          /   M
       R_MZ  |S
      D ]    |S
 /?V?\} {}/  |S /    \
R{/A  Z$[/   |S/     {\
  } J{\      |S      ;_
 \^  /       |S   R'~++Y++~'L
 /    /      |S       }Y;
 \  \        ;^/
 /  /         +\+ R'~++A++~'L
 \  <Z________________/
    ;\X       //
              \Y/
               *

公平警告,我测试过的唯一实现是我自己将语言移植到F#。它不是打高尔夫球的,主要是因为我发现自己的主要常数沿着底部冷却时,可以进行几次长跑,所以我可能会回来调整它。

它是如何工作的?

  • R'~++Y++~'L模块融合一个256常数并将其向下发射,从而将反应堆的质量倍增器直接设置在其下方。
  • 所述R'~++A++~'A块熔断器另一256和发射它朝向反应器的上方,所述颗粒成的两个质量倍数,其裂变65536每个质量,发射他们左右(其中右粒子立即被终止破坏)。
  • 剩下的粒子撞击另一个反应器并发生裂变,分裂成两个质量相等的粒子,分别向上和向下移动。
  • 向上传播的2的幂的粒子经过净零质量操纵,向左反射,然后设置聚变反应堆的质量倍数。这个反应堆将是我们如何乘以H块的方法。
  • 向下移动的粒子向左反射并在长期内减少质量,最终达到质量65521(我们的大质数)。
  • 旋转镜(Z运行结束时,)导致粒子复制质子,将质子复制回右侧,最终确定裂变反应堆(^)的储存质量。这就是我们将模运算符应用于H块的方式。
  • 第二份副本被反射回去,并在其中执行裂变反应堆的类似功能(<我们将对L块使用)。
  • 现在我们的常数就位了,我们进入左上方的诡计来读取我们的输入并生成我们的两个列表。老实说,我忘记了它们是如何工作的,但是对于空字符串,我不得不减慢H块求和粒子,这解释了|S “冷却塔”。
  • \Y/ 融合L块(通过左通道进入)和H块(通过右通道进入),然后将它们猛撞到终结器中,后者将退出代码设置为融合质量。

除非我在某个地方犯了错误,否则这似乎不适用于官方解释器(link)。我在哪里可以将您的端口转到F#?
丹尼斯

@Dennis我想弄清楚该错误是否在我的身上,但是我也无法使解释器正常工作。我将查看是否可以正常运行,然后根据需要更新答案。
安德鲁·库恩斯

@Dennis似乎在线解释器不处理错误代码中止*,这就是我返回输出的方式。我明天看看是否可以找到另一个解释器来验证输出。
安德鲁·库恩斯
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.