Helios is right... but there is more.... If we only cared about the distribution in space there would be no problem! However we care about the distribution in time too... Every method one can use to get a random number from min to max, heavily depends on the implementation of the rand() function. Let's say that rand() works giving those 10 numbers periodically:
{0,1,2,3,4,5,6,7,8,9}
in that case rand()%2 would give something like:
{0,1,0,1,0,1,0,1,0,1} (uniform distribution in both time and space, nice!)
while (rand()*2)/9 would give:
{0,0,0,0,0,1,1,1,1,1} (uniform distribution only in space, I don't like this...)
on the other hand, if rand() was using a table like:
{0,2,4,6,8,1,3,5,7,9}
the results for the two methods would be:
{0,0,0,0,0,1,1,1,1,1} for rand()%2 and
{0,0,0,1,1,0,0,1,1,1} for (rand()*2)/9
So, the thing is (and Stroustrup himself says that in his book, you know the one with the ocean wave and the lime colored C++ logo on the front cover) that (rand()*n)/RAND_MAX gives better results (but ofc with the same space distribution) than rand()%n in most implementations of the rand() function.
After making these thoughts it occured to me:
"maybe our problem could be changed into finding a rand() implementation that works fine with rand()%n" so I got down to work and came up with this one:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
|
#include <iostream>
#include <cstdlib>
using namespace std;
class RNG //random number generator
{
public:
RNG(unsigned int sp=5u, unsigned int bp=7u){seed_seq=0; init(sp,bp);}
~RNG(){if (seed_seq) delete[]seed_seq;}
unsigned int rand()
{
if (cur_seed >= big_prime) cur_seed%=big_prime;
unsigned int ret=(small_prime*(cur_seed))%big_prime;
cur_seed=seed_seq[cur_seed];
return ret;
}
unsigned int srand(unsigned int s)
{
cur_seed=s;
return rand();
}
void init(unsigned int sp, unsigned int bp)
{
small_prime=sp;
big_prime=bp;
cur_seed=0;
if (seed_seq) delete[] seed_seq;
seed_seq=new unsigned int[bp];
int i;
for (i=0; i<bp/2; i++)
{
seed_seq[i]=bp-i-1;
}
for (i=bp-1; i>bp/2; i--)
{
seed_seq[i]=bp-i;
}
seed_seq[bp/2]=0;
//for (i=0; i<bp; i++)
// cout << "seed[" << i << "]=" << seed_seq[i] << endl;
}
private:
unsigned int small_prime;
unsigned int big_prime;
unsigned int * seed_seq;
unsigned int cur_seed;
};
int main()
{
RNG rng;
int i;
unsigned int p, bp,n;
unsigned int *frequency;
unsigned int rnd;
while (true)
{
system("cls");
cout << "enter a small prime, a big prime and n (for rand()%n operation): ";
cin >> p >> bp >> n;
rng.init(p,bp);
frequency=new unsigned int[n];
memset(frequency,0,n*sizeof(int));
cout << "{ ";
for (i=0; i<bp; i++)
{
rnd=rng.rand()%n;
cout << rnd << ' ';
frequency[rnd]++;
}
cout << '}' << endl;
for (i=0; i<n; i++)
{
cout << "frequency of " << i << ": " << frequency[i] << endl;
}
delete[] frequency;
cout << "do it again? (0->no, 1->yes) ";
cin>>i;
if (i==0) break;
}
system("pause");
return 0;
}
|
I tried 5 7 2, 5 13 2, 13 17 2, 79 89 3 and more and everything was fine! ^^
Here is a site with primes so you can do your tests:
http://www.prime-numbers.org/prime-number-1-5001.htm