Code Golf: Congettura di Collatz


86

Ispirato da http://xkcd.com/710/, ecco un codice da golf per questo.

La sfida

Dato un numero intero positivo maggiore di 0, stampa la sequenza di grandine per quel numero.

La sequenza della grandine

Vedi Wikipedia per maggiori dettagli ..

  • Se il numero è pari, dividerlo per due.
  • Se il numero è dispari, triplicalo e aggiungine uno.

Ripeti l'operazione con il numero prodotto fino a raggiungere 1. (se continua dopo 1, andrà in un ciclo infinito di 1 -> 4 -> 2 -> 1...)

A volte il codice è il modo migliore per spiegare, quindi eccone alcuni da Wikipedia

function collatz(n)
  show n
  if n > 1
    if n is odd
      call collatz(3n + 1)
    else
      call collatz(n / 2)

Questo codice funziona, ma sto aggiungendo una sfida in più. Il programma non deve essere vulnerabile agli stack overflow . Quindi deve usare l'iterazione o la ricorsione della coda.

Inoltre, punti bonus per se può calcolare grandi numeri e la lingua non lo ha già implementato. (o se reimplementi il ​​supporto per numeri grandi utilizzando numeri interi di lunghezza fissa)

Scenario di prova

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

Number: 3
Results: 3 -> 10 -> 5 -> 16 -> 8 -> 4 -> 2 -> 1

Inoltre, il codice golf deve includere l'input e l'output dell'utente completo.



20
non deve essere vulnerabile agli stack overflow : allora non avresti dovuto postarlo qui! ;)
Felix Kling

51
I miei amici hanno smesso di chiamarmi, significa che ho risolto il problema?
Martin

18
Sei su SO, ma una volta avevi degli amici? ... com'è stato?
Pop il

5
La risposta dell'assemblatore è interessante, ma è un po ' anti-code-golf selezionare la risposta più lunga !
John La Rooy

Risposte:


129

x86 assembly, 1337 caratteri

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

27
x86 asm e 1337 caratteri. Piango di gioia.
ZoogieZork

10
Mi piace l'uso (ab) di lea per 3n + 1.
wowest

Grazie per non averlo utilizzato int 23h.
Mike D.

come faccio a compilare in linux non vedo l'ora di provarlo
hidroto

64

Befunge

&>:.:1-|
  >3*^ @
  |%2: <
 v>2/>+

2
Dovresti leggerlo in 2D. <> ^ v sono frecce che cambiano la direzione del "contatore del programma". | e _ sono condizionali che vanno su / giù o sinistra / destra a seconda che il valore in pila sia vero o falso. L'intera "arena del codice" avvolge dall'alto in basso e da sinistra a destra.
SF.

E solo 35 caratteri inclusi gli spazi bianchi! Non è affatto male!
Potatoswatter

6
Sei sicuro che non sia Perl?
ijw

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

TESTD UNDR JUSTIN J. MEZA 'S INTERPRETR . KTHXBYE!


51

Python - 95 64 51 46 caratteri

Ovviamente non produce un overflow dello stack.

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

4
Potresti voler specificare Python 2.x. IIRC, Python 3.x inputnon esegue un file eval.
Mike D.

5
Questo non soddisfa i requisiti - non stampa il primo numero
Ben Lings

7
perché è accettato? non è il più corto e non stampa il primo numero
Claudiu

1
Immagino che input () faccia eco ai caratteri
digitati

17
È possibile stampare il primo numero per un costo di soli 2 byte utilizzandon=input()*2
John La Rooy

23

Perl

Ho deciso di essere un po 'anticoncorrenziale e di mostrare come codifichereste normalmente tale problema in Perl.
Alla fine c'è anche una voce da golf con codice di 46 caratteri (totale).

Questi primi tre esempi iniziano tutti con questa intestazione.

#! /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( $_ );
}
  • Semplice versione ricorsiva

    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 );
        }
      }
    }
    
  • Semplice versione iterativa

    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;
      }
    }
    
  • Versione iterativa ottimizzata

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

