C++中的随机生成算法

Posted

技术标签:

【中文标题】C++中的随机生成算法【英文标题】:Random generation algorithm in C++ 【发布时间】:2017-06-28 15:05:19 【问题描述】:

假设您需要生成前 N 个整数的随机排列。例如,4, 3, 1, 5, 2 和​​ 3, 1, 4, 2, 5 是合法排列,但 5, 4, 1, 2, 1 不是,因为一个数 (1 ) 重复,而另一个 (3) 丢失。该例程通常用于算法的模拟。我们假设存在一个随机数生成器 RandInt(i,j),它以相等的概率在 i 和 j 之间生成。这是算法:

将数组 A 从 A[0] 填充到 A[N-1],如下所示:要填充 A[i],生成随机数,直到得到一个不在 A[0]、A[1] 中的随机数,…, A[i-1].

用 C++ 实现这个算法并找出复杂度。这是我的代码:

int a;
bool b = false;
A[0] = RandInt(1,n);
for (int i=1;i<n;i++) 
do 
  b = false;
  a = RandInt(1,n);
  for (int j=0;j<i;j++)
     if(A[j] == a)
        b = true;
 while(b);
A[i] = a;

这段代码正确吗?我怎样才能找到算法的复杂性?由于 RandInt(i,j) 生成随机数,我不知道 do while 循环将重复多少次。

【问题讨论】:

如果序列少于大约 1,000,000,000 个值:用序列中的值填充数组,并使用成对的随机数通过交换值来打乱数组。至少进行与数组大小一样多的交换。要审核工作代码,请在 Code Review 上发帖:codereview.stackexchange.com @RichardCritten 更好的方法是 Fisher–Yates shuffle 或 std::shuffle,这可以保证均匀分布,但 OP 坚持使用他被告知无论如何都要使用的技术。 shuffleiota 提供的向量。 生成随机数,然后添加到std::set,这将自动消除重复。当set 的大小达到所需值时停止添加。对于复杂性,您需要查看随着set 的大小增加而选择已选择号码的概率。 【参考方案1】:

该算法将产生正确的结果,从所有可能的排列中随机均匀地选择一个排列。

运行时间不受任何确定性函数的限制,因为正如您所指出的,它实际上可以永远运行。在最好的情况下,该算法在 O(n^2) 中运行并选择随机排列,而无需重复任何选择。平均而言,您预计必须尝试 n/n=1 次才能获得第一个唯一随机数,n/(n-1) 次才能获得第二个随机数,依此类推,直到期望值 n/1= n次得到最后一个。将它们加在一起得到 n*H(n),其中 H(n) 是第 n 次谐波数。原来 H(N) 是 Theta(log n) 所以这个算法在平均情况下是 O(n^2 log n)。

有一种更好的方法来做你想做的事情:你可以从任何排列开始,然后使用在最坏情况下为 O(n) 的算法将它洗牌到另一个排列中。该算法是Fisher-Yates算法,工作原理如下:

FisherYates(array[1...n])
1. if n == 1 then return
2. r = random(2, n)
3. temp = array[1]
4. array[1] = array[r]
5. array[r] = temp
6. FisherYates(array[2...n])

这是一种递归公式,但迭代公式很简单。它准确地调用了random n 次,其中n 是最顶层调用时数组的大小。

【讨论】:

但是在最好的情况下,我们不是在第二个 for 循环中进行 1+2+3+...+n-1+n 次搜索吗?不是 O(n^2) 吗? @OlsionHysa 你知道吗,你是对的。我错过了。这将为给定的每个复杂性添加一个术语n:最佳和平均情况。所以这些实际上是最佳情况的 O(n^2) 和平均情况的 O(n^2 log n)。我很抱歉。

以上是关于C++中的随机生成算法的主要内容,如果未能解决你的问题,请参考以下文章

C++ 生成随机数

C++ 实现随机数生成(WindowsLinux)

使用C++生成1-33中的6个随机数,无重复

[NOI2014]随机数生成器

需要一个用于 C++ 的快速随机生成器 [关闭]

C++常用算法random_shuffle