我正在处理的问题涉及一些问题,即:
splice
无法正常工作。我首先选择1到1,000,000,000之间的数字。
my $random = int(rand(1_000_000_000)) + 1;
我添加一个值,比如说100,以使$random
和$random + 100
定义一个间隔。
my $interval = $random + 100;
然后我push
将$random
和$interval
放入另一个数组中。另一个数组是存储间隔。
push ( @rememberOldIntervals, $random, $interval );
我使用@rememberOldIntervals
循环遍历数组for
,成对提取项目。一对中的第一个是前$random
,另一个是$interval
。在这个for
循环中,我做了另一个随机数生成。但是生成的数字不能在已经采用的间隔之间。如果是这样,请继续采样,直到找到唯一的数字。此外,这个新的随机数必须与任何旧的间隔至少相距100。
for ( my $i= 0; $i < (scalar @rememberOldIntervals) / 2 ; $i=+2) {
$random = int(rand(1_000_000_000)) + 1;
my $new_random_low = $random - 100;
my $new_random_high = $random + 100;
if ( $new_random_low <= $rememberOldIntervals[0] OR
$new_random_high >= $rememberOldIntervals[1] ){
push( @rememberOldIntervals, $new_random_low, $new_random_high );
}
else {
until ($new_random_low <= $rememberOldIntervals[0] OR
$new_random_high >= $rememberOldIntervals[1] ) {
$random = int(rand(1_000_000_000)) + 1;
my $new_random_low = $random - 100;
my $new_random_high = $random + 100;
}
}
}
后一个循环需要嵌入另一个循环中以驱动它多次,比如10,000次。
答案 0 :(得分:1)
您可以使用哈希值和索引加快速度。
这会将空间分成宽度为200的索引段,每个间隔将随机放置在一个随机段中。
my $interval = 100;
my $space = 1e9;
my $interval_count = 1e4;
my @values;
my %index_taken;
for(1..$interval_count)
{
my $index;
$index while $index_taken{$index = int rand $space/2/$interval }++;
my $start = $index*2*$interval + 1 + int rand $interval;
push @values, $start, $start+$interval;
}
它保证不重叠的间隔,但在两个间隔之间将存在高达200的无法访问的空间。
或者,如果您希望间隔排序:
@values = map {$_*=2*$interval; $_+=1+int rand $interval; ($_,$_+$interval)}
sort keys %index_taken;
答案 1 :(得分:1)
这个问题可以重新定义为在0到10亿之间抽取10,000个随机数,其中没有数字在100之内。
蛮力 - 5秒
因为你只提取10,000个数字,而且可能不需要经常这样做,我建议最初使用暴力来解决这类问题。这是试图遵循Premature optimization is the root of all evil
的设计模式在这种情况下,这意味着只需提取随机数并将它们与之前提取的所有数字进行比较。这将具有O(N^2)
的速度,但也将减少代码。
use strict;
use warnings;
my $max = 1_000_000_000;
my $dist = 100;
my $count = 10_000;
die "Too many numbers" if 2 * $dist * $count >= $max;
my @numbers;
while (@numbers < $count) {
my $num = int rand $max;
push @numbers, $num if ! grep {abs($num - $_) < $dist} @numbers;
}
print scalar(@numbers), "\n";
输出需要5秒钟:
10000
二进制搜索更快生成 - 0.14秒
现在为了更快的算法,我同意ysth
一个更有效的解决方法是创建两个随机数列表。其中一个是运行列表,另一个是排序。使用排序列表对位置进行二进制搜索,然后与附近的元素进行比较,看它是否在100之内。
这会减少从O(N^2)
到O(N log N)
的比较次数。运行时间仅为0.14秒,而暴力方法则为5秒。
use strict;
use warnings;
my $max = 1_000_000_000;
my $dist = 100;
my $count = 10_000;
die "Too many numbers" if 2 * $dist * $count >= $max;
my @numbers;
my @sorted = (-$dist, $max); # Include edges to simplify binary search logic.
while (@numbers < $count) {
my $num = int rand $max;
# Binary Search of Sorted list.
my $binary_min = 0;
my $binary_max = $#sorted;
while ($binary_max > $binary_min) {
my $average = int( ($binary_max + $binary_min) / 2 );
$binary_max = $average if $sorted[$average] >= $num;
$binary_min = $average + 1 if $sorted[$average] <= $num;
}
if (! grep {abs($num - $_) < $dist} @sorted[$binary_max, $binary_max - 1]) {
splice @sorted, $binary_max, 0, $num;
push @numbers, $num;
}
}
print scalar(@numbers), "\n";
最快的商数哈希 - 0.05秒
我在评论中询问:“你可以简化这个问题来选择100的随机倍数吗?这样可以确保没有重叠,然后你只需要选择1到1千万的随机数而不需要重复,然后将它乘以100。“你没有回应,但我们仍然可以使用100的倍数进行分组来简化这个问题。
基本上,如果我们跟踪一个数字除以100的商数,我们只需要将它与商数加减1的数字进行比较。这减少了与O(N)
的比较次数,这在0.05秒内最快就是最快:
use strict;
use warnings;
my $max = 1_000_000_000;
my $dist = 100;
my $count = 10_000;
die "Too many numbers" if 2 * $dist * $count >= $max;
my @numbers;
my %num_per_quot;
while (@numbers < $count) {
my $num = int rand $max;
my $quotient = int $num / $dist;
if (! grep {defined && abs($num - $_) < $dist} map {$num_per_quot{$quotient + $_}} (-1, 0, 1)) {
push @numbers, $num;
$num_per_quot{$quotient} = $num;
}
}
print scalar(@numbers), "\n";
如果您使用的是
,请注意如果您在Windows上运行此代码并且使用的perl版本低于v5.20,则需要使用比内置rand
更好的随机数生成。出于原因,请阅读avoid using rand if it matters
。
我在这段代码中使用了Math::Random::MT qw(rand);
,因为我正在使用Strawberry Perl v5.18.2。但是,从Perl v5.20开始,这将不再是一个问题,因为rand now uses a consistent random number generator
。