Perl
Decidi ser um pouco anticompetitivo e mostrar como você normalmente codificaria esse problema em Perl.
Há também uma entrada de código de golfe de 46 (total) char no final.
Todos esses três primeiros exemplos começam com este cabeçalho.
#! /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( $_ );
}
Versão recursiva simples
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 );
}
}
}
Versão iterativa simples
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;
}
}
Versão iterativa otimizada
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];
}
Agora vou mostrar como você faria esse último exemplo com uma versão do Perl anterior à 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];
}
}
Benchmark
Em primeiro lugar, o IO sempre será a parte lenta. Portanto, se você realmente os comparou como estão, deverá obter aproximadamente a mesma velocidade de cada um.
Para testá-los, abri um identificador de arquivo para /dev/null
( $null
) e editei todos say $n
para, em vez disso, ler say {$null} $n
. Isso é para reduzir a dependência de 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;
...
}
Depois de executá-lo 10 vezes, aqui está uma saída de amostra representativa:
Taxa recursiva iterativa otimizada
Recursivo 1715 / s - -27% -46%
Iterativo 2336 / s 36% - -27%
3187 / s otimizado 86% 36% -
Finalmente, uma entrada real de código de golfe:
perl -nlE'say;say$_=$_%2?3*$_+1:$_/2while$_>1'
46 caracteres no total
Se você não precisa imprimir o valor inicial, pode remover mais 5 caracteres.
perl -nE'say$_=$_%2?3*$_+1:$_/2while$_>1'
41 chars totalizam
31 chars para a parte do código real, mas o código não funcionará sem o -n
switch. Portanto, incluo o exemplo completo em minha contagem.