php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #12455 Srand and shuffle give odd results
Submitted: 2001-07-29 05:51 UTC Modified: 2001-07-31 12:10 UTC
From: sulka at sulake dot com Assigned:
Status: Not a bug Package: *Math Functions
PHP Version: 4.0.4pl1, 4.0.6 OS: All
Private report: No CVE-ID: None
 [2001-07-29 05:51 UTC] sulka at sulake dot com
I'm using the following code to create random strings 
(passwords):

$password = "";
$array = 
array('a','b','c','d','e','f','g','h','i','j','k','l','m','
n','o','p','q','r','s','t','u','v','w','x','y','z',0,1,2,4,
5,6,7,8,9);
srand ((double)microtime()*1000000);
shuffle(&$array);
for ($i=0; $i<6; $i++) { $password .= $array[$i]; }

Now, for some reason this seems to return only five 
different values ever on the Solaris machine I'm running 
the code on. And I'm not checking on the same run of the 
script, this is based on accessing page with the script 
through http and looking at the results for about 150 
reloads on two different days.

This is the first time I'm sending a PHP report so I don't 
know exactly what information to provide. Please ask for 
more if as you see fit.

Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2001-07-29 14:26 UTC] rasmus@php.net
I don't think I understand what the problem is here.  I tested your code with the following:

<?
function pwd() {
$password = "";
$array = array('a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z',0,1,2,4,5,6,7,8,9);
srand ((double)microtime()*1000000);
shuffle(&$array);
for ($i=0; $i<6; $i++) { $password .= $array[$i]; }
return $password;
}

$i=0;
while($i<500) {
    $password = pwd();
    $a[$password] = $password;
    $i++;
}
echo count($a);
?>

This always returns 500 which means that 500 unique passwords were always created.
 [2001-07-29 16:41 UTC] sulka at sulake dot com
Well, when I run that code I get 4, not 500. Upping the 
number of iterations doesn't help. I think the problem is 
with the particular build (extension/OS combination). Any 
way to debug this?
 [2001-07-30 05:17 UTC] sulka at sulake dot com
A friend tried to run the test code on his machine and also 
got 4 as a result. This is a Linux machine so my original 
guess of this being Solaris specific isn't true. The only 
real common denominator seems to be the PHP version? I'm 
trying to get an upgrade to see if this is happening under 
4.0.6.

Here's more info on the two servers this is occuring on:

PHP Version: 4.0.4pl1

System: 
Linux xxx.com 2.2.14 #2 SMP Tue Mar 14 14:42:34 PST 2000 
i686 unknown
Configure Command:  './configure' 
'--with-apxs=/usr/local/apache/bin/apxs' '--with-xml' 
'--with-mcrypt' '--with-mysql' '--enable-track-vars'
Server API: Apache

System:	SunOS yyy.com 5.8 Generic_108528-02 sun4u sparc 
SUNW,UltraSPARC-IIi-cEngine
Configure Command:	'./configure' 
'--with-apache=../apache_1.3.14' 
'--with-mysql=/usr/local/mysql' '--with-ldap=/usr/local' 
'--with-db3=/usr/local/BerkeleyDB.3.2' '--with-gd=../gd1.5' 
'--with-ttf=/usr/local' '--enable-wddx'


 [2001-07-30 06:25 UTC] sulka at sulake dot com
The same problem also occurs on PHP 4.0.6 on Darwin/PPC. 
System info:

PHP Version 4.0.6
System: Darwin localhost 1.3.7 Darwin Kernel Version 1.3.7: 
Sat Jun??9 11:12:48 PDT 2001; 
root:xnu/xnu-124.13.obj~1/RELEASE_PPC??Power Macintosh 
powerpc
Configure Command   './configure' '--with-mysql=/usr/local' 
'--with-apxs=/usr/sbin/apxs' '--with-zlib=/usr'
Server API: Apache
Apache Version: Apache/1.3.20

 [2001-07-31 10:00 UTC] jflemer@php.net
Try moving your srand() call outside of the pwd() function. That should fix it. The posted script returns 4 for me on Solaris 5.8, and 500 when I move the srand() outside of pwd().

srand() should only be called *once*.

Reopen this if that does not fix it.
 [2001-07-31 10:20 UTC] sulka at sulake dot com
Ok, here's sample code that should wake you up. The problem 
occurs even when srand is only called once / page load.

Create two PHP pages:

password.php

<?PHP
$password = "";
$array = 
array('a','b','c','d','e','f','g','h','i','j','k','l','m','
n','o','p','q','r','s','t','u','v','w','x','y','z',0,1,2,4,
5,6,7,8,9);
srand ((double)microtime()*1000000);
shuffle(&$array);
for ($i=0; $i<6; $i++) { $password .= $array[$i]; }
echo $password;?>

load100times.php

<?PHP
$i=0;
while($i<100) {
	$file = fopen ("http://localhost/password.php", "r");
    $password = fgets ($file, 128);
	fclose($file);
    $a[$password] = $password;
    $i++;
	}
echo "Count: " . count($a);
?>

Then load the load100times.php in your browser. The result 
should be 100 but is still 4 on the machines I'm testing 
on. I'm assuming srand is not ment to be run only once / 
server start?

 [2001-07-31 12:09 UTC] jflemer@php.net
OK, well can you check to see that microtime() is working for you? This script should print out 100.

<?php
$a = array();
for($i=0; $i<100; $i++)
{
  $a[((double)microtime()*1000000)]= 1;
}

echo count($a), "\n";
?>

BTW, your most recent example (calling a "remote" script) is essentially the same as calling the local function pwd(). You still are re-seeding the PRNG with each time you call the function. In order to produce a pseudo-random sequence you should only seed the PRNG once. This is a hard thing to do in the case where you generate one random number from the sequence per execution.

Here is something that works for me even though I call the srand() function inside the function (though the randomness is probably pretty poor). You might be able to adapt it in some way for your use. The idea here is that once you shuffle the array once, you use that as the starting point for the next shuffle.

<?php
$pass_array = array('a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z',0,1,2,4,5,6,7,8,9);

function pwd(&$pass_array) {
  srand ((double)microtime()*1000000);
  $password = '';
  shuffle(&$pass_array);
  for ($i=0; $i<6; $i++) { $password .= $pass_array[$i]; }
  return $password;
}

$a = array();
for($i=0; $i<500; $i++) {
	$a[pwd($pass_array)] = 1;
}

echo count($a), "\n";

This is not a *bug* (IMHO). This is a problem with your script, you might want to try this discussion on php-general@lists.php.net and see if people have more suggestions for you.
 [2001-07-31 12:10 UTC] jflemer@php.net
open -> bogus
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Thu Mar 28 23:01:26 2024 UTC