來自:【數據結構與算法分析——C語言描述】練習2.7
問題描述:假設需要生成前N個自然數的一個隨機置換。例如,{4,1,2,5,2} 和 {3,1,4,2,5} 就是合法的置換,但 {5,4,1,2,1} 卻不是,因為數1出現了兩次而數 3 缺沒有。這個程序常常用於模擬一些算法。我們假設存在一個隨機數生成器 randInt(i, j) ,它以相同的概率生成 i 和 j 之間的一個整數。
下面是三個算法:
1.如下填入 A[0] 到 A[N-1] 的數組 A;為了填入 A[i] ,生成隨機數直到它不同於已經生成的 A[0], A[1], ... , A[i-1] 時,再將其填入 A[i] 。
2.同算法1,但是要保存一個附加的數組,稱之為 Used(用過的)數組。當一個隨機數 Ran 最初被放入數組A的時候,置Used[Ran]=1。這就是說,當用一個隨機數填入 A[i] 時,可以用一步來測試是否該隨機數已經被使用,而不是像第一個算法那樣(可能)進行 i 步測試。
3.填寫該數組使得 A[i] = i + 1。然后
for(i = 1; i < N; i++) swap(&A[i], &A[randInt(0, i)]);
題目中有說“我們假設存在一個隨機數RandInt(i, j)”,既然如此,暫時不必考慮其內部實現以及內存限制,只根據這個接口考慮算法即可。
算法1
i = 0; while (i < N) { ran = randInt(0, N); for (k = 0; k < i; k++) { if (A[k] == ran) break; } if (k == i) { A[i] = ran; count++; } }
以循環次數作為時間度量。循環從i=0到i=n-1寫出n個不重復隨機數,因為生成第i個不重復隨機數的概率是(N-i)/N,所以理論上經過N/(N-i)次生成,得到不重復隨機數的概率為1。所以總耗時為
這里做了分子的放大,以及這個調和和公式:
該算法的時間復雜度即為O(N2logN)。
算法2
i = 0; while (i < N) { ran = randInt(0, N); if (used[ran] == 0) { A[i] = ran; used[ran] = 1; i++; } }
算法 2 比算法 1 少了內層的 for 循環,所以時間復雜度降了一階,即為 O(NlogN)。
算法3
for (i = 0; i < N; i++) A[i] = i + 1; for (i = 0; i < N; i++) swap(&A[i], &A[randInt(0, i)]);
時間復雜度顯然是線性級的,O(N)。