Jump to content

rand and mt_rand aren't random on win32!


fizix

Recommended Posts

I hope somebody can help me with this because I'm at wit's end:

 

I have a script that generates a random number. I don't want it to ever generate the same random number twice so I'm seeding the rand function with an incrementing number. However, after a certain point, it generates a duplicate random number anyway. Here's the function to prove it:

 

$test = array();

set_time_limit(200);

for ($i=10;$i<100000;$i++) {
srand($i);
$number = rand();
if (in_array($number, $test)) {
	die("Found dupe at iteration number $i<br />\nNumber is $number");
}
else {
	$test[] = $number;
}
  $runtime = time() - $starttime;
}

 

The output I get when running the script under Apache in Windows is this:

 

Found dupe at iteration number 32778

Number is 16507

 

However, when I run the script in a Linux environment I don't seem to have the problem (although it times out before it gets to the 200 second time limit). Anybody have any ideas how to fix this?

 

By the way, you'll need to have "suhosin.srand.ignore = Off" in your php.ini file to the code above.

Link to comment
Share on other sites

it is not windows or linux's problem, if you seed the random number generator the same number you'll get the same result multiple times or more frequently if that. because NO computer generated number is completely random, and relies on certain factors which change rapidly for example system ticks. miliseconds of script running, miliseconds of server uptime, anything which changes constantly and rapidly is usually what seeds a random number, seeding an incrementing number will not yield too much of a random number array since the seeds are very very close together therefore you'll more than likely run into same numbers output, but then again logically.. random numbers could return all the same number every time.. just pretty rare.. so running into duplicate results isn't that unlikely

Link to comment
Share on other sites

When finding a duplicate, just subtract 1 from $i and move on. That'll give you the required number of random integers.

 

Unfortunately I can't do this because I'll have multiple scripts running at the same time and none of them should be generating the same number. I can't store all the numbers in a database either... the database would get way too big way too quickly. Does anybody know of a way to generate a random number, without duplicates, and not log all the numbers that have already been generated?

Link to comment
Share on other sites

if you need something unique for your numbers.. generate an md5 hash for each integer :)

 

That didn't help.  :( I changed my test function to this:

 

$test = array();

$starttime = time();

for ($i=0;$i<100000;$i++) {
srand($i);
if ($runtime % 30 == 0) set_time_limit(200);
$number = md5(rand());
if (in_array($number, $test)) {
	die("Found dupe at iteration number $i<br />\nNumber is $number");
}
else {
	$test[] = $number;
}
  $runtime = time() - $starttime;
}

 

And got:

 

Found dupe at iteration number 32768

Number is 827ccb0eea8a706c4c34a16891f84e7b

Link to comment
Share on other sites

if you need something unique for your numbers.. generate an md5 hash for each integer :)

 

That didn't help.  :( I changed my test function to this:

 

$test = array();

$starttime = time();

for ($i=0;$i<100000;$i++) {
srand($i);
if ($runtime % 30 == 0) set_time_limit(200);
$number = md5(rand());
if (in_array($number, $test)) {
	die("Found dupe at iteration number $i<br />\nNumber is $number");
}
else {
	$test[] = $number;
}
  $runtime = time() - $starttime;
}

 

And got:

 

Found dupe at iteration number 32768

Number is 827ccb0eea8a706c4c34a16891f84e7b

 

Well then you could md5 it with random salts. Example:

 

$test = array();

$starttime = time();

for ($i=0;$i<100000;$i++) {
srand($i);
if ($runtime % 30 == 0) set_time_limit(200);
$salt1 = rand();
        srand($i + rand());
        $salt2 = rand();
        srand($i - rand());
        $number = md5($salt1 . rand() . $salt2);
if (in_array($number, $test)) {
	die("Found dupe at iteration number $i<br />\nNumber is $number");
} else {
	$test[] = $number;
}
  $runtime = time() - $starttime;
}

Link to comment
Share on other sites

Does anybody know of a way to generate a random number, without duplicates, and not log all the numbers that have already been generated?

 

If it is truely a random number then it will inevitably have duplicates.

 

