Algorithm 范围内非重复随机搜索算法

Algorithm 范围内非重复随机搜索算法,algorithm,random,non-repetitive,Algorithm,Random,Non Repetitive,我正在寻找一种高效的算法,可以在一定范围内生成随机值,而不需要重复 在伪代码中:(在类Rand中) 用法: Rand r = new Rand(1, 100000000); long x = r.getNumber(); long y = r.getNumber(); ... 从r.getNumber()返回的数字应始终与以前返回的数字不同。 当然,如果返回了to-from+1数字,那么算法应该重新开始(或者出错——无论如何都不重要) 请注意,范围可能非常大,因此随机排列的数

我正在寻找一种高效的算法,可以在一定范围内生成随机值,而不需要重复

在伪代码中:(在类Rand中)

用法:

  Rand r = new Rand(1, 100000000);

  long x = r.getNumber();
  long y = r.getNumber();
  ...
从r.getNumber()返回的数字应始终与以前返回的数字不同。
当然,如果返回了
to-from+1
数字,那么算法应该重新开始(或者出错——无论如何都不重要)


请注意,范围可能非常大,因此随机排列的数组(最初包含
[from,to]
数字)可能会使内存溢出。

一种方法是生成一个介于from和to之间的数字列表,随机删除这些数字,直到袋子变空,然后重新填充。为了节省大范围的存储空间,您可以将拾取的数字记录到某一点(在选择复制品时重新拾取),因为拾取复制品的概率最初应该很低。确定最佳过渡点可能是一项经验性工作

编辑:还有一些想法


对于真正的大范围,即使这样也不能在内存限制下提供良好的性能。一个想法可能是将候选对象存储为间隔,而不是作为数字列表。因此,最初,您可以在从和到之间进行选择,得到x1。下次,从第一个子区间或第二个子区间中选择一个数字,概率与区间长度成比例。在每个步骤中,消除长度为零的间隔。这需要存储M+2个整数(在最坏的情况下),其中M是绘制的数量,或者N/2渐近地存储大N(在最坏的情况下),其中N是初始间隔大小。不过,也许有人会仔细检查我

一种方法是生成一个介于from和to之间的数字列表,随机删除这些数字,直到袋子变空,然后重新填充。为了节省大范围的存储空间,您可以将拾取的数字记录到某一点(在选择复制品时重新拾取),因为拾取复制品的概率最初应该很低。确定最佳过渡点可能是一项经验性工作

编辑:还有一些想法


对于真正的大范围,即使这样也不能在内存限制下提供良好的性能。一个想法可能是将候选对象存储为间隔,而不是作为数字列表。因此,最初,您可以在从和到之间进行选择,得到x1。下次,从第一个子区间或第二个子区间中选择一个数字,概率与区间长度成比例。在每个步骤中,消除长度为零的间隔。这需要存储M+2个整数(在最坏的情况下),其中M是绘制的数量,或者N/2渐近地存储大N(在最坏的情况下),其中N是初始间隔大小。不过,也许有人会仔细检查我

如果不要求间隔中的每个数字最终出现,可以使用:

它是周期性的,新的周期从种子等于初始值开始,周期的长度取决于A和C的选择

优点:O(1)时间和空间,缺点:并非间隔中的每个数字都会出现


对于长度为2^m的间隔,请看我没有使用它,但wikipedia说它可能是最大长度,也就是说,您可以在输出中显示所有数字(除一个以外)。

如果您不要求间隔中的每个数字最终都显示,您可以使用:

它是周期性的,新的周期从种子等于初始值开始,周期的长度取决于A和C的选择

优点:O(1)时间和空间,缺点:并非间隔中的每个数字都会出现


对于长度为2^m的间隔,请看我没有使用它,但wikipedia说它可能是最大长度,也就是说,您可以在输出中显示所有数字(除一个以外)。

关于起点的一些想法:

1) 假设你有一个函数f(x),它是1..N上的一个置换,其中N大于你的范围。如果将其应用于范围内的x,则可能会产生非法值-超出范围的值。您可以通过对非法值再次调用f来定义范围内的置换。你最终会得到一个合法值,因为序列x,f(x),f^2(x),f^3(x)最终必须循环,如果最坏的情况发生,它将在x处返回

2) 有一些交换网络可以让你在N个对象上产生所有可能的排列,例如(有趣的URL-Benes网络)。通过随机设置开关,可以在N个对象上获得任意排列,其中N可能是2的幂。因为将有K个开关,所以有2^K种设置它们的方法,这意味着您没有M!概率相等的排列,但也许你不会介意,或者可以通过多次重复来最小化非随机性