Ora vi mostrerò come fareste a fare quell'ultimo esempio con una versione di Perl precedente alla v5.10.0

#! /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];
  }
}

Prova delle prestazioni

Prima di tutto l'IO sarà sempre la parte lenta. Quindi, se li hai effettivamente confrontati così come sono, dovresti ottenere circa la stessa velocità da ciascuno.

Per testarli quindi, ho aperto un handle di file in /dev/null( $null) e modificato ogni say $nper invece leggere say {$null} $n. Questo per ridurre la dipendenza da 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;
  ...
}

Dopo averlo eseguito 10 volte, ecco un output di esempio rappresentativo:

            Tasso iterativo ricorsivo ottimizzato
Ricorsivo 1715 / s - -27% -46%
Iterativo 2336 / s 36% - -27%
Ottimizzato 3187 / s 86% 36% -

Infine, una vera voce da golf in codice:

perl -nlE'say;say$_=$_%2?3*$_+1:$_/2while$_>1'

46 caratteri in totale

Se non è necessario stampare il valore iniziale, è possibile rimuovere altri 5 caratteri.

perl -nE'say$_=$_%2?3*$_+1:$_/2while$_>1'

41 caratteri in totale
31 caratteri per la porzione di codice effettiva, ma il codice non funzionerà senza l' -ninterruttore. Quindi includo l'intero esempio nel mio conteggio.


La tua versione ottimizzata non lo è.
Motti

@ Motti Questi esempi dipendono molto da IO. Dopo averli testati più volte, la versione ottimizzata mantiene sempre un vantaggio significativo.
Brad Gilbert

@ Brad, quando esegui Collatz su un numero, l'ottimizzazione è una pessimizzazione poiché nessun numero dovrebbe apparire più di una volta (a meno che la congettura non sia sbagliata). Il motivo per cui stai vedendo un miglioramento è che stai eseguendo molti numeri (come nel problema di Eulero), infatti ho scritto un post sul blog di recente su questo lanzkron.wordpress.com/2010/01/18/…
Motti

2
@Motti questa è l'ottimizzazione di cui parlavo. Inoltre, in Perl $i + 1è sempre aggiunta (risposta al post del blog). Anche l'utilizzo Sub::Call::Recurè anche un'ottimizzazione. Altrimenti lo userei @_=$n;goto &Collatz. (È più lento del 10-20% se si state @nextpassa amy @next
Brad Gilbert

3
Credo che gli standard per il conteggio dei colpi di perl golf non contino i colpi obbligatori per invocare l'interprete né le virgolette, ma contano uno per ogni bandiera accanto a E. Usando queste regole, le tue ultime voci contano rispettivamente 37 caratteri e 32 caratteri.
R. Martinho Fernandes

23

Haskell, 62 caratteri 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

Input dell'utente, output stampato, utilizza memoria e stack costanti, funziona con numeri interi arbitrariamente grandi.

Un'esecuzione di esempio di questo codice, dato un numero di 80 cifre di tutti gli '1 (!) Come input, è piuttosto divertente da guardare.


Versione originale, solo funzione:

Haskell 51 caratteri

f n=n:[[],f([n`div`2,3*n+1]!!(n`mod`2))]!!(1`mod`n)

Comunque, chi @ & ^ # ha bisogno dei condizionali?

(modifica: ero "intelligente" e usavo la correzione. Senza di essa, il codice è sceso a 54 caratteri. edit2: sceso a 51 fattorizzando f())


Dopo aver scritto il mio post su Miranda (che è fondamentalmente solo Haskell più vecchio), almeno in Miranda puoi ridurlo usando un solo punto esclamativo ciascuno - fn = n: [[], [f (n div 2), f (3 * n + 1)]! (n mod 2)]! (1 mod n) - Funziona :)
Derek H

Oh, sì, ti mancano input e output.
R. Martinho Fernandes

@ Martinho: Anch'io, ma grazie a una valutazione pigra, le tabelle sono anche molto più interessanti che in altre lingue.
Dario

