一次遍历等概率选取字符串中的某个字符
http://blog.chinaunix.net/uid-7921481-id-3022614.html
存在一個等概率的0、1發生器。
給一個文本流,給定一個指定的字符'x',寫一個函數,等概率地返回'x'的一個文本流偏移(就是'x'在字符串中的位置,比如文本為 axbx,那么x的偏移為{1, 3},最后你需要獲得1或者3,概率分別為1/2
假設文本流每個字符1字節)
============================基本問題=========================================
-----------------------------------------------------------------------------
已知等概率的0-1生產器uint make0~1(),求等概率的0~n生產器uint make0~n(uint n)
uint make0~n(uint n)
{
?? //...
?? //make0~1();
?? //...
?? return ?; //
}
具體什么方法使得make0~n最高效還是大家去想吧。。。
這里高效的意思是很快就能夠返回結果。
-----------------------------------------------------------------------------
=============================================================================
【方法一】
設共有N個字符.
for(;;)
{
??? 產生0~N-1的隨機數r
??? if(A[r]=='x') return r;
}
分析:因為0~N-1是等概率的,產生'x'的位置也是等概率的.
缺陷:循環return的概率是R/N (R為'x'的個數)
若N超級大R很小,for循環很難return.
【方法二】
1:vector<uint> a_pos;//從頭到尾記錄'x'出現的位置
2:這里有R==a_pos.size(). 產生0~R-1的隨機數r
3:取a_pos[r]即可。
缺陷:當R很大時空間開銷很大。
比方法一的優勢在于: 這個無須循環一下就可return.
【方法三】
先遍歷一遍字符串,記下有'x'出現的次數為R;
產生0~R-1 的隨機數 r
再遍歷一遍字符串獲取第r個'x'的位置
缺陷:最壞情況要遍歷兩遍字符串
優勢:克服了方法一和二的缺陷
=========面試官會問,還有沒有更好的方法=========================
【方法四】
有沒有只遍歷一遍就OK的方法呢?
直觀的,我們每碰到一個'x',設當前碰到的'x'的位置是i,我們可以以某個概率p去獲取這個
'x'。此時獲取'x'的概率絕對不是1/R,因為我們還不知道R的確切數字呢。
我們仍然需要繼續遍歷i之后的字符串,然后覆蓋之。。。
于是列個方程組(*),獲取第一個的概率是P(1),獲取第i個的概率是P(i),獲取第R個的概率是P(R)
滿足:
P(1)(1-P(2))(1-P(3))...(1-P(R))=1/R
P(2)(1-P(3))...(1-P(R))=1/R
P(3)(1-P(4))...(1-P(R))=1/R
...
P(R-1)(1-P(R))=1/R
P(R)=1/R
上述方程組的思想是:每個字符獲取的概率為1/R,遍歷到它時能獲取它,其后的所有該字符都不會獲取。
求P(i),得:
P(R)=1/R
P(R-1)=1/(R-1)
P(R-2)=1/(R-2)
...
P(2)=1/2
P(1)=1
于是乎,碰到第一個'x'就取,碰到第二個就以1/2的概率去取(若概率發生,就更新當前的'x'的位置),
...第R個'x'就以1/R的概率去取,于是乎完成操作。
優勢:遍歷一遍
缺陷:每碰到一個指定的字符都需要產生相應的隨機數(產生等概率的隨機數的最高效的算法是什么呢?)
注意了:對于一個文本流,在該流沒有流完之前,你不知道該流總共有多少個字符,你也不知道總共有多少個指定的字符,并且只流一次(不會再流一次),因此,方法一和方法三都不行不通。
http://www.cnblogs.com/DeadKnight/archive/2010/06/21/1762190.html
題:
給你一個字符串(可能有幾億個字符),給定一個特殊的字符'a',再給定一個可以產生0和1的隨機數發生器,然后讓你寫一個函數,等概率地返回'a'的一個索引(就是'a'在字符串中的位置,比如字符串為 aaba,那么a的索引為{0, 1, 3},等概率地返回0、1或者3)
想:
最簡單的想法:這個如果產生隨機數,再把該隨機數哈希到字符串長度范圍內,再把得到的哈希值作為下標去看對應的字符滿足不滿足條件,滿足條件則返回下標,不滿足條件的話重新隨機。缺點——如果滿足條件的字符很少,那么完全不靠譜!
稍微容易想到的方法:遍歷這個字符串,對滿足條件的下標建立索引,然后根據索引大小產生隨機數,隨機訪問索引,返回索引所對應的下標值。這個可以有,但是也有明顯缺點——如果滿足條件的字符很多,那么索引很大,占用大量存儲!
最后就是完美的解答了:
答:
#include <sstream> #include <iostream> #include <string> #include <time.h>using namespace std;int given_rand() {return rand() % 2; }unsigned int my_rand() {int i, result;for( i = 0, result = 0 ; i < 32 ; i++ ){result <<= 1;result += given_rand();}return result; }int main() { int index = 0, cnt = 0;stringstream strstring("aabbbaaabbbbabbbbabbbbbbbbbbbaabbbbbbb");int ret = 0;char c;srand(time(NULL));while (strstring >> c){index++;if (c == 'a') {++cnt;int rr = my_rand() % (cnt); if (rr < 1)ret = index;}}cout << ret;return 0; }
總結
以上是生活随笔為你收集整理的一次遍历等概率选取字符串中的某个字符的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 聚类Introducion
- 下一篇: 概率随机问题