這道題應該算是我原創的的一道題,**於我遇到的乙個具體需求。大致需求是已知一批數和每個數出現的次數,然後寫個介面,每次呼叫都能返回已知資料中的某個數,且返回的概率和原始資料中每個數出現的概率一致,題目描述起來有些繞口,我們來舉個實際的例子。
以上面的輸入為例,要求實現的介面必須以11.96%的概率返回5、18.10%的概率返回91……16.55%的概率返回98,當然我的要求不僅僅是這幾個數,而是可能有10^5個數。 先別急著往下看,給你幾分鐘先思考下。
各種語言其實都內建了random函式,可以隨機返回int或者long型的隨機數,這裡我們先不考慮溢位的問題。為了方便講解,假設我們已有n個數存在在num[n]中,其出現的頻次存放在fre[n]中。 借助已有的random(),我們很簡單就可以生成0-n之間的乙個隨機數i,但是如果直接返回num[i]的話,每個數返回的概率是一致的,明顯不滿足我們的需求。
其實解決方案也很簡單,我們按照每個數出現的頻次大小,將其對映成不同的區間大小,出現的概率越大,區間越大。想象下,這些資料按不同的區間大小把乙個飛鏢盤分成不同的部分,我們生成數的時候就是拿個飛鏢隨機扎,扎到哪個算哪個。
當然我們可以直接用一位直線區間描述上面的二維飛鏢盤模型。只需要隨機生成0-100%之間的數即可,假設某次隨機生成的數是0.65(65%),我們算一下 正好對應在數字58對應的區間上,所以這次直接返回58就是了,我們可以開始寫**了。
int
num;
// 數字
int[
] fre;
// 出現的頻次
double
pro;
// 出現的概率
int n;
// 資料量
void
init()
for(
int i =
0; i < n; i++)}
intgetrandom()
sum += pro[i];}
return num[n-1]
;}
似乎一切都很完美,但每次getrandom()的時間複雜度是o(n),大量的使用效能也抗不太住。有沒有更好的實現方式?既然寫到這裡了,必然是有的。
上面**迴圈中有個sum += pro[i]; 每次計算都要累加,我們是不是可以提前在init()中累加好?然後你會發現因為每次累加的數都只正數,所以pro是個遞增序列,對於有序序列的查詢 二分必然是首選。這時候我們可以用二分重寫上面**。
int
num;
// 數字
int[
] fre;
// 出現的頻次
double
pro;
// 出現的概率
int n;
// 資料量
void
init()
for(
int i =
0; i < n; i++)}
}int
getrandom()
else
}return num[n-1]
;}
上述**中pro的計算有必要嗎? 能否直接用fre替代其功能?
面試題精選 微軟試題
微軟試題 1 直線飛行 一架飛機載滿油飛行距離為1,n架飛機最遠能飛多遠?就是不是兜圈沒有迎頭接應的情況,這問題就是n架飛機能飛多遠?存在的極值問題是不要重複飛行,比如兩架飛機同時給一架飛機加油同 時飛回來即可認為是重複,或者換句話說就是離出發點越遠在飛的飛機 就越少,這個極值條件是顯然的,因為n架...
幾個面試題精選
面試題35 typedef和define有什麼區別 1 用法不同 typedef用來定義一種資料型別的別名,增強程式的可讀性。define主要用來定義常量,以及書寫複雜使用頻繁的巨集。2 執行時間不同 typedef是編譯過程的一部分,有型別檢查的功能。define是巨集定義,是預編譯的部分,其發生...
C 面試題精選
1 執行如下的c 輸出是什麼?class a class b public a int main 答案 輸出b fun with number 10。由於a是乙個指向b例項的引用,因此在執行的時候會呼叫b fun。但預設引數是在編譯期決定的。在編譯的時候,編譯器只知道a是乙個型別a的引用,具體指向什...