【问题标题】:What is the Big-O of code that uses random number generators?使用随机数生成器的代码大 O 是什么?
【发布时间】:2016-10-05 08:40:03
【问题描述】:

我想用从 1 到 N 的随机值填充数组“a”(没有重复值)。让我们假设 randInt(i, j) 的 Big-O 为 O(1),并且此函数生成从 i 到 j 的随机值。
输出示例如下:

{1,2,3,4,5} 或 {2,3,1,4,5} 或 {5,4,2,1,3} 但不是 {1,2,1,3,4 }

#include<set>
using std::set;

set<int> S;// space O(N) ?
int a[N];  // space O(N)
int i = 0; // space O(1)
do {
    int val = randInt(1,N);   //space O(1), time O(1) variable val is created many times ?
    if (S.find(val) != S.end()) { //time O(log N)? 
        a[i] = val; // time O(1)
        i++; // time O(1)
        S.insert(val); // time O(log N)  <-- we execute N times O(N log N)
    }
 } while(S.size() < N); // time O(1)

While 循环将继续,直到我们生成从 1 到 N 的所有值。 我的理解是 Set 以对数时间 log​​(N) 对值进行排序,并在 log(N) 中插入。

Big-O = O(1) + O(X*log N) + O(N*log N) = O(X*log N) 

其中X越多,生成不在Set中的数字的概率就越大。

time O(X log N)

space O(2N+1) => O(N), we reuse the space of val 

在哪里 ??每次执行 randInt 都很难生成所有不同的数字,所以至少我希望执行 N 次。
变量 X 是否创建了多次?
X 的良好价值是多少?

【问题讨论】:

  • 弄清楚代码的大 O 不如编写可以工作/没有无限循环的代码重要
  • 我们也对您的随机来源一无所知。如果它真的是随机的,那么你的最坏情况 X 是 ∞。

标签: c++ algorithm big-o


【解决方案1】:

假设 RNG 是理想的。也就是说,对 randInt(1,N) 的重复调用会生成一个 i.i.d。 (独立且同分布)均匀分布在 {1,...,N} 上的值序列。

(当然,实际上 RNG 并不理想。但让我们继续吧,因为它使数学更容易。)

平均情况

在第一次迭代中,选择了一个随机值 val1,当然它还没有在集合 S 中。

在下一次迭代中,选择另一个随机值。

  • 在概率 (N-1)/N 的情况下,它将与 val1 不同,并且将执行内部条件。在这种情况下,调用所选值 val2
  • 否则(概率为 1/N),所选值将等于 val1。重试。

平均需要多少次迭代才能选择有效的(不同于 val1)val2?好吧,我们有一个独立的尝试序列,每个尝试成功的概率为 (N-1)/N,我们想知道平均需要多少次尝试才能第一次成功。这是一个几何分布,通常成功概率为 p 的几何分布的均值为 1/p。因此,平均需要 N/(N-1) 次尝试来选择 val2

同样,平均需要 N/(N-2) 次尝试来选择与 val1 和 val2 不同的 val3,等等。最后,第 N 个值平均需要 N/1 = N 次尝试。

总共会执行do循环

平均次数。和 是第N 个harmonic number,可以用ln(N) 粗略近似。 (有一个众所周知的better approximation,它有点复杂,涉及到Euler-Mascheroni constant,但ln(N) 足以找到渐近复杂度。)

因此,对于一个近似值,平均迭代次数将为 N ln N。

算法的其余部分呢?像将 N 个东西插入一个集合之类的事情最多也需要 O(N log N) 时间,因此可以忽略。剩下的最大的事情是每次迭代你必须检查选择的随机值是否在 S 中,这在 S 的当前大小中需要对数时间。所以我们必须计算

从数值实验来看,对于大 N,它似乎大约等于 N/2 * (ln N)^2。(考虑在 math.SE 上寻求证明。) del> 编辑:请参阅this math.SE answer 以获得简短的非正式证明,以及other answer to that question 以获得更正式的证明。

因此,总而言之,总平均复杂度为 Θ(N (ln N)^2)。 同样,这是假设 RNG 是理想的。

最坏情况

就像 xaxxon 提到的,原则上可能(尽管不太可能)算法根本不会终止。因此,最坏情况的复杂度将是 O(∞)。

【讨论】:

    【解决方案2】:

    这是实现目标的非常糟糕的算法。

    只需用数字 1 到 N 填充数组,然后随机播放。

    这是 O(N)

    https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle

    要随机播放,请在 0 和 N-1 之间选择一个索引并将其与索引 0 交换。然后在 1 和 N-1 之间选择一个索引并将其与索引 1 交换。一直到列表末尾。

    就您的具体问题而言,这取决于您的随机数生成器的行为。如果它真的是随机的,它可能永远不会完成。如果是伪随机,则取决于生成器的周期。如果它的周期为 5,那么您将永远不会有任何欺骗。

    【讨论】:

    • 我知道这是一个糟糕的算法,但我不知道如何分析 Big-O。
    • 不回答问题。 -1
    • 问题的重点是分析一个非平凡算法的行为,而不是把它写得最优。出于本次讨论的目的,人们可能会假设 RNG 是正常的。
    • @user2143819 代码还不错,其实不行。
    • @xaxxon 它使答案变得微不足道:此代码的大 O(最佳、平均、最差)总是无穷大 :)
    【解决方案3】:

    这是具有复杂行为的灾难性代码。生成第一个数字是 O(1),然后第二个涉及二进制搜索,所以 log N,加上如果找到数字,则重新运行生成器。得到一个新数的机会是 p = 1- i/N。所以重新运行的平均次数是倒数,给你另一个 N 因子。所以 O(N^2 log N)。

    这样做的方法是生成数字,然后将它们随机排列。那是 O(N)。

    【讨论】:

    • 如果不考虑随机数生成器的范围,就无法分析算法。您是否因为被否决而删除了之前的答案并将其作为新答案粘贴回来?
    • 不,因为我错过了搜索是二进制的。但是随机数生成器是 O(1) 并且范围是已知的,所以代码已经定义了行为并且可以被分析。
    • 这取决于底层算法的范围和周期性——可能实际上并没有生成数字 1-N。
    • 正如我所说,我们可以假设 RNG 是正常的。
    • 没有“功能性 RNG”这样的东西
    猜你喜欢
    • 1970-01-01
    • 2019-11-28
    • 1970-01-01
    • 2015-02-07
    • 2019-06-04
    • 2010-11-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多