1
Usando l'idea di jleedev: c 1=[1];c n=n:(c$div(nmod 2*(5*n+2)+n)2)- 41 caratteri, questo usa il fatto che questo è k * (3n + 1) + (1-k) * n / 2 dove k = n mod 2
sdcvvc

2
Ho cancellato l'altra mia voce e ho spostato il mio codice qui e ho incorporato ancora più idee da questi commenti. Aumentato a 76 caratteri, ma esegue input e output.
MtnViewMark

22

Golfscript: 20 caratteri

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

Questo è equivalente a

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;

2
"deve includere l'input e l'output completo dell'utente"
F'x

2
@FX, la sostituzione di 21con ~farà sì che il programma utilizzi un numero da stdin
John La Rooy

@gnibbler: Golfscript.rb è aggiornato? Ho (eval):1:in inizializzato ': metodo undefined leftparen' for nil:NilClass (NoMethodError)quando si sostituisce 21con ~.
kennytm

@KennyTM, Purtroppo GolfScript non può leggere lo stdin in modo interattivo, devi inserire qualcosa in stdin, comeecho 21 | ruby golfscript.rb collatz.gs
John La Rooy

19

bc 41 caratteri

Immagino che questo tipo di problemi sia ciò per cui è bcstato inventato:

for(n=read();n>1;){if(n%2)n=n*6+2;n/=2;n}

Test:

bc1 -q collatz.bc
21
64
32
16
8
4
2
1

Codice corretto:

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

bcgestisce numeri composti da un massimo di INT_MAXcifre

Modifica: l' articolo di Wikipedia menziona che questa congettura è stata controllata per tutti i valori fino a 20x2 58 (circa 5.76e18 ). Questo programma:

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

test 2 20.000 +1 (circa 3.98e6.020 ) in 68 secondi, 144.404 cicli.


Cambia "n! = 1" in "n> 1" per 54 caratteri.
Jerry Coffin

4
Ecco una riga di comando per generare numeri casuali di lunghezza arbitraria per questa voce (10000 cifre in questo caso): cat /dev/urandom | tr -dc '0-9' | head -c 10000 | bc collatz-conjecture.bc
indiv

3
@indiv - Ho dovuto testarlo :), ci sono voluti 3 minuti e 12 secondi per elaborare il numero di 10.000 cifre. Ho salvato l'output in un file, è lungo circa 1,2 GB, ma sì, è finito correttamente in 1. Punto perbc
Carlos Gutiérrez

16

Perl: 31 caratteri

perl -nE 'say$_=$_%2?$_*3+1:$_/2while$_>1'
#         123456789 123456789 123456789 1234567

Modificato per rimuovere 2 spazi non necessari.

Modificato per rimuovere 1 spazio non necessario.


Puoi rimuovere due spazi non necessari (dopo dire e dopo un po ')
sorpigal

Prova perl -E 'dì $ _ = 10; dì $ _ = $ _% 2? $ _ * 3 + 1: $ _ / 2 mentre $ _> 1'
sorpigal

Ho pensato che l'utente sarebbe stato a conoscenza del numero iniziale della sequenza ;-).
a'r

41
A volte, quando mi imbatto in testo codificato in base64, a volte lo scambio per codice sorgente Perl.
Martin,

21
@ Martin: non riesco a immaginare come lo faresti. Base64 è molto più leggibile.
Jerry Coffin

15

MS Excel, 35 caratteri

=IF(A1/2=ROUND(A1/2,0),A1/2,A1*3+1)

Tratto direttamente da Wikipedia :

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

È bastato copiare / incollare la formula 111 volte per ottenere il risultato per un numero iniziale di 1000.;)


16
Immagino sia troppo tardi per farmi notare che a questo serve il quadratino di riempimento, eh? ehow.com/how_2284668_use-fill-handle-microsoft-excel.html :)
Jordan Running

Questa è una caratteristica piuttosto utile che non sapevo nemmeno esistesse. Ho appena copiato la prima cella e quindi evidenziato tutte le altre celle e incollato una volta.
Lance McNearney,

