Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/perl/11.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
试图预测未来的结果,collatz,Perl_Perl_Caching_For Loop_Prediction_Collatz - Fatal编程技术网

试图预测未来的结果,collatz,Perl

试图预测未来的结果,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的结果,依此类推 我试着解决这个问题

我正在制作一个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循环的外部

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);
        #...