Code Golf:Collat​​z猜想

Ear*_*rlz 86 language-agnostic code-golf collatz rosetta-stone

灵感来自http://xkcd.com/710/这里是一个高尔夫代码.

挑战

给定大于0的正整数,打印出该数字的冰雹序列.

冰雹序列

有关更多详细信息,请参阅Wikipedia

  • 如果数字是偶数,则除以2.
  • 如果数字是奇数,则将其加倍并添加一个.

用生成的数字重复此操作,直到达到1.(如果在1之后继续,它将进入无限循环1 -> 4 -> 2 -> 1...)

有时代码是最好的解释方式,所以这里有一些来自维基百科

function collatz(n)
  show n
  if n > 1
    if n is odd
      call collatz(3n + 1)
    else
      call collatz(n / 2)
Run Code Online (Sandbox Code Playgroud)

这段代码有效,但我正在增加额外的挑战.程序不能容易受到堆栈溢出的影响.所以它必须使用迭代或尾递归.

此外,如果它可以计算大数字并且语言尚未实现,则奖励积分.(或者如果使用固定长度的整数重新实现大数字支持)

测试用例

Number: 21
Results: 21 -> 64 -> 32 -> 16 -> 8 -> 4 -> 2 -> 1

Number: 3
Results: 3 -> 10 -> 5 -> 16 -> 8 -> 4 -> 2 -> 1
Run Code Online (Sandbox Code Playgroud)

此外,代码高尔夫必须包括完整的用户输入和输出.

Mar*_*tin 129

x86程序集,1337个字符

;
; To assemble and link this program, just run:
;
; >> $ nasm -f elf collatz.asm && gcc -o collatz collatz.o
;
; You can then enjoy its output by passing a number to it on the command line:
;
; >> $ ./collatz 123
; >> 123 --> 370 --> 185 --> 556 --> 278 --> 139 --> 418 --> 209 --> 628 --> 314
; >> --> 157 --> 472 --> 236 --> 118 --> 59 --> 178 --> 89 --> 268 --> 134 --> 67
; >> --> 202 --> 101 --> 304 --> 152 --> 76 --> 38 --> 19 --> 58 --> 29 --> 88
; >> --> 44 --> 22 --> 11 --> 34 --> 17 --> 52 --> 26 --> 13 --> 40 --> 20 --> 10
; >> --> 5 --> 16 --> 8 --> 4 --> 2 --> 1
; 
; There's even some error checking involved:
; >> $ ./collatz
; >> Usage: ./collatz NUMBER
;
section .text
global main
extern printf
extern atoi

main:

  cmp dword [esp+0x04], 2
  jne .usage

  mov ebx, [esp+0x08]
  push dword [ebx+0x04]
  call atoi
  add esp, 4

  cmp eax, 0
  je .usage

  mov ebx, eax
  push eax
  push msg

.loop:
  mov [esp+0x04], ebx
  call printf

  test ebx, 0x01
  jz .even

.odd:
  lea ebx, [1+ebx*2+ebx]
  jmp .loop

.even:

  shr ebx, 1
  cmp ebx, 1
  jne .loop

  push ebx
  push end
  call printf

  add esp, 16
  xor eax, eax
  ret

.usage:
  mov ebx, [esp+0x08]
  push dword [ebx+0x00]
  push usage
  call printf
  add esp, 8
  mov eax, 1
  ret

msg db "%d --> ", 0
end db "%d", 10, 0
usage db "Usage: %s NUMBER", 10, 0
Run Code Online (Sandbox Code Playgroud)

  • x86 asm*和*1337字符.我高兴地哭泣. (27认同)
  • 我喜欢(ab)使用lea for 3n + 1. (10认同)

Jos*_*Lee 64

Befunge

&>:.:1-|
  >3*^ @
  |%2: <
 v>2/>+
Run Code Online (Sandbox Code Playgroud)

  • 你确定它不是Perl吗? (6认同)
  • 这是什么" ? (3认同)
  • 您应该在2D中阅读此内容.<> ^ v是改变"程序计数器"漂移方向的箭头.| 和_是上/下或左/右的条件,具体取决于堆栈上的值是true还是false.整个"代码竞技场"绕过上下左右. (2认同)

