Until recently, I knew that rand() in C++ works awfully with random_shuffle and it was not credible, but continued to use it for most tasks.. But recently I wrote some code and I cannot explain the result. Maybe you can?
I advise you to read to the end, it is really very interesting!
At each step, this code generates a random value from 0 to 1. As soon as 11 (for example) identical values equal 1 were received in a row, index number of current value is added to the vector. This operation is repeated a number of times and each time it starts from the beginning.
Let's output vector values and look at the last 15 of them for convenience.
Code here:
CODE#include <bits/stdc++.h>
using namespace std;
int main() {
srand(time(0));
vector<int> v;
for(int i = 0; i < 100; ++ i) {
int it = 0, tec = 0;
while(true) {
++ it;
if(tec == 11) {
v.push_back(it);
break;
}
if(rand() % 2) ++ tec;
else tec = 0;
}
}
for(int i = v.size() - 15; i < v.size(); ++ i)
cout << v[i] << endl;
}
OUTPUT16906 4109 125 9564 245 7841 10130 3685 265 272 9041 6916 4918 9028 13739
OUTPUT23685 265 272 9041 6916 4918 9028 13739 1796 133 7302 645 201 1207 192
OUTPUT316608 6906 4109 125 9564 245 7841 10130 3685 265 272 9041 6916 4918 9028
OUTPUT4245 7841 10130 3685 265 272 9041 6916 4918 9028 13739 1796 133 7302 645
At first glance it seems that the values are random. But let's try to sort the vector.
Code here:
CODE#include <bits/stdc++.h>
using namespace std;
int main() {
srand(time(0));
vector<int> v;
for(int i = 0; i < 100; ++ i) {
int it = 0, tec = 0;
while(true) {
++ it;
if(tec == 11) {
v.push_back(it);
break;
}
if(rand() % 2) ++ tec;
else tec = 0;
}
}
sort(v.begin(), v.end());
for(int i = v.size() - 15; i < v.size(); ++ i)
cout << v[i] << endl;
}
OUTPUT19041 9564 9564 9564 10130 10130 10130 10130 13739 13739 13739 13739 16608 16608 16608
OUTPUT29041 9041 9564 9564 9564 10130 10130 10130 13739 13739 13739 13739 16608 16608 16608
OUTPUT39564 9564 9564 9564 10130 10130 10130 10130 13739 13739 13739 16608 16608 16608 16608
OUTPUT49564 9564 9564 9564 10130 10130 10130 10130 13739 13739 13739 13739 16608 16608 16608
It should be noted that the output is different each time, but the individual values are the same. Moreover, these individual values are repeated three to four times.
Using of mt19937 rnd(chrono::steady_clock::now().time_since_epoch().count()); instead of rand() gives a random output array.
If we add a line that will not affect the answer in any way, but will affect the time difference between the rand() calls we also get random values in the output array. In doing so, we have to use random delay. If the delay is fixed, we will not get random values.
CODE1, rnd(), normal random values#include <bits/stdc++.h>
using namespace std;
mt19937 rnd(chrono::steady_clock::now().time_since_epoch().count());
int main() {
srand(time(0));
vector<int> v;
for(int i = 0; i < 100; ++ i) {
int it = 0, tec = 0;
while(true) {
++ it;
if(tec == 11) {
v.push_back(it);
break;
}
if(rnd() % 2) ++ tec;
else tec = 0;
}
}
sort(v.begin(), v.end());
for(int i = v.size() - 15; i < v.size(); ++ i)
cout << v[i] << endl;
}
CODE2, random delay between calls rand(), normal random values#include <bits/stdc++.h>
using namespace std;
int main() {
srand(time(0));
vector<int> v;
for(int i = 0; i < 100; ++ i) {
int it = 0, tec = 0;
while(true) {
//
int u = 1;
for(int j = 0; j < rand() % 10; ++ j) u *= 3;
//
++ it;
if(tec == 11) {
v.push_back(it);
break;
}
if(rand() % 2) ++ tec;
else tec = 0;
}
}
sort(v.begin(), v.end());
for(int i = v.size() - 15; i < v.size(); ++ i)
cout << v[i] << endl;
}
With a fixed delay, we also get magic values, even if the delay is large.
CODE3, fixed delay between calls rand(), magic values#include <bits/stdc++.h>
using namespace std;
int main() {
srand(time(0));
vector<int> v;
for(int i = 0; i < 100; ++ i) {
int it = 0, tec = 0;
while(true) {
//
int u = 1;
for(int j = 0; j < 10; ++ j) u *= 3;
//
++ it;
if(tec == 11) {
v.push_back(it);
break;
}
if(rand() % 2) ++ tec;
else tec = 0;
}
}
sort(v.begin(), v.end());
for(int i = v.size() - 15; i < v.size(); ++ i)
cout << v[i] << endl;
}
Now let's instead of an empty loop in the third code call rand().
CODE#include <bits/stdc++.h>
using namespace std;
int main() {
srand(time(0));
vector<int> v;
for(int i = 0; i < 100; ++ i) {
int it = 0, tec = 0;
while(true) {
//
rand();
//
++ it;
if(tec == 11) {
v.push_back(it);
break;
}
if(rand() % 2) ++ tec;
else tec = 0;
}
}
sort(v.begin(), v.end());
for(int i = v.size() - 25; i < v.size(); ++ i)
cout << v[i] << endl;
}
OUTPUT15447 5447 5447 5447 5447 5447 9664 9664 9664 9664 9664 9664 9664 9752 9752 9752 9752 9752 9752 13473 13473 13473 13473 13473 13473
OUTPUT25077 5447 5447 5447 5447 5447 5447 9664 9664 9664 9664 9664 9664 9752 9752 9752 9752 9752 9752 13473 13473 13473 13473 13473 13473
OUTPUT35447 5447 5447 5447 5447 5447 9664 9664 9664 9664 9664 9664 9752 9752 9752 9752 9752 9752 9752 13473 13473 13473 13473 13473 13473
Values become repeated much more often..
CODE#include <bits/stdc++.h>
using namespace std;
int main() {
srand(time(0));
vector<int> v;
for(int i = 0; i < 100; ++ i) {
int it = 0, tec = 0;
while(true) {
//
for(int j = 0; j < 2; ++ j) rand();
//
++ it;
if(tec == 11) {
v.push_back(it);
break;
}
if(rand() % 2) ++ tec;
else tec = 0;
}
}
sort(v.begin(), v.end());
for(int i = v.size() - 25; i < v.size(); ++ i)
cout << v[i] << endl;
}
OUTPUT9213 9213 9213 9213 9213 9213 9213 9213 9213 9213 9213 15676 15676 15676 15676 15676 15676 15676 15676 15676 15676 15676 15676 15676 15676
If we call the method twice, the values are repeated more often.
I believe that this is not quite a trivial algorithm and the result is rather strange. If you have any idea how to explain this, please state your thoughts in the comments. Thanks for reading!
My guess is: it's due to time(0). But I don't really know how or why :S
Checked without using srand(time(0)); You should understand that every time the output array will be the same. But the question is whether we get groups of identical values in a sorted vector.
Result:
9041 9041 9564 9564 9564 10130 10130 10130 13739 13739 13739 13739 16608 16608 16608
Therefore, srand(time(0)); does not affect the oddity of the result.
P.S. Using srand(time(nullptr)); instead of srand(time(0)); gives same result as using of srand(time(0));.
No, I mean, I know you need to seed the rand, but maybe there's a pattern on time(0) everytime you run it. I read that time(0) gets time in seconds since the Unix epoch, I thought it might depend on how codeforces or your computer get that value, but I think I'm wrong.
Tried to use srand(56164989), same result.
It isn't magic. It's small period of std::rand
So how I should write this code with rand() and without crutches to get a normal result?
Don't use std::rand :)
Why std::rand?
rand
is part of the C standard library, is it not? std:: prefix is not needed.Nevermind, apparently everything contained in libraries is part of the std namespace. But compilers are allowed to inject things in the global namespace as well.
You also shouldn't really expect any differences between CODE and CODE3. You are simply doing a loop that doesn't change the state of the PRNG.
The topic has been updated. If we call the method a fixed number of times
or
or
the repeating becomes more frequent. But if we use
values become random.
Can you explain this in some way?
Auto comment: topic has been updated by seo (previous revision, new revision, compare).
No magic at all here. That's just an implementation of
rand()
function.The only fact you should know that it is Linear congruential generator with some parameters.
So what you are saying (11 times odd numbers in a row) are actually just some equations which need to be satisfied, so you have not many solutions. You can increase this number to 17 odd numbers in a row and will eventually find that on Windows it will fail to find any number (I've waited for five minutes and haven't found any number). That means that no value satisfies the system of equations.
Very interesting. Thanks for the reply. I understood how it works approximately, but for me it remains unclear how to explain it exactly.