Ho imparato a conoscere la pasta di area circa dieci anni dopo aver scoperto la maniglia di riempimento. figure.
Jimmy

14

C: 64 caratteri

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

Con supporto per interi grandi: 431 caratteri (necessari)

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

Nota : non rimuovere #include <stdlib.h>senza almeno prototipare malloc / realloc, poiché così facendo non sarà sicuro su piattaforme a 64 bit (il void * a 64 bit verrà convertito in int a 32 bit).

Questo non è stato ancora testato vigorosamente. Potrebbe anche essere necessario accorciarlo.


Versione precedente:

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

(rimossi 12 caratteri perché nessuno segue il formato di output ...: |)


12

Un'altra versione assembler. Questo non è limitato a numeri a 32 bit, può gestire numeri fino a 10 65534 sebbene il formato ".com" utilizzato da MS-DOS sia limitato a numeri di 80 cifre. Scritto per l'assembler A86 e richiede un box DOS Win-XP per funzionare. Assembla a 180 byte:

    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

10

dc - 24 caratteri 25 28

dc è un buon strumento per questa sequenza:

?[d5*2+d2%*+2/pd1<L]dsLx
dc -f collatz.dc
21
64
32
16
8
4
2
1

Anche 24 caratteri utilizzando la formula dalla voce Golfscript :

?[3*1+d2%5*1+/pd1<L]dsLx

57 caratteri per soddisfare le specifiche:

[Number: ]n?[Results: ]ndn[d5*2+d2%*+2/[ -> ]ndnd1<L]dsLx
dc -f collatz-spec.dc
Numero 3
Risultati: 3 -> 10 -> 5 -> 16 -> 8 -> 4 -> 2 -> 1

9

Schema: 72