lun*_*dov 52

LOLCODE:406 CHARAKTERZ

HAI
BTW COLLATZ SOUNDZ JUS LULZ

CAN HAS STDIO?

I HAS A NUMBAR
BTW, I WANTS UR NUMBAR
GIMMEH NUMBAR

VISIBLE NUMBAR

IM IN YR SEQUENZ
  MOD OF NUMBAR AN 2
  BOTH SAEM IT AN 0, O RLY?
    YA RLY, NUMBAR R QUOSHUNT OF NUMBAR AN 2
    NO WAI, NUMBAR R SUM OF PRODUKT OF NUMBAR AN 3 AN 1
  OIC
  VISIBLE NUMBAR
  DIFFRINT 2 AN SMALLR OF 2 AN NUMBAR, O RLY?
    YA RLY, GTFO
  OIC
IM OUTTA YR SEQUENZ

KTHXBYE
Run Code Online (Sandbox Code Playgroud)

JESTIN J. MEZA'S INTERPRETR的 TESTD UNDR .KTHXBYE!


mak*_*puf 51

Python - 95 64 51 46 char

显然不会产生堆栈溢出.

n=input()
while n>1:n=(n/2,n*3+1)[n%2];print n
Run Code Online (Sandbox Code Playgroud)

  • 您可以使用`n = input()*2`打印第一个数字,只需2个字节 (17认同)
  • 为什么接受这个?它不是最短的,也不会打印第一个数字 (7认同)
  • 这不符合要求 - 它不会打印第一个数字 (5认同)
  • 您可能想要指定Python 2.x. IIRC,Python 3.x`input`没有做'eval`. (4认同)

jkf*_*kff 23

Haskell中,62个字符63 76 83,86,97,137

c 1=[1]
c n=n:c(div(n`mod`2*(5*n+2)+n)2)
main=readLn>>=print.c
Run Code Online (Sandbox Code Playgroud)

用户输入,打印输出,使用常量内存和堆栈,使用任意大整数.

这个代码的示例运行,给出一个80位数的全'1(!)作为输入,看起来非常有趣.


原创,仅限功能版本:

Haskell 51个字符

f n=n:[[],f([n`div`2,3*n+1]!!(n`mod`2))]!!(1`mod`n)
Run Code Online (Sandbox Code Playgroud)

无论如何,@&^#谁需要条件?

(编辑:我是"聪明"并使用修复.没有它,代码下降到54个字符.编辑2:通过分解掉到51 f())

  • 我删除了我的其他条目,并在此处移动了我的代码,并整合了这些评论中的更多想法.增加到76个字符,但输入和输出. (2认同)

Bra*_*ert 23

Perl的

我决定有点反竞争,并展示你通常如何在Perl中编写这样的问题.
最后还有一个46(总)char码 - 高尔夫球场.

前三个示例都是从这个标题开始的.

#! /usr/bin/env perl
use Modern::Perl;
# which is the same as these three lines:
# use 5.10.0;
# use strict;
# use warnings;

