For N samples, how can we take M samples uniformly and randomly? That is, each of the N samples can have a probability of M/N being taken.
1. Simple insert sampling
This is the most basic and intuitive method. Insert 1 ~ in an initially empty set ~ A random integer of n. The number is M. However, this method has a weakness: When you want to insert a number, you can determine whether the number exists in the set. If it exists, you need to continue sampling until you get a number that is not in the original set, repeated sampling requires a great deal of overhead, and the higher the overhead is later.
2. Floyd sampling (inserts the nth number into the association with the first n-1 number of samples)
Floyd sampling was proposed by the famous Robert W. Floyd. The basic idea is that M-1 samples has been randomly taken out in [1, n-1] intervals, a 1 ~ N random number. If the number falls into M-1 sample, n is added to the sample set. If n is not included in the original m-1 sample, the random number is added to the acquired sample set. The complexity of this algorithm is O (m)
Proof: for the nth number, the probability of being selected is 1/n + (m-1)/n = M/N;
For any number in the first n-1 number, the total probability P = the probability of the previous round selected + the probability of the previous round not selected * The total probability of the current round selected:
Easy to get p = (m-1)/(n-1) * 1 + (n-M)/(n-1) * 1/(n + 1) = M/N.
Recursive Implementation:
- Function sample (m, n) // programming Chapter 6 Floyd algorithm
- If M = 0 then
- Return the empty set;
- Else
- S: = sample (M-1, N-1)
- T: = randint (1, N)
- If t is not in S then
- Insert T in S
- Else
- Insert N in S
- Return
- The probability of inserting N into S is (M-1 + 1)/n = M/N;
- Void floysampling (int n, int m) {If (M> N) {printf ("error \ n"); return;} vector <int> q; for (Int J = N-m; j <n; ++ J) {int T = rand () % (J + 1); ++ t; vector <int>:: iterator t_position = find (Q. begin (), Q. end (), T); If (t_position = Q. end () Q. push_back (t); else Q. push_back (J + 1);} For (vector <int>: iterator iter = Q. begin (); iter! = Q. End (); ++ ITER) printf ("%-3D", * ITER); cout <Endl ;}
3. There is a sampling method associated with Baidu Yiyi's pen questions.
Original Baidu Test Question: To analyze user behavior, the system usually needs to store some of the user's queries. However, because of the large number of queries, the system cannot store all of them. The system only saves m of queries every day, an algorithm is designed to randomly select M queries requested by the user. Please give a solution so that the probability of each query being extracted is equal and analyzed. Note: At the last moment, I do not know the user's total Request volume.
Answer policy: obtain a random number in [1, m + I]. If the random number falls on (M, M + I], the original number of m should be retained. If the random number falls on [1, m], replace the random number in [1, m] with the latest record.
The proof is as follows:
1) assuming that the system reads the n + 1 record, the M records currently stored are all stored with the M/(m + n) probability in the previous m + n records;
2) Take a random number [1, m + n + 1] and follow the preceding rules.
3) the probability that new records can be retained in the M array is m/(m + n + 1)
4) the probability that the number (set to a) in the original M array can be retained in this round of selection (the condition is that A is retained in the previous selection ):
(N + 1)/(m + n + 1) + M/(m + n + 1) * (1-1/m) = (m + n) /(m + n + 1 ).
Then multiply it by the probability it retains. The probability that a is still in the M array is m/(m + n + 1 ).
Such a loop always ensures that the probability of each number being selected is equal. The complexity of this algorithm is O (n)
- Void baidusampling (int n, int m) {If (M> N) {printf ("error \ n"); return;} vector <int> V; for (INT I = 1; I <= m; ++ I) {v. push_back (I) ;}for (INT I = m + 1; I <= N; ++ I) {int T = rand () % (I-1); + + T; if (T <= m) V [T-1] = I;} For (vector <int>: iterator iter = v. begin (); iter! = V. End (); ++ ITER) printf ("%-3D", * ITER); cout <Endl ;}
4. Random Sequence
Sometimes, we not only want to randomly select m samples from N samples, but also want the order of m samples to be random. Obviously, the sequence obtained after simple insertion and sampling is random. The order obtained by the following two methods is not random. In each insert sample of Floyd, if the random number is in the original set, the maximum number is always inserted at the end of the container, which is the main cause of random order. We put it behind the random number. The above is an intuitive description. For the true and rigorous proof, see Chapter 13 of programming Pearl 2.
The third method is not random mainly because the first M sample sequence is not random.
Modify the Floyd algorithm code to obtain an algorithm that generates a random sequence:
- Void generaterandomseries (int n, int m) {If (M> N) {printf ("error \ n"); return;} deque <int> q; for (Int J = N-m; j <n; ++ J) {int T = rand () % (J + 1); ++ t; deque <int>:: iterator t_position = find (Q. begin (), Q. end (), T); If (t_position = Q. end () Q. push_front (t); elseq. insert (t_position +, J + 1);} For (deque <int >:: iterator iter = Q. begin (); iter! = Q. End (); ++ ITER) printf ("%-3D", * ITER); cout <Endl ;}
Equiprobability sampling Floyd algorithm (conversion)