Hi rjt,
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.
Update: Added results from two machines.
Laurent's demonstration plus updates:
#!/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;
iM71's C++ demonstration converted to Perl plus updates:
#!/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;
Step counting using Inline C:
#!/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;
Results from two machines:
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
Regards, Mario
-
Are you posting in the right place? Check out Where do I post X? to know for sure.
-
Posts may use any of the Perl Monks Approved HTML tags. Currently these include the following:
<code> <a> <b> <big>
<blockquote> <br /> <dd>
<dl> <dt> <em> <font>
<h1> <h2> <h3> <h4>
<h5> <h6> <hr /> <i>
<li> <nbsp> <ol> <p>
<small> <strike> <strong>
<sub> <sup> <table>
<td> <th> <tr> <tt>
<u> <ul>
-
Snippets of code should be wrapped in
<code> tags not
<pre> tags. In fact, <pre>
tags should generally be avoided. If they must
be used, extreme care should be
taken to ensure that their contents do not
have long lines (<70 chars), in order to prevent
horizontal scrolling (and possible janitor
intervention).
-
Want more info? How to link
or How to display code and escape characters
are good places to start.