(define(c n)(if(= n 1)`(1)(cons n(if(odd? n)(c(+(* n 3)1))(c(/ n 2))))))

Questo utilizza la ricorsione, ma le chiamate sono ricorsive di coda, quindi penso che saranno ottimizzate per l'iterazione. In alcuni test rapidi, non sono stato in grado di trovare un numero per il quale lo stack trabocca comunque. Solo per esempio:

(c 9876543219999999999000011234567898888777766665555444433332222 77777777777777777777777777777777777798797657657651234143375987342987 53987098123749825298309837432758098053923098374327580953923098577587587539539230985777587539

... funziona bene. [è tutto un numero - l'ho appena rotto per adattarlo allo schermo.]


8

Mathematica, 45 50 chars

c=NestWhileList[If[OddQ@#,3#+1,#/2]&,#,#>1&]&

Ho contato 58 caratteri. E puoi sostituire OddQ[#]con OddQ@#per salvare 1 carattere.
kennytm

2
50 caratteri:c[n_]:=NestWhileList[If[OddQ@#,3#+1,#/2]&,n,#>1&]
Michael Pilat

7

Ruby, 50 caratteri, nessun overflow dello stack

Fondamentalmente una copia diretta della soluzione Python di makapuf :

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

Ruby, 45 caratteri, traboccherà

Fondamentalmente uno strappo diretto del codice fornito nella domanda:

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

Che versione di Ruby è quella? Non vengo n.odd??definito. Inoltre, è vulnerabile agli stack overflow con grandi numeri.
Earlz

Interessante. Ho 1.8.7. L'aggiunta di uno spazio tra i punti interrogativi dovrebbe risolverlo. E hai ragione riguardo allo stack overflow. Modificherò la mia risposta per prenderne nota.
Jordan Running il

3
Puoi salvare quattro personaggi conp n=[n/2,n*3+1][n%2]
Wayne Conrad

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

50
Java: il linguaggio in cui devi usare BigIntegers solo per contare il numero di caratteri nel codice della soluzione.
Jared Updike

3
@ Jared Sono totalmente d'accordo che Java sia prolisso. Devi ammettere che la soluzione presentata a) soddisfa i requisiti b) è molto più lunga del necessario ec) gioca con il sistema di tipo java in modo piacevole
wowest

7

Python 45 Char

Ho rasato un carbonchio alla risposta di Makapuf.

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

uso molto intelligente dell'operatore ~. Ho dovuto cercarlo per vedere cosa faceva (cerco di evitare gli operatori binari in Python, quindi non li conosco molto bene).
Ponkadoodle

5

TI-BASIC

Non il più breve, ma un approccio nuovo. Certo rallentare notevolmente con sequenze di grandi dimensioni, ma non dovrebbe traboccare.

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

4

Haskell: 50

c 1=[1];c n=n:(c$if odd n then 3*n+1 else n`div`2)

Utilizzando l'idea di jkff: c 1=[1];c n=n:(c$[ndiv 2,3*n+1]!!(nmod 2)), 44 caratteri
sdcvvc

4

non la più corta, ma un'elegante soluzione di chiusura

(defn collatz [n]
 (print n "")
 (if (> n 1)
  (recur
   (if (odd? n)
    (inc (* 3 n))
    (/ n 2)))))

4

C #: 216 caratteri

using C=System.Console;class P{static void Main(){var p="start:";System.Action<object> o=C.Write;o(p);ulong i;while(ulong.TryParse(C.ReadLine(),out i)){o(i);while(i > 1){i=i%2==0?i/2:i*3+1;o(" -> "+i);}o("\n"+p);}}}

in forma lunga:

using C = System.Console;
class P
{
    static void Main()
    {
        var p = "start:"; 
        System.Action<object> o = C.Write; 
        o(p); 
        ulong i; 
        while (ulong.TryParse(C.ReadLine(), out i))
        {
            o(i); 
            while (i > 1)
            {
                i = i % 2 == 0 ? i / 2 : i * 3 + 1; 
                o(" -> " + i);
            } 
            o("\n" + p);
        }
    }
}

Nuova versione, accetta un numero come input fornito tramite la riga di comando, nessuna convalida dell'input. 173 154 caratteri.

using System;class P{static void Main(string[]a){Action<object>o=Console.Write;var i=ulong.Parse(a[0]);o(i);while(i>1){i=i%2==0?i/2:i*3+1;o(" -> "+i);}}}

in forma lunga:

using System;
class P
{
    static void Main(string[]a)
    {
        Action<object>o=Console.Write;
        var i=ulong.Parse(a[0]);
        o(i);
        while(i>1)
        {
            i=i%2==0?i/2:i*3+1;
            o(" -> "+i);
        }
    }
}

Sono in grado di radere alcuni personaggi strappando l'idea in questa risposta di usare un ciclo for piuttosto che un po '. 150 caratteri.

using System;class P{static void Main(string[]a){Action<object>o=Console.Write;for(var i=ulong.Parse(a[0]);i>1;i=i%2==0?i/2:i*3+1)o(i+" -> ");o(1);}}

Dovresti menzionare che il tuo codice accetta più di un input. Oppure toglilo e taglia via alcuni caratteri.
R. Martinho Fernandes

È possibile abbreviare Action <object> e possibilmente altro in Dynamic in C # 4.
Dykam

@Dykam: Ho appena controllato: non riesce con "errore CS0428: Impossibile convertire il gruppo di metodi" Scrivi "in un tipo non delegato" dinamico ". Intendevi invocare il metodo?".
R. Martinho Fernandes

Oh, naturalmente ... la conversione implicita in delegati ... richiede la denotazione del delegato.
Peccato

4

Ruby, 43 caratteri

bignum supportato, con suscettibilità allo stack overflow:

def c(n)p n;n%2>0?c(3*n+1):c(n/2)if n>1 end

... e 50 caratteri, supportati bignum, senza overflow dello stack:

def d(n)while n>1 do p n;n=n%2>0?3*n+1:n/2 end end

Complimenti alla Giordania. Non sapevo di "p" come sostituto di put.


4

nroff 1

Corri con nroff -U hail.g

.warn
.pl 1
.pso (printf "Enter a number: " 1>&2); read x; echo .nr x $x
.while \nx>1 \{\
.  ie \nx%2 .nr x \nx*3+1
.  el .nr x \nx/2
\nx
.\}

1. versione groff


2
Spaventoso! Tuttavia, almeno l'output dovrebbe essere formattato correttamente.
Jonathan Leffler

3
Ehi, eseguilo come groff -U hail.ge ottieni PostScript! :-)
DigitalRoss

4

Scala + Scalaz

import scalaz._
import Scalaz._
val collatz = 
   (_:Int).iterate[Stream](a=>Seq(a/2,3*a+1)(a%2)).takeWhile(1<) // This line: 61 chars

E in azione:

scala> collatz(7).toList
res15: List[Int] = List(7, 22, 11, 34, 17, 52, 26, 13, 40, 20, 10, 5, 16, 8, 4, 2)

Scala 2.8

val collatz = 
   Stream.iterate(_:Int)(a=>Seq(a/2,3*a+1)(a%2)).takeWhile(1<) :+ 1

Questo include anche il trailing 1.

scala> collatz(7)
res12: scala.collection.immutable.Stream[Int] = Stream(7, 22, 11, 34, 17, 52, 26, 13, 40, 20, 10, 5, 16, 8, 4, 2, 1)

Con quanto segue implicito

implicit def intToEven(i:Int) = new {
  def ~(even: Int=>Int, odd: Int=>Int) = { 
    if (i%2==0) { even(i) } else { odd(i) }
  }
}

questo può essere abbreviato in

val collatz = Stream.iterate(_:Int)(_~(_/2,3*_+1)).takeWhile(1<) :+ 1

Modifica: 58 caratteri (inclusi input e output, ma escluso il numero iniziale)

var n=readInt;while(n>1){n=Seq(n/2,n*3+1)(n%2);println(n)}

Potrebbe essere ridotto di 2 se non hai bisogno di nuove righe ...


3

F #, 90 caratteri

let c=Seq.unfold(function|n when n<=1->None|n when n%2=0->Some(n,n/2)|n->Some(n,(3*n)+1))

> c 21;;
val it : seq<int> = seq [21; 64; 32; 16; ...]

Oppure, se non utilizzi F # interattivo per visualizzare il risultato, 102 caratteri:

let c=Seq.unfold(function|n when n<=1->None|n when n%2=0->Some(n,n/2)|n->Some(n,(3*n)+1))>>printf"%A"

3

Common Lisp, 141 caratteri:

(defun c ()
  (format t"Number: ")
  (loop for n = (read) then (if(oddp n)(+ 1 n n n)(/ n 2))
     until (= n 1)
     do (format t"~d -> "n))
  (format t"1~%"))

Prova:

Number: 171
171 -> 514 -> 257 -> 772 -> 386 -> 193 -> 580 -> 290 -> 145 -> 436 ->
218 -> 109 -> 328 -> 164 -> 82 -> 41 -> 124 -> 62 -> 31 -> 94 -> 47 ->
142 -> 71 -> 214 -> 107 -> 322 -> 161 -> 484 -> 242 -> 121 -> 364 ->
182 -> 91 -> 274 -> 137 -> 412 -> 206 -> 103 -> 310 -> 155 -> 466 ->
233 -> 700 -> 350 -> 175 -> 526 -> 263 -> 790 -> 395 -> 1186 -> 593 ->
1780 -> 890 -> 445 -> 1336 -> 668 -> 334 -> 167 -> 502 -> 251 -> 754 ->
377 -> 1132 -> 566 -> 283 -> 850 -> 425 -> 1276 -> 638 -> 319 ->
958 -> 479 -> 1438 -> 719 -> 2158 -> 1079 -> 3238 -> 1619 -> 4858 ->
2429 -> 7288 -> 3644 -> 1822 -> 911 -> 2734 -> 1367 -> 4102 -> 2051 ->
6154 -> 3077 -> 9232 -> 4616 -> 2308 -> 1154 -> 577 -> 1732 -> 866 ->
433 -> 1300 -> 650 -> 325 -> 976 -> 488 -> 244 -> 122 -> 61 -> 184 ->
92 -> 46 -> 23 -> 70 -> 35 -> 106 -> 53 -> 160 -> 80 -> 40 -> 20 ->
10 -> 5 -> 16 -> 8 -> 4 -> 2 -> 1 

Quasi. Non c'è intestazione per la seconda riga. Avrei potuto rasare 3 caratteri ignorando la freccia, altri 3-4 elidendo spazi non necessari, ma sono contento di un multiplo di 3.
Vatine

3

Il programma di Jerry Coffin ha un numero intero in overflow, prova questo:

#include <iostream>

int main(unsigned long long i)
{
    int j = 0;
    for(  std::cin>>i; i>1; i = i&1? i*3+1:i/2, ++j)
        std::cout<<i<<" -> ";

    std::cout<<"\n"<<j << " iterations\n";
}

testato con

Il numero inferiore a 100 milioni con il tempo di arresto totale più lungo è 63.728.127, con 949 passaggi.

Il numero inferiore a 1 miliardo con il tempo di arresto totale più lungo è 670.617.279, con 986 passaggi.


Qualsiasi tipo di numero intero finito non può impedire l'overflow di numeri interi. Nemmeno unsigned long long.
kennytm

3

ruby, 43 anni, che potrebbe soddisfare i requisiti di I / O


Corri con ruby -n hail

n=$_.to_i
(n=n%2>0?n*3+1: n/2
p n)while n>1

3

C #: 659 caratteri con supporto BigInteger

using System.Linq;using C=System.Console;class Program{static void Main(){var v=C.ReadLine();C.Write(v);while(v!="1"){C.Write("->");if(v[v.Length-1]%2==0){v=v.Aggregate(new{s="",o=0},(r,c)=>new{s=r.s+(char)((c-48)/2+r.o+48),o=(c%2)*5}).s.TrimStart('0');}else{var q=v.Reverse().Aggregate(new{s="",o=0},(r, c)=>new{s=(char)((c-48)*3+r.o+(c*3+r.o>153?c*3+r.o>163?28:38:48))+r.s,o=c*3+r.o>153?c*3+r.o>163?2:1:0});var t=(q.o+q.s).TrimStart('0').Reverse();var x=t.First();q=t.Skip(1).Aggregate(new{s=x>56?(x-57).ToString():(x-47).ToString(),o=x>56?1:0},(r,c)=>new{s=(char)(c-48+r.o+(c+r.o>57?38:48))+r.s,o=c+r.o>57?1:0});v=(q.o+q.s).TrimStart('0');}C.Write(v);}}}

Ungolfed

using System.Linq;
using C = System.Console;
class Program
{
    static void Main()
    {
        var v = C.ReadLine();
        C.Write(v);
        while (v != "1")
        {
            C.Write("->");
            if (v[v.Length - 1] % 2 == 0)
            {
                v = v
                    .Aggregate(
                        new { s = "", o = 0 }, 
                        (r, c) => new { s = r.s + (char)((c - 48) / 2 + r.o + 48), o = (c % 2) * 5 })
                    .s.TrimStart('0');
            }
            else
            {
                var q = v
                    .Reverse()
                    .Aggregate(
                        new { s = "", o = 0 }, 
                        (r, c) => new { s = (char)((c - 48) * 3 + r.o + (c * 3 + r.o > 153 ? c * 3 + r.o > 163 ? 28 : 38 : 48)) + r.s, o = c * 3 + r.o > 153 ? c * 3 + r.o > 163 ? 2 : 1 : 0 });
                var t = (q.o + q.s)
                    .TrimStart('0')
                    .Reverse();
                var x = t.First();
                q = t
                    .Skip(1)
                    .Aggregate(
                        new { s = x > 56 ? (x - 57).ToString() : (x - 47).ToString(), o = x > 56 ? 1 : 0 }, 
                        (r, c) => new { s = (char)(c - 48 + r.o + (c + r.o > 57 ? 38 : 48)) + r.s, o = c + r.o > 57 ? 1 : 0 });
                v = (q.o + q.s)
                    .TrimStart('0');
            }
            C.Write(v);
        }
    }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.