随机数组程序吃我的记忆

时间:2013-01-08 14:25:14

标签: arrays perl random

我想编写一个perl程序来生成具有长度为8的非重复单位数字(随机顺序的数字1-8)和第九个元素作为下划线的数组。我写了这样的代码。我想将这个生成的数组用于基于数字的益智游戏。

@mat = (0,0,0,0,0,0,0,0,0);

sub randgen { 

    $randigit = int(rand(9));

    if ($randigit == 0) {  
        &randgen;   
    }

    elsif (    $mat[0] == $randigit
            || $mat[1] == $randigit
            || $mat[2] == $randigit
            || $mat[3] == $randigit
            || $mat[4] == $randigit
            || $mat[5] == $randigit
            || $mat[6] == $randigit
            || $mat[7] == $randigit
            || $mat[8] == $randigit
          )
    {
        &randgen;
    }
}

&randgen;

for ( $assign = 0; $assign <= 8; $assign++) {

    $mat[$assign] = $randigit;
    print "@mat \n"; # To see to what extent the program has generated the array
    &randgen;
}

for ($i = 0; $i <= $#mat; $i++) {   

    $sum = $sum + $mat[$i];
}

$miss = 36 - $sum ;
$mat[7] = $miss;
$mat[8] = "_";
print "@mat \n";

程序在分配第7个元素后,我的程序开始吃内存(10 GB)。我不明白这个原因。我使用数学逻辑来找到缺失的数字(数字之和--36(n(n + 1)/ 2))。为什么要吃我的记忆?或者是否有任何有效的方法来编写相同的程序?

3 个答案:

答案 0 :(得分:6)

有没有有效的方法来编写相同的程序?

你打赌,打赌。您只需要几行:

use List::Util 'shuffle';       # import shuffle
my @array = shuffle( 1 .. 8 );  # shuffle 1 to 8
push @array, '_';               # add the underscore

在一行中:

my @array = ( shuffle( 1 .. 8 ), '_' );

请考虑以下事项:

  • 将您的子结构设为return一个值
  • &randgen;做同样的事情
  • 时,避免写randgen();
  • 通过my
  • 进行词法范围确定
  • use strict; use warnings;
  • 尽可能避免进行递归调用(这可能是你的内存消耗的来源)

答案 1 :(得分:5)

我没有看过你的记忆是什么,但这是一种更简单的方法来实现你想要的东西:

#!/usr/bin/env perl
use strict;
use warnings;
use List::Util 'shuffle';

my @mats = shuffle 1 .. 8;
push @mats, '_';

print "@mats\n";

示例输出:

3 1 5 8 2 7 4 6 _

答案 2 :(得分:4)

当然,使用shuffle是解决问题的正确方法。这是关于你的代码出了什么问题的讨论,以及如何更优雅地表达这样的算法。

一旦你use warnings,就会收到关于深度递归的警告 - 调用堆栈正在占用你的记忆。在for循环中,在第8个元素(索引7)填充后调用randgen。在这种情况下,递归条件总是正确的(因为八个元素是唯一的,现在总是存在$randigit相等的元素。)

您的代码中还有一些其他内容可以改进:

for ($i = MIN; $i <= MAX; $i++)这样的循环最好写成foreach循环:

for my $i (MIN .. MAX)

子程序可以接受参数(这些参数在@_中)并返回值。这消除了使用全局变量来传递参数的需要。

另外,use strict; use warnings。这指出了许多错误,并强制您声明所有变量(您可以使用my声明变量。)

以下是您的代码的更新版本。它不具有递归功能,但具有循环功能。它完美无缺:

#!/usr/bin/perl

use strict; use warnings;

# randgen takes
#   - the largest number that may be used, and
#   - a list of all forbidden numbers.
sub randgen { 
    my ($max, @forbidden) = @_; # unpack arguments
    my $rand;
    do {
        $rand = int rand($max + 1);
    } while grep {$_ == $rand} 0, @forbidden;
    return $rand;  # return the rand value.
}

my $digits = 8;
my @mat;  # don't prefill fields.
for ( 1 .. $digits ) {                 # do $digits times 
    push @mat, randgen($digits, @mat); # push appends item to array
    print "@mat\n";
}
push @mat, "_";

print "@mat\n";

示例输出:

8
8 4
8 4 7
8 4 7 2
8 4 7 2 3
8 4 7 2 3 5
8 4 7 2 3 5 1
8 4 7 2 3 5 1 6
8 4 7 2 3 5 1 6 _

grep内置函数采用表达式或块,以及值列表。它返回表达式计算结果为true的所有值。可以通过$_访问当前项目。在我的循环中,只要@forbidden中的值或值0中的值等于$rand,它就会返回true。