note
marioroy
<p>Hi [rjt],</p>
<p>Thank you for this challenge. This consumed so much of my time in a great way. The reason is partly due to, "What if possible for many CPU cores?" But first made attempts for fast using 1 core. Below are the 3 progressive solutions, each one running faster.</p>
<p>Update: Added results from two machines.</p>
<p><b>Laurent's demonstration plus updates:</b></p>
<code>
#!/usr/bin/env perl
use strict;
use warnings;
my $size = shift || 1e6;
$size = 1e6 if $size < 1e6; # minimum
$size = 1e9 if $size > 1e9; # maximum
##
# Laurent's demonstration + updates
# https://www.perlmonks.org/?node_id=11115520
# https://www.perlmonks.org/?node_id=11115540
#
# Parallel solution
# https://www.perlmonks.org/?node_id=11115544
##
my @cache = (0, 1, 2);
my @seqs;
sub collatz_seq {
my $size = shift;
my ($n, $steps);
for my $input (2..$size) {
$n = $input, $steps = 0;
while ($n != 1) {
$steps += $cache[$n], last if defined $cache[$n];
$n % 2 ? ( $steps += 2, $n = (3 * $n + 1) >> 1 )
: ( $steps += 1, $n = $n >> 1 );
}
$cache[$input] = $steps if $input < $size;
push @seqs, [ $input, $steps ] if $steps > 400;
}
}
collatz_seq($size);
@seqs = ( sort { $b->[1] <=> $a->[1]} @seqs )[ 0..19 ];
printf "Collatz(%5d) has sequence length of %3d steps\n", @$_
for @seqs;
</code>
<p><b>iM71's C++ demonstration converted to Perl plus updates:</b></p>
<code>
#!/usr/bin/env perl
use strict;
use warnings;
my $size = shift || 1e6;
$size = 1e6 if $size < 1e6; # minimum
$size = 1e9 if $size > 1e9; # maximum
##
# iM71's demonstration + applied T(x) notation and compression
# https://stackoverflow.com/a/55361008
# https://www.youtube.com/watch?v=t1I9uHF9X5Y (1 min into video)
#
# Parallel solution
# https://www.perlmonks.org/?node_id=11115780
##
my @cache = (0, 1, 2);
my @seqs;
sub collatz_seq {
my $size = shift;
my ($n, $steps);
for my $input (2..$size) {
$n = $input, $steps = 0;
$n % 2 ? ( $steps += 2, $n = (3 * $n + 1) >> 1 )
: ( $steps += 1, $n = $n >> 1 )
while $n != 1 && $n >= $input;
$cache[$input] = $steps += $cache[$n];
push @seqs, [ $input, $steps ] if $steps > 400;
}
}
collatz_seq($size);
@seqs = ( sort { $b->[1] <=> $a->[1]} @seqs )[ 0..19 ];
printf "Collatz(%5d) has sequence length of %3d steps\n", @$_
for @seqs;
</code>
<p><b>Step counting using Inline C:</b></p>
<code>
#!/usr/bin/env perl
use strict;
use warnings;
use Inline C => Config => CCFLAGSEX => '-O2 -fomit-frame-pointer';
use Inline C => <<'END_OF_C_CODE';
#include <stdint.h>
void num_steps_c( SV* _n, SV* _s )
{
uint64_t n, input;
int steps = 0;
n = input = SvUV(_n);
while ( n != 1 && n >= input ) {
n % 2 ? ( steps += 2, n = (3 * n + 1) >> 1 )
: ( steps += 1, n = n >> 1 );
}
sv_setuv(_n, n);
sv_setiv(_s, steps);
return;
}
END_OF_C_CODE
my $size = shift || 1e6;
$size = 1e6 if $size < 1e6; # minimum
$size = 1e9 if $size > 1e9; # maximum
##
# iM71's demonstration + applied T(x) notation and compression
# https://stackoverflow.com/a/55361008
# https://www.youtube.com/watch?v=t1I9uHF9X5Y (1 min into video)
#
# Parallel solution
# https://www.perlmonks.org/?node_id=11115780
##
my @cache = (0, 1, 2);
my @seqs;
sub collatz_seq {
my $size = shift;
my ($n, $steps);
for my $input (2..$size) {
num_steps_c($n = $input, $steps);
$cache[$input] = $steps += $cache[$n];
push @seqs, [ $input, $steps ] if $steps > 400;
}
}
collatz_seq($size);
@seqs = ( sort { $b->[1] <=> $a->[1]} @seqs )[ 0..19 ];
printf "Collatz(%5d) has sequence length of %3d steps\n", @$_
for @seqs;
</code>
<p><b>Results from two machines:</b></p>
<code>
64-bit VM:
rjt 0.903s
Laurent + updates 0.696s
iM71 + updates 0.602s
Step counting in C 0.273s (1st time involves compiling)
AMD 3970x:
rjt 0.635s
Laurent + updates 0.516s
iM71 + updates 0.467s
Step counting in C 0.191s (1st time involves compiling)
Collatz(837799) has sequence length of 525 steps
Collatz(626331) has sequence length of 509 steps
Collatz(939497) has sequence length of 507 steps
Collatz(704623) has sequence length of 504 steps
Collatz(910107) has sequence length of 476 steps
Collatz(927003) has sequence length of 476 steps
Collatz(511935) has sequence length of 470 steps
Collatz(767903) has sequence length of 468 steps
Collatz(796095) has sequence length of 468 steps
Collatz(970599) has sequence length of 458 steps
Collatz(546681) has sequence length of 452 steps
Collatz(818943) has sequence length of 450 steps
Collatz(820022) has sequence length of 450 steps
Collatz(820023) has sequence length of 450 steps
Collatz(410011) has sequence length of 449 steps
Collatz(615017) has sequence length of 447 steps
Collatz(886953) has sequence length of 445 steps
Collatz(906175) has sequence length of 445 steps
Collatz(922524) has sequence length of 445 steps
Collatz(922525) has sequence length of 445 steps
</code>
<p>Regards, Mario</p>
11115088
11115813