Does it need to be a large and complex number? Or would just a count work? You could have a table with a single integer autonumber column and just do an insert (of NULL) and then get the last inserted key (in Oracle you could just use a sequence, but MySQL doesn't support them). To stop the table just delete old inserts regularly (possibly triggered by inserts to the table).

 

If you want to mix it up a bit then just hash that number.

 

All the best

 

Keith

Link to comment
Share on other sites

Does anybody know of a way to generate a random number, without duplicates, and not log all the numbers that have already been generated?

 

If it is truely a random number then it will inevitably have duplicates.

 

Does it need to be a large and complex number? Or would just a count work? You could have a table with a single integer autonumber column and just do an insert (of NULL) and then get the last inserted key (in Oracle you could just use a sequence, but MySQL doesn't support them). To stop the table just delete old inserts regularly (possibly triggered by inserts to the table).

 

If you want to mix it up a bit then just hash that number.

 

All the best

 

Keith

 

Wouldn't this basically do the same thing as my for loop?

Link to comment
Share on other sites

Hi

 

No, because it appears you are just using your loop to test when the number as a seed causes a duplicate.

 

If you just use the id then no need to use it as a seed or anything. Just use it as it is.

 

Why does the number need to be random? The suggestion of uniqueid() appears to give you exactly what you want without any need for any extra random number generation.

 

All the best

 

Keith

Link to comment
Share on other sites

if you need something unique for your numbers.. generate an md5 hash for each integer :)

 

That didn't help.  :( I changed my test function to this:

 

$test = array();

$starttime = time();

for ($i=0;$i<100000;$i++) {
srand($i);
if ($runtime % 30 == 0) set_time_limit(200);
$number = md5(rand());
if (in_array($number, $test)) {
	die("Found dupe at iteration number $i<br />\nNumber is $number");
}
else {
	$test[] = $number;
}
  $runtime = time() - $starttime;
}

 

And got:

 

Found dupe at iteration number 32768

Number is 827ccb0eea8a706c4c34a16891f84e7b

 

Well then you could md5 it with random salts. Example:

 

$test = array();

$starttime = time();

for ($i=0;$i<100000;$i++) {
srand($i);
if ($runtime % 30 == 0) set_time_limit(200);
$salt1 = rand();
        srand($i + rand());
        $salt2 = rand();
        srand($i - rand());
        $number = md5($salt1 . rand() . $salt2);
if (in_array($number, $test)) {
	die("Found dupe at iteration number $i<br />\nNumber is $number");
} else {
	$test[] = $number;
}
  $runtime = time() - $starttime;
}

 

Still didn't work.  :'(

 

Found dupe at iteration number 32768

Number is 40be5f590f625b8cff8072df0026756d

Link to comment
Share on other sites

Hi

 

No, because it appears you are just using your loop to test when the number as a seed causes a duplicate.

 

If you just use the id then no need to use it as a seed or anything. Just use it as it is.

 

Why does the number need to be random? The suggestion of uniqueid() appears to give you exactly what you want without any need for any extra random number generation.

 

All the best

 

Keith

 

uniqueid gives me no control over the length of the string I'm generating, nor does it guarantee that I would have duplicates.

Link to comment
Share on other sites

Hi

 

What do you need the string for that you need to control the length of it? Does it need to be random rather than just unique?

 

If you truely need a unique and random id of a specified length then I think you will have to do it by making a random one up and then checking it hasn't been used.

 

All the best

 

Keith

Link to comment
Share on other sites

Hi

 

What do you need the string for that you need to control the length of it? Does it need to be random rather than just unique?

 

If you truely need a unique and random id of a specified length then I think you will have to do it by making a random one up and then checking it hasn't been used.

 

All the best

 

Keith

 

Yes, it needs to be random and unique. I'm basically generating a bunch of random IP addresses and I don't want to generate the same one twice. I can't keep a list of addresses I've already generated because I'll have multiple processes running at the same time and the database containing the addresses would get WAY too big WAY too quickly (trust me, I've already tried).

Link to comment
Share on other sites

you can use array_unique on your array of random numbers. Random number generators will run into duplicate entries at some point, so perhaps randomly generated numbers isn't what you want.

 

also uniqueid() seems to be exactly what you want as people have said.. why dont you use that?

Link to comment
Share on other sites

you can use array_unique on your array of random numbers. Random number generators will run into duplicate entries at some point, so perhaps randomly generated numbers isn't what you want.

 

also uniqueid() seems to be exactly what you want as people have said.. why dont you use that?

 

Read UP ^

Link to comment
Share on other sites

Attempt 2:

 

$test = array();

$starttime = time();

for ($i=0;$i<100000;$i++) {
srand($i.strtotime(date()));
if ($runtime % 30 == 0) set_time_limit(200);
        $number = rand();
if (in_array($number, $test)) {
	$i--;
                //die("Found dupe at iteration number $i<br />\nNumber is $number");
} else {
	$test[] = $number;
}
  $runtime = time() - $starttime;
}

 

Should work as expected.

Link to comment
Share on other sites

Hi

 

If it a tricky one then.

 

IP address is just 4 numbers each from 0 to 255, so just really a 4 byte unsigned integer (or 4 unsigned tinyints). Not that large to store lots (you could store evey one possible in about 16gb). However possibly not manageable to search them each time.

 

Afraid I am not sure I can see a solution without storing them to check against duplicates.

 

All the best

 

Keith

Link to comment
Share on other sites

Hi

 

If it a tricky one then.

 

IP address is just 4 numbers each from 0 to 255, so just really a 4 byte unsigned integer (or 4 unsigned tinyints). Not that large to store lots (you could store evey one possible in about 16gb). However possibly not manageable to search them each time.

 

Afraid I am not sure I can see a solution without storing them to check against duplicates.

 

All the best

 

Keith

 

I think you're right... I give up.  :'(

Link to comment
Share on other sites

Hi

 

If you could put up with them being non random it would be possible.

 

All the best

 

Keith

 

I don't want to create them sequentially but maybe if I assigned each process a group of IP addresses (say 2000 at a time) and then told it to select from that group at random and removed each address I tried from the group as I went it would still be quasi-random and they would all be unique...

Link to comment
Share on other sites

This thread is more than a year old. Please don't revive it unless you have something important to add.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.