use*_*819 1 c++ algorithm big-o
我想用 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)
Run Code Online (Sandbox Code Playgroud)
While 循环会一直持续下去,直到我们生成从 1 到 N 的所有值。我的理解是 Set 将值以对数时间 log(N) 排序,并插入到 log(N) 中。
Big-O = O(1) + O(X*log N) + O(N*log N) = O(X*log N)
Run Code Online (Sandbox Code Playgroud)
其中X越多,生成不在Set中的数的概率就越高。
time O(X log N)
space O(2N+1) => O(N), we reuse the space of val
Run Code Online (Sandbox Code Playgroud)
在哪里??每次执行 randInt 时都很难生成所有不同的数字,所以至少我期望执行 N 次。
变量 X 是否被创建了多次?
X 的合适值是多少?
假设RNG是理想的。也就是说,重复调用 randInt(1,N) 会生成均匀分布在 {1,...,N} 上的 iid(独立同分布)值序列。
\n\n(当然,实际上 RNG 并不理想。但我们就这样吧,因为它让数学变得更容易。)
\n\n在第一次迭代中,选择一个随机值 val 1,当然它还不在集合 S 中。
\n\n在下一次迭代中,选择另一个随机值。
\n\n平均需要多少次迭代才能选择有效(与 val 1不同)的 val 2?好吧,我们有一个独立的尝试序列,每次成功的概率为 (N-1)/N,我们想知道第一次成功之前平均需要多少次尝试。这是一个几何分布,一般来说,成功概率为 p 的几何分布的平均值为 1/p。因此,平均需要 N/(N-1) 次尝试才能选择 val 2。
\n\n同样,平均需要 N/(N-2) 次尝试才能选择与 val 1和 val 2不同的val 3,依此类推。最后,第 N 个值平均需要 N/1 = N 次尝试。
\n\n总共将执行 do 循环
\n\n\n\n平均次数。总和
是 N 次谐波数,可以粗略地近似为 ln(N)。(有一个众所周知的更好的近似,它有点复杂并且涉及Euler-Mascheroni 常数,但是 ln(N) 足以找到渐近复杂性。)
因此,近似而言,平均迭代次数将为 N ln N。
\n\n算法的其余部分怎么样?像将 N 个东西插入到集合中这样的事情最多也需要 O(N log N) 时间,因此可以忽略。剩下的一件大事是,每次迭代你都必须检查所选的随机值是否位于 S 中,这需要 S 的当前大小的对数时间。所以我们必须计算
\n\n\n\n根据数值实验,对于较大的 N,它似乎大约等于 N/2 * (ln N)^2。(也许可以考虑在 math.SE 上索取这方面的证明。)编辑:请参阅这个 math.SE 答案以获得简短的非正式证明,以及该问题的另一个答案以获得更正式的证明。
因此总而言之,总平均复杂度为 \xce\x98(N (ln N)^2)。\n这再次假设 RNG 是理想的。
\n\n就像 xxxxon 提到的那样,原则上算法有可能(尽管不太可能)根本不会终止。因此,最坏情况的复杂度为 O(\xe2\x88\x9e)。
\n