題目:
給定數字N,N>0, 設計一個隨機數生成器,運行該生成器N次,能夠不重複地生成1,2,..,N。
思路1:
最簡單的方法,首先定義一個隨機數生成器,其可以生成1-N範圍內的隨機數。 第一次運行時生成一個隨機數,然後記錄該隨機數已經生成過,例如,可以定義數組bool array[N]={false}, 第一次生成了x,則置array[x-1]=true; 第二次時,先生成一個隨機數,然後檢查是否已經了,如果是,則重新生成,直到生成一個沒有生成過的爲止。
缺點: 可能會花費很長時間,例如,前面千辛萬苦生成了N-1個數之後,最後需要生成一個剩餘的7,你可能運行1年也得不到這個7啊。 這是工程實踐中的大忌!!!!!
思路2:
面試時竟然想了出來,見的算法多了真是有用的。
考慮到思路1的缺點在於隨機數生成後有可能與前面的重複,於是着手解決該問題。
當時場景:有N個測試案例,請設計隨機數生成器,用於生成1-N數字,(得到這個數字後對應的測試案例會被運行),運行該生成器N次後,能夠覆蓋素有測試案例。
首先建立一個鏈表,共有N個節點(例如,每個節點對應要給測試案例,即用於隨機案例測試的場景)。第一次時,生成1-N範圍內的數字,例如生成了3,則把鏈表中的第三個節點刪除, 鏈表中還剩餘N-1個節點,然後,下一次可以控制生成器生成1-(N-1)範圍內的任意一個數,這樣就不會存在重複問題了。
但是,一個缺點是,每次刪除鏈表中的節點的時候,需要遍歷鏈表,導致時間複雜度爲o(n^2)。
查閱資料後,最優的解決辦法:
繼續保持我的這個思路,,第一次時生成1-N範圍的,第二次時生成1 - (N-1)範圍的,,,。然後,關鍵在於如何使用這些生成的數。因爲第一次和第二次生成的可能都是3。 下面數組登場, 先定義以數組base[N],base[i] = i+1,使得其保存1-N。
假設第一次時,產生3,則將3作爲一個索引值, 從base中得到base[3],(索引的話,隨機數生成器的範圍改成0 -- N-1),
然後,將base[3] = base[N-1],即將最後一個元素放到3的位置,因爲下一個隨機數生成的範圍爲0 - N-2,不會得到base[N-1]了,這樣也可以保證第二次同樣生成3時,能取到不同值呀。
另外,第一次生成N-1時,則取base[N-1],然後base[N-1] = base[N-1],也ok的。
Note小技巧: val = max*coeff, coeff=[0,1), max爲正整數,coeff爲float,, (int)val屬於[0,max-1],向下取整了。
Show me:
#include <iostream>
#include <vector>
#include <cstdlib>
/*
Function: generateRandomNums
Description: Give parameter N(N>0), then generate N unduplicated random numbers within the range from 1 to N.
*/
void generateRandomNums(unsigned int max)
{
if (max < 1)
return;
unsigned int MaxNum = max;
std::vector<unsigned int> BaseArray(max);
std::vector<unsigned int> mResult(max);
unsigned int si = BaseArray.size();
for (unsigned int i = 0; i < max; ++i)
{
BaseArray[i] = i + 1;
}
for (int i = 0; i < MaxNum; ++i)
{
/**generate a random number within the range of max*[0,1)*/
int index = (rand() % 100) / 100.0f *max;
/**get a member from BaseArray*/
mResult[i] = BaseArray[index];
BaseArray[index] = BaseArray[max - 1];
/**decrease max to ensure that mResult's elements is ununduplicated. */
max--;
}
for (auto val : mResult)
{
std::cout << val << std::endl;
}
}