while( <> ){
  chomp;
  last unless $_;
  Collatz( $_ );
}
Run Code Online (Sandbox Code Playgroud)
  • 简单的递归版本

    use Sub::Call::Recur;
    sub Collatz{
      my( $n ) = @_;
      $n += 0; # ensure that it is numeric
      die 'invalid value' unless $n > 0;
      die 'Integer values only' unless $n == int $n;
      say $n;
      given( $n ){
        when( 1 ){}
        when( $_ % 2 != 0 ){ # odd
          recur( 3 * $n + 1 );
        }
        default{ # even
          recur( $n / 2 );
        }
      }
    }
    
    Run Code Online (Sandbox Code Playgroud)
  • 简单的迭代版本

    sub Collatz{
      my( $n ) = @_;
      $n += 0; # ensure that it is numeric
      die 'invalid value' unless $n > 0;
      die 'Integer values only' unless $n == int $n;
      say $n;
      while( $n > 1 ){
        if( $n % 2 ){ # odd
          $n = 3 * $n + 1;
        } else { #even
          $n = $n / 2;
        }
        say $n;
      }
    }
    
    Run Code Online (Sandbox Code Playgroud)
  • 优化的迭代版本

    sub Collatz{
      my( $n ) = @_;
      $n += 0; # ensure that it is numeric
      die 'invalid value' unless $n > 0;
      die 'Integer values only' unless $n == int $n;
      #
      state @next;
      $next[1] //= 0; # sets $next[1] to 0 if it is undefined
      #
      # fill out @next until we get to a value we've already worked on
      until( defined $next[$n] ){
        say $n;
        #
        if( $n % 2 ){ # odd
          $next[$n] = 3 * $n + 1;
        } else { # even
          $next[$n] = $n / 2;
        }
        #
        $n = $next[$n];
      }
      say $n;
      # finish running until we get to 1
      say $n while $n = $next[$n];
    }
    
    Run Code Online (Sandbox Code Playgroud)

现在,我将展示如何使用v5.10.0之前的Perl版本来完成最后一个示例

#! /usr/bin/env perl
use strict;
use warnings;

while( <> ){
  chomp;
  last unless $_;
  Collatz( $_ );
}
{
  my @next = (0,0); # essentially the same as a state variable
  sub Collatz{
    my( $n ) = @_;
    $n += 0; # ensure that it is numeric
    die 'invalid value' unless $n > 0;

    # fill out @next until we get to a value we've already worked on
    until( $n == 1 or defined $next[$n] ){
      print $n, "\n";

      if( $n % 2 ){ # odd
        $next[$n] = 3 * $n + 1;
      } else { # even
        $next[$n] = $n / 2;
      }
      $n = $next[$n];
    }
    print $n, "\n";

    # finish running until we get to 1
    print $n, "\n" while $n = $next[$n];
  }
}
Run Code Online (Sandbox Code Playgroud)

基准

首先,IO始终是缓慢的部分.因此,如果您实际对它们进行基准测试,那么您应该获得相同的速度.

为了测试这些,我打开了一个文件句柄/dev/null($null),并编辑每一个say $n而不是读取say {$null} $n.这是为了减少对IO的依赖.

#! /usr/bin/env perl
use Modern::Perl;
use autodie;

open our $null, '>', '/dev/null';

use Benchmark qw':all';

cmpthese( -10,
{
  Recursive => sub{ Collatz_r( 31 ) },
  Iterative => sub{ Collatz_i( 31 ) },
  Optimized => sub{ Collatz_o( 31 ) },
});

sub Collatz_r{
  ...
  say {$null} $n;
  ...
}
sub Collatz_i{
  ...
  say {$null} $n;
  ...
}
sub Collatz_o{
  ...
  say {$null} $n;
  ...
}
Run Code Online (Sandbox Code Playgroud)

运行10次之后,这是一个有代表性的示例输出:

            Rate Recursive Iterative Optimized
Recursive 1715/s        --      -27%      -46%
Iterative 2336/s       36%        --      -27%
Optimized 3187/s       86%       36%        --

最后,一个真正的代码高尔夫球场:

perl -nlE'say;say$_=$_%2?3*$_+1:$_/2while$_>1'
Run Code Online (Sandbox Code Playgroud)

共46个字符

如果您不需要打印起始值,则可以删除另外5个字符.

perl -nE'say$_=$_%2?3*$_+1:$_/2while$_>1'
Run Code Online (Sandbox Code Playgroud)