3) 如果您准备通过多次应用许多不同的基本置换来实现近似随机性,您可以尝试在整个范围内交替添加mod N,然后将范围拆分为子范围,例如,对于范围内的一些p-1值拉伸,应用通过乘以一些k模式p产生的置换。我们希望,尽管每个步骤都非常基本,但只要应用足够多的步骤并使它们足够多样化,结果就会接近随机,特别是如果您随机选择参数。

关于起点的一些想法:

1) 假设你有一个函数f(x),它是1..N上的一个置换,其中N大于你的范围。如果将其应用于范围内的x,则可能会产生非法值-超出范围的值。您可以通过对非法值再次调用f来定义范围内的置换。你最终会得到一个法律价值,因为
  Rand r = new Rand(1, 100000000);

  long x = r.getNumber();
  long y = r.getNumber();
  ...
int getNumber() {
    seed = (seed * A + C) mod (to-from);
    return seed + to;
}
import random

def randomFromTo(FROM,TO,k): #k is the number of sample you want to generate
    m= random.sample(xrange(FROM,TO),k)
    return (i for i in m)
RandomGenerator=randomFromTo(10,1000000000,12)

for k in range(12): 
    print RandomGenerator.next()
57625960
50621599
2891457
56292598
54608718
45258991
24112743
55282480
28873528
1120483
56876700
98173231
> sample(x = 1:30, size = 10, replace = FALSE)
 [1]  9 16 13 20 12  3  1  5 28  7
> sample(x = 1:30, size = 10, replace = FALSE)
 [1] 22 11 26 29 20  1  3  6  7 10
> sample(x = 1:30, size = 10, replace = FALSE)
 [1]  1 11 16  7 22 26  3 25  8  9
> sample(x = 1:30, size = 10, replace = FALSE)
 [1]  7 17  3 22 21 24 27 12 28  2
> sample(x = 1:30, size = 10, replace = FALSE)
 [1] 30 21 23  2 27 24  3 18 25 19
> sample(x = 1:30, size = 10, replace = FALSE)
 [1]  4  6 11 16 26  8 17 22 23 25
 37  5  53  21  45  13  61  29  33  1  49  17  41  9  57  25  39  7  55  23  47  15  63  31  35  3  51  19  43  11  59  27  36  4  52  20  44  12  60  28  32  0  48  16  40  8  56  24  38  6  54  22  46  14  62  30  34  2  50  18  42  10  58  26 

bits: 6,  count: 64, set: 64
function math_random_filled_arithmetical($n,$max,$seed)
    {
    /*
    - produces 0..max, repeatable, unique, one-to-one mapped random numbers
    - uses arithmetic operations to imitate randomness
    - $n: 0<=$n=<$max
    - $max: any integer, not only power of two
    - $seed: any string or number
    */
    $n=intval($n);
    $max=intval($max);
    $opt1=$n;
    $opt2=$max-$n;
    $n2=min($opt1,$opt2);
    $reverseit=crc32($seed.'internalseed'.$n2)&1;
    if($opt1>$opt2) $reverseit=!$reverseit;
    $max2=floor(intval($max-1)/2);
    //echo "n:$n, max:$max,n2:$n2,max2:$max2,reverseit:$reverseit\n";
    if($max>=3)
    if($opt1!=$opt2)
        $n2=math_random_filled_arithmetical($n2,$max2,$seed.'*');
    $res=$reverseit? $max-$n2:$n2;
    $res=intval(fmod($res+(crc32($seed)&(1<<30)),$max+1));
    //echo "n:$n, max:$max, res:$res\n";
    return $res;
    }


//demonstration
$max=41;//-- test a max value 
for($n=0;$n<=$max;$n++)
    {
    $r=math_random_filled_arithmetical($n,$max,$seed='someseed');
    $ar[$r]=1;
    echo " $r ";
    }
$filled=0;
for($n=0;$n<=$max;$n++)
    if(isset($ar[$n])) $filled++;
echo "\n count: ".($max+1).", filled: ". $filled."\n";
 20  19  18  17  33  32  37  36  14  13  31  34  35  26  16  11  12  3  39  40  0  41  1  2  38  29  30  25  15  6  7  10  28  27  5  4  9  8  24  23  22  21 
 count: 42, filled: 42