Problem description:
0 ~ An ordered list of M random integers in the n-1 range, which cannot be repeated. m <= n.
Given that the value of N may be large, the random number that can be returned by the random number generator provided by C/C ++ is usually [0, rand_max], where rand_max is 0x7fff. That is to say, only 15 digits are random. Therefore, we need to have our own random number generator so that we can return more random numbers, usually 30 digits. The following functions can meet our requirements:
Int bigrand ()
{
Return (rand_max * rand () + rand ());
}
Note: here you can also shift the return value of the First Rand () to the left by 15 digits, and then add the return value of the second rand () to speed up.
Solution 1,
Next, we can use this random number generator to solve the above problem. Do you think of any solution here. The following method comes from algorithm master Donald knuth.
Void genknuth (int m, int N)
{
Srand (unsigned INT) Time (null ));
For (INT I = 0; I <n; I ++)
{
If (RAND () % (n-I) <m)
{
Cout <I <"/N ";
M --;
}
}
}
The srand statement is added to obtain different random number sequences at each run.
I don't know if this method is unexpected. It's only four sentences in the for loop. However, these four statements have perfectly solved the above-mentioned problems, such as order, interchange, and m count.
This method scans 0 ~ The random numbers generated by each number within the n-1 range are ordered and mutually exclusive.
How can we ensure the number of M instances? There is no loop-like statement to control the number of random numbers.
Remember, program statements are just a form of expression for implementing a certain function. That is to say, we need to implement a function in many forms. Next we will analyze how this method ensures that M random numbers can be generated.
We can see that all the generated random numbers are output in the IF statement. That is to say, the IF statement controls the number of random numbers. Therefore, it must be noted that M random numbers can be generated, as long as the if statement can be executed only m times.
First, if statements can be executed up to m times without launching a for loop, because each execution of the IF statement will reduce the value of M by 1. When m = 0, if statement will not be executed again. Because (RAND () % (n-I) is a non-negative number.
Second, we analyze how many times the if statement can be executed at least. If the if statement is not executed at the beginning, (n-I) decreases until it is equal to M. At this time (RAND () % (n-I) must be smaller than (n-I), that is, M, so that the if statement can be executed once. At this time, the for loop has been executed (n-m + 1) times, And m-1) times are left before the launch loop, so that the if statement can be executed m times, then the if statement for the next (S-1) loop must be executed. What is the truth? When the first if statement starts to be executed, (n-I) = M. After execution, both (n-I) and M are reduced by 1, so that there are still (n-I) = m is set up. This process continues until m = 0. It's exactly the same as the 1-1 time.
Therefore, we can believe that the if statement can indeed be executed and only M can be executed.
Donald knuth is really amazing!
By the way, it is said that the first place is none of the members of a program design competition during the university.
Solution 2: Insert a random integer into a set that is initially empty until enough integers exist.
The pseudocode is as follows:
Initialize set S to empty
Size = 0
While size <m do
T = bigrand () % N
If t is not in S
Insert T into S
Size ++
Print the elements of S in sorted order
This algorithm ensures that all elements have the same selection probability when selecting elements, and its output is random.
Void genknuth (int m, int N)
{
Set <int> S;
While (S. Size () <m)
S. insert (bigrand () % N );
Set <int>: iterator I;
For (I = S. Begin (); I! = S. End (); ++ I)
Cout <* I <"/N ";
}
The C ++ Standard Template Library Specification ensures that each insert operation is completed within O (log m) time, and the internal iteration of the set requires O (m ), therefore, the entire program requires O (m log (m) (M is smaller than N ). However, this data structure consumes a large amount of space.
Solution 3,
For I = [0, n)
Swap (I, randint (I, n-1 ))
This is to mess up data X [0... n-1]. In this case, only the M elements in front of the array need to be stirred up.
Int bigrand ()
{
Return rand_max * rand () + rand ();
}
Int randint (int l, int U)
{
Return L + bigrand () % (u-L + 1 );
}
Void genknuth (int m, int N)
{
Int I, J;
Int * x = new int [N];
For (I = 0; I <n; I ++)
X [I] = I;
For (I = 0; I <m; I ++)
{
J = randint (I, n-1 );
Swap (X [I], X [J]);
}
Sort (x, x + M );
For (I = 0; I <m; I ++)
Cout <X [I] <"/N ";
}
Int main ()
{
Srand (unsigned) Time (null ));
Int M, N;
While (1)
{
Cout <"Please enter M and N :";
Cin> m> N;
Genknuth (m, n );
}
Return 0;
}
The algorithm uses n characters of memory and requires O (N + mlogm) time.
Solution 4: (recursion)
Void randselect (int m, int N)
{
Assert (M> = 0 & M <= N );
If (M> 0)
If (bigrand () % N) <m)
{
Randselect (m-1, n-1 );
Cout <n-1 <",";
}
Else
Randselect (M, n-1 );
}
Original article:
Http://blog.csdn.net/ztj111/archive/2007/10/23/1840190.aspx
Http://hi.baidu.com/xwf_like/blog/item/ff2a9b98293022006e068c2d.html