41个字符总共
31个字符用于实际代码部分,但如果没有-n开关,代码将无法工作.所以我把整个例子都包含在我的计数中.

  • 我认为perl高尔夫球击球计数标准不计算调用翻译器和引号的强制笔画,但是对于E旁边的每个标志都要计数一个.使用这些规则,你的最后一个条目分别计数37个字符和32个字符. (3认同)
  • @Motti这就是我所说的优化.另外,在Perl` $ i + 1`中**总是**加法(对博客条目的回复).使用`Sub :: Call :: Recur`也是一种优化.否则我会使用`@ _ = $ n; goto&Collat​​z`.(如果将`state @ next`更改为`my @ next`,速度会慢10-20% (2认同)

ken*_*ytm 22

Golfscript:20个字符

  ~{(}{3*).1&5*)/}/1+`
# 
# Usage: echo 21 | ruby golfscript.rb collatz.gs
Run Code Online (Sandbox Code Playgroud)

这相当于

stack<int> s;
s.push(21);
while (s.top() - 1) {
  int x = s.top();
  int numerator = x*3+1;
  int denominator = (numerator&1) * 5 + 1;
  s.push(numerator/denominator);
}
s.push(1);
return s;
Run Code Online (Sandbox Code Playgroud)

  • "必须包括完整的用户输入和输出" (2认同)
  • @FX:除了这是Golfscript接受输入的唯一方法. (2认同)
  • @FX,用`~`替换`21`将导致程序使用stdin中的数字 (2认同)

Car*_*rez 19

bc 41个字符

我猜这种问题是bc为此发明的:

for(n=read();n>1;){if(n%2)n=n*6+2;n/=2;n}
Run Code Online (Sandbox Code Playgroud)

测试:

bc1 -q collatz.bc
21
64
32
16
8
4
2
1
Run Code Online (Sandbox Code Playgroud)

正确的代码:

for(n=read();n>1;){if(n%2)n=n*3+1else n/=2;print n,"\n"}
Run Code Online (Sandbox Code Playgroud)

bc处理最多INT_MAX数字的数字

编辑:维基百科的文章中提到这个猜想已经为所有的值被检查高达20X2 58(近似5.76e18).这个程序:

c=0;for(n=2^20000+1;n>1;){if(n%2)n=n*6+2;n/=2;c+=1};n;c
Run Code Online (Sandbox Code Playgroud)

68秒内测试2 20,000 +1(aprox.3.98e6,020),144,404个周期.

  • 这是一个命令行,用于为此条目生成随机任意长度的数字(在这种情况下为10000位):`cat/dev/urandom | tr -dc'0-9'| 头-c 10000 | bc collat​​z-conjecture.bc` (4认同)
  • @indiv - 我必须测试它:),花了3分12秒来处理10,000位数字.我将输出保存到一个文件,大约1.2gb长,但是它确实在1中正确完成.指向`bc` (3认同)

a'r*_*a'r 16

Perl:31个字符

perl -nE 'say$_=$_%2?$_*3+1:$_/2while$_>1'
#         123456789 123456789 123456789 1234567
Run Code Online (Sandbox Code Playgroud)

编辑删除2个不必要的空格.

编辑删除1个不必要的空间.

  • 有时,当我遇到base64编码文本时,我有时会将其误认为是Perl源代码. (41认同)
  • @Martin:我无法想象你会怎么做.Base64**更具可读性. (21认同)

Lan*_*ney 15

MS Excel,35个字符

=IF(A1/2=ROUND(A1/2,0),A1/2,A1*3+1)
Run Code Online (Sandbox Code Playgroud)

直接来自维基百科:

In cell A1, place the starting number.
In cell A2 enter this formula =IF(A1/2=ROUND(A1/2,0),A1/2,A1*3+1) 
Drag and copy the formula down until 4, 2, 1
Run Code Online (Sandbox Code Playgroud)

它只需要复制/粘贴公式111次以获得起始编号为1000的结果.;)

  • 我想现在为时已晚,我指出这就是填充手柄的用途,对吧?http://www.ehow.com/how_2284668_use-fill-handle-microsoft-excel.html :) (16认同)

ken*_*ytm 14

C:64个字符

main(x){for(scanf("%d",&x);x>=printf("%d,",x);x=x&1?3*x+1:x/2);}
Run Code Online (Sandbox Code Playgroud)

有大整数支持:431(必要)字符

#include <stdlib.h>
#define B (w>=m?d=realloc(d,m=m+m):0)
#define S(a,b)t=a,a=b,b=t
main(m,w,i,t){char*d=malloc(m=9);for(w=0;(i=getchar()+2)/10==5;)
B,d[w++]=i%10;for(i=0;i<w/2;i++)S(d[i],d[w-i-1]);for(;;w++){
while(w&&!d[w-1])w--;for(i=w+1;i--;)putchar(i?d[i-1]+48:10);if(
w==1&&*d==1)break;if(*d&1){for(i=w;i--;)d[i]*=3;*d+=1;}else{
for(i=w;i-->1;)d[i-1]+=d[i]%2*10,d[i]/=2;*d/=2;}B,d[w]=0;for(i=0
;i<w;i++)d[i+1]+=d[i]/10,d[i]%=10;}}
Run Code Online (Sandbox Code Playgroud)

注意:不要#include <stdlib.h>在没有至少原型化malloc/realloc的情况下删除,因为这样做在64位平台上是不安全的(64位void*将转换为32位int).

这个尚未经过大力测试.它也可以使用一些缩短.


之前的版本:

main(x){for(scanf("%d",&x);printf("%d,",x),x-1;x=x&1?3*x+1:x/2);} // 66
Run Code Online (Sandbox Code Playgroud)

(删除了12个字符,因为没有人遵循输出格式...:|)


Ski*_*izz 12

另一个汇编版本.这个不限于32位数字,它可以处理多达10 65534的数字,尽管MS-DOS使用的".com"格式限制为80位数字.为A86汇编程序编写,需要运行Win-XP DOS框.汇编为180字节:

    mov ax,cs
    mov si,82h
    add ah,10h
    mov es,ax
    mov bh,0
    mov bl,byte ptr [80h]
    cmp bl,1
    jbe ret
    dec bl
    mov cx,bx
    dec bl
    xor di,di
 p1:lodsb
    sub al,'0'
    cmp al,10
    jae ret
    stosb
    loop p1
    xor bp,bp
    push es
    pop ds
 p2:cmp byte ptr ds:[bp],0
    jne p3
    inc bp
    jmp p2
    ret
 p3:lea si,[bp-1]
    cld
 p4:inc si
    mov dl,[si]
    add dl,'0'
    mov ah,2
    int 21h
    cmp si,bx
    jne p4
    cmp bx,bp
    jne p5
    cmp byte ptr [bx],1
    je ret
 p5:mov dl,'-'
    mov ah,2
    int 21h
    mov dl,'>'
    int 21h
    test byte ptr [bx],1
    jz p10
    ;odd
    mov si,bx
    mov di,si
    mov dx,3
    dec bp
    std
 p6:lodsb
    mul dl
    add al,dh
    aam
    mov dh,ah
    stosb
    cmp si,bp
    jnz p6
    or dh,dh
    jz p7
    mov al,dh
    stosb
    dec bp
 p7:mov si,bx
    mov di,si
 p8:lodsb
    inc al
    xor ah,ah
    aaa
    stosb
    or ah,ah
    jz p9
    cmp si,bp
    jne p8
    mov al,1
    stosb
    jmp p2
 p9:inc bp
    jmp p2
    p10:mov si,bp
    mov di,bp
    xor ax,ax
p11:lodsb
    test ah,1
    jz p12
    add al,10
p12:mov ah,al
    shr al,1
    cmp di,bx
    stosb
    jne p11
    jmp p2
Run Code Online (Sandbox Code Playgroud)


Car*_*rez 10

dc - 24 chars 25 28

dc 是这个序列的好工具:

?[d5*2+d2%*+2/pd1<L]dsLx
Run Code Online (Sandbox Code Playgroud)
dc -f collatz.dc
21
64
32
16
8
4
2
1

另外24个字符使用Golfscript条目中的公式:

?[3*1+d2%5*1+/pd1<L]dsLx
Run Code Online (Sandbox Code Playgroud)

符合规格的57个字符:

[Number: ]n?[Results: ]ndn[d5*2+d2%*+2/[ -> ]ndnd1<L]dsLx
Run Code Online (Sandbox Code Playgroud)
dc -f collatz-spec.dc
Number: 3
Results: 3 -> 10 -> 5 -> 16 -> 8 -> 4 -> 2 -> 1


Jer*_*fin 9

方案:72

(define(c n)(if(= n 1)`(1)(cons n(if(odd? n)(c(+(* n 3)1))(c(/ n 2))))))
Run Code Online (Sandbox Code Playgroud)

