试图预测未来的结果,collatz,Perl
我正在制作一个collatz序列。我目前有一个for循环 然后我有一个简单的方法来计算循环的计数(完成这个理论需要很多次) 我发现,通过使用一个简单的理论,可以更快地编写代码 如果n=3,那么如果n为,它将有这个序列{3,10,5,16,8,4,2,1}[8]= 如果n=12,它将有这个序列{6,3,10,5,16,8,4,2,1}[9] 将有这个序列{12,6,3,10,5,16,8,4,2,1}[10] 所以我想保存3的结果,通过在计数中加1,就可以算出6的结果,依此类推 我试着解决这个问题,用我认为会奏效的方法,但实际上我的程序需要多花1分钟才能完成,我现在有一个程序需要1.49秒,而不是以前的30秒 这就是我添加缓存的方式(可能是错误的) 下面是for循环的外部试图预测未来的结果,collatz,Perl,perl,caching,for-loop,prediction,collatz,Perl,Caching,For Loop,Prediction,Collatz,我正在制作一个collatz序列。我目前有一个for循环 然后我有一个简单的方法来计算循环的计数(完成这个理论需要很多次) 我发现,通过使用一个简单的理论,可以更快地编写代码 如果n=3,那么如果n为,它将有这个序列{3,10,5,16,8,4,2,1}[8]= 如果n=12,它将有这个序列{6,3,10,5,16,8,4,2,1}[9] 将有这个序列{12,6,3,10,5,16,8,4,2,1}[10] 所以我想保存3的结果,通过在计数中加1,就可以算出6的结果,依此类推 我试着解决这个问题
my $cache = 0;
my $lengthcache = 0;
$cache = $i;
$lengthcache = $count;
if ($cache = $num*2) {
$lengthcache++;
}
然后我有一段代码,位于for循环中$I行,第4行之后
my $cache = 0;
my $lengthcache = 0;
$cache = $i;
$lengthcache = $count;
if ($cache = $num*2) {
$lengthcache++;
}
我不想得到完整的答案,我只需要了解如何正确缓存代码而不使代码变慢。如果将实现更改为递归函数,可以使用Memoize()将其包装起来,以加快已计算的响应
use strict;
use warnings;
use feature qw/say/;
use Data::Printer;
use Memoize;
memoize('collatz');
for my $num (qw/3 6 12 1/) {
my @series = collatz($num);
p(@series);
say "$num : " . scalar @series;
}
sub collatz {
my($i) = @_;
return $i if $i == 1;
return ($i, collatz( $i % 2 ? 3 * $i + 1 : $i / 2 ));
}
输出
[
[0] 3,
[1] 10,
[2] 5,
[3] 16,
[4] 8,
[5] 4,
[6] 2,
[7] 1
]
3 : 8
[
[0] 6,
[1] 3,
[2] 10,
[3] 5,
[4] 16,
[5] 8,
[6] 4,
[7] 2,
[8] 1
]
6 : 9
[
[0] 12,
[1] 6,
[2] 3,
[3] 10,
[4] 5,
[5] 16,
[6] 8,
[7] 4,
[8] 2,
[9] 1
]
12 : 10
[
[0] 1
]
1 : 1
你只是想要长度,对吗?缓存序列不会节省很多钱,而且内存使用量也会相当大 编写一个返回长度的递归函数
sub seq_len {
my ($n) = @_;
return 1 if $n == 1;
return 1 + seq_len( $n % 2 ? 3 * $n + 1 : $n / 2 );
}
缓存结果
my %cache;
sub seq_len {
my ($n) = @_;
return $cache{$n} if $cache{$n};
return $cache{$n} = 1 if $n == 1;
return $cache{$n} = 1 + seq_len( $n % 2 ? 3 * $n + 1 : $n / 2 );
}
还可以将终止条件移动到缓存中
my %cache = ( 1 => 1 );
sub seq_len {
my ($n) = @_;
return $cache{$n} ||= 1 + seq_len( $n % 2 ? 3 * $n + 1 : $n / 2 );
}
递归不是必需的。你可以通过把它调平来加速它。这有点棘手,但您可以使用通常的技术来完成[1]
确保它工作正常:
use strict;
use warnings;
use feature qw( say );
use List::Util qw( sum );
my $calculations;
my %cache = ( 1 => 1 );
sub seq_len {
my ($n) = @_;
my @to_cache;
while (1) {
if (my $length = $cache{$n}) {
$cache{pop(@to_cache)} = ++$length while @to_cache;
return $length;
}
push @to_cache, $n;
++$calculations;
$n = $n % 2 ? 3 * $n + 1 : $n / 2;
}
}
my @results = map { seq_len($_) } 3,6,12;
say for @results;
say "$calculations calculations instead of " . (sum(@results)-@results);
注:
将您的算法更改为缓存结果,以便它可以提前爆发:
use strict;
use warnings;
my @steps = (0,0);
my $max_steps = 0;
my $max_num = 0;
for my $num (2..1_000_000) {
my $count = 0;
my $i = $num;
while ($i >= $num) {
$i = $i % 2 ? 3 * $i + 1 : $i / 2;
$count++;
}
$count += $steps[$i];
$steps[$num] = $count;
if ($max_steps < $count) {
$max_steps = $count;
$max_num = $num;
}
}
print "$max_num takes $max_steps steps\n";
当我使用
memoize
时,如Oesor
所示,速度是23秒
使用散列怎么样?你能再解释一点吗,我不完全明白这是在做什么?谢谢你的反馈,但是使用最后一种方法,速度与我第一次尝试的速度差不多。你做错了什么事。它需要6秒而不是29秒。是的,我一定是做错了什么,但这很难,因为我不完全理解所使用的方法,或者for是如何实现的。这是一个计数循环。执行循环体一百万次。就像你问题中的循环一样。我只是用更少的字符写的;它将多次计算某些输入的序列长度。注意,优化依赖于OP中的调用模式;如果不计算之前所有其他输入的序列长度,它就无法找到任意输入的序列长度,如果只需要计算少数输入的长度,速度会慢得多。是的,它只进行部分缓存,但对于这种情况,2.5秒就足够了。同样考虑到两天前的帖子和提供的代码,似乎有理由假设OP想要确定最大周期,因此将迭代所有数字,而不是子集。哇,这太棒了!我说的对吗?数组的最后一部分使用$I作为键是为了阻止数组自身过载?@user3662493我按顺序计算@steps
值。因此,一旦$i
小于当前的$num
,我们就知道$steps[$i]
将被定义
,并且可以提前打破循环并依赖缓存的值。
my %steps = (1 => 0);
for my $num (2..1_000_000) {
my @i = $num;
while (! defined $steps{$i[-1]}) {
push @i, $i[-1] % 2 ? 3 * $i[-1] + 1 : $i[-1] / 2;
}
my $count = $steps{pop @i};
$steps{pop @i} = ++$count while (@i);
#...
use strict;
use warnings;
my @steps = (0,0);
my $max_steps = 0;
my $max_num = 0;
for my $num (2..1_000_000) {
my $count = 0;
my $i = $num;
while ($i >= $num) {
$i = $i % 2 ? 3 * $i + 1 : $i / 2;
$count++;
}
$count += $steps[$i];
$steps[$num] = $count;
if ($max_steps < $count) {
$max_steps = $count;
$max_num = $num;
}
}
print "$max_num takes $max_steps steps\n";
my %steps = (1 => 0);
for my $num (2..1_000_000) {
my @i = $num;
while (! defined $steps{$i[-1]}) {
push @i, $i[-1] % 2 ? 3 * $i[-1] + 1 : $i[-1] / 2;
}
my $count = $steps{pop @i};
$steps{pop @i} = ++$count while (@i);
#...