这使用递归,但调用是尾递归的,所以我认为它们将被优化为迭代.在一些快速测试中,我无法找到堆栈溢出的数字.仅举例如:

(c 9876543219999999999000011234567898888777766665555444433332222 7777777777777777777777777777777798797657657651234143375987342987 5398709812374982529830983743297432985230985739287023987532098579 058095873098753098370938753987)

......运行得很好.[这是所有一个数字 - 我刚刚打破它以适应屏幕.]


Pil*_*lsy 8

Mathematica,45岁 50 字符

c=NestWhileList[If[OddQ@#,3#+1,#/2]&,#,#>1&]&
Run Code Online (Sandbox Code Playgroud)

  • 50个字符:`c [n _]:= NestWhileList [如果[OddQ @#,3#+ 1,#/ 2]&,n,#> 1&]` (2认同)

Jor*_*ing 7

Ruby,50个字符,没有堆栈溢出

基本上直接扯掉了makapuf的Python解决方案:

def c(n)while n>1;n=n.odd?? n*3+1: n/2;p n end end
Run Code Online (Sandbox Code Playgroud)

Ruby,45个字符,将溢出

基本上是直接翻录问题中提供的代码:

def c(n)p n;n.odd?? c(3*n+1):c(n/2)if n>1 end
Run Code Online (Sandbox Code Playgroud)

  • 您可以使用`pn = [n/2,n*3 + 1] [n%2]`保存四个字符 (3认同)

wow*_*est 7

import java.math.BigInteger;
public class SortaJava {

    static final BigInteger THREE = new BigInteger("3");
    static final BigInteger TWO = new BigInteger("2");

    interface BiFunc<R, A, B> {
      R call(A a, B b);
    }

    interface Cons<A, B> {
      <R> R apply(BiFunc<R, A, B> func);
    }

    static class Collatz implements Cons<BigInteger, Collatz> {
      BigInteger value;
      public Collatz(BigInteger value) { this.value = value; }
      public <R> R apply(BiFunc<R, BigInteger, Collatz> func) {
        if(BigInteger.ONE.equals(value))
          return func.call(value, null);
        if(value.testBit(0))
          return func.call(value, new Collatz((value.multiply(THREE)).add(BigInteger.ONE)));
        return func.call(value, new Collatz(value.divide(TWO)));
      }
    }

    static class PrintAReturnB<A, B> implements BiFunc<B, A, B> {
      boolean first = true;
      public B call(A a, B b) {
        if(first)
          first = false;
        else
          System.out.print(" -> ");
        System.out.print(a);
        return b;
      }
    }

    public static void main(String[] args) {
      BiFunc<Collatz, BigInteger, Collatz> printer = new PrintAReturnB<BigInteger, Collatz>();
      Collatz collatz = new Collatz(new BigInteger(args[0]));
      while(collatz != null)
        collatz = collatz.apply(printer);
    }
}
Run Code Online (Sandbox Code Playgroud)

  • Java:您必须使用BigIntegers来计算解决方案代码中的字符数的语言. (50认同)
  • @Jared我完全同意Java很冗长.你必须承认所提出的解决方案a)符合要求b)比实际需要更长的时间c)以愉快的方式使用java类型系统 (3认同)

Gui*_*fay 7

Python 45 Char

刮掉了makapuf的回答.

n=input()
while~-n:n=(n/2,n*3+1)[n%2];print n
Run Code Online (Sandbox Code Playgroud)


小智 5

TI-BASIC

不是最短的,而是一种新颖的方法.某些大型序列会大幅减速,但不应该溢出.

PROGRAM:COLLATZ
:ClrHome
:Input X
:Lbl 1
:While X?1
:If X/2=int(X/2)
:Then
:Disp X/2?X
:Else
:Disp X*3+1?X
:End
:Goto 1
:End
Run Code Online (Sandbox Code Playgroud)