Main Page | Namespace List | Class Hierarchy | Alphabetical List | Data Structures | Directories | File List | Data Fields | Globals | Related Pages

rand.c

Go to the documentation of this file.
00001 /*
00002    +----------------------------------------------------------------------+
00003    | PHP Version 4                                                        |
00004    +----------------------------------------------------------------------+
00005    | Copyright (c) 1997-2003 The PHP Group                                |
00006    +----------------------------------------------------------------------+
00007    | This source file is subject to version 2.02 of the PHP license,      |
00008    | that is bundled with this package in the file LICENSE, and is        |
00009    | available at through the world-wide-web at                           |
00010    | http://www.php.net/license/2_02.txt.                                 |
00011    | If you did not receive a copy of the PHP license and are unable to   |
00012    | obtain it through the world-wide-web, please send a note to          |
00013    | license@php.net so we can mail you a copy immediately.               |
00014    +----------------------------------------------------------------------+
00015    | Authors: Rasmus Lerdorf <rasmus@php.net>                             |
00016    |          Zeev Suraski <zeev@zend.com>                                |
00017    |          Pedro Melo <melo@ip.pt>                                     |
00018    |          Sterling Hughes <sterling@php.net>                          |
00019    |                                                                      |
00020    | Based on code from: Shawn Cokus <Cokus@math.washington.edu>          |
00021    +----------------------------------------------------------------------+
00022  */
00023 /* $Id: rand.c,v 1.60.2.3 2004/01/19 03:16:04 sniper Exp $ */
00024 
00025 #include <stdlib.h>
00026 
00027 #ifdef PHP_WIN32
00028 # ifndef WIN32_LEAN_AND_MEAN
00029 #  define WIN32_LEAN_AND_MEAN
00030 # endif
00031 # include <windows.h>
00032 #endif
00033 
00034 #if defined(NETWARE) && !defined(NEW_LIBC)  /* For getpid() used below */
00035 #include "netware/pwd.h"
00036 #endif
00037 
00038 #include "php.h"
00039 #include "php_math.h"
00040 #include "php_rand.h"
00041 #include "php_lcg.h"
00042 
00043 #include "basic_functions.h"
00044 
00045 
00046 /* SYSTEM RAND FUNCTIONS */
00047 
00048 /* {{{ php_srand
00049  */
00050 PHPAPI void php_srand(long seed TSRMLS_DC)
00051 {
00052 #ifdef ZTS
00053         BG(rand_seed) = (unsigned int) seed;
00054 #else
00055 # if defined(HAVE_SRANDOM)
00056         srandom((unsigned int) seed);
00057 # elif defined(HAVE_SRAND48)
00058         srand48(seed);
00059 # else
00060         srand((unsigned int) seed);
00061 # endif
00062 #endif
00063 
00064         /* Seed only once */
00065         BG(rand_is_seeded) = 1;
00066 }
00067 /* }}} */
00068 
00069 /* {{{ php_rand
00070  */
00071 PHPAPI long php_rand(TSRMLS_D)
00072 {
00073         long ret;
00074 
00075         if (!BG(rand_is_seeded)) {
00076                 php_srand(GENERATE_SEED() TSRMLS_CC);
00077         }
00078 
00079 #ifdef ZTS
00080         ret = php_rand_r(&BG(rand_seed));
00081 #else
00082 # if defined(HAVE_RANDOM)
00083         ret = random();
00084 # elif defined(HAVE_LRAND48)
00085         ret = lrand48();
00086 # else
00087         ret = rand();
00088 # endif
00089 #endif
00090 
00091         return ret;
00092 }
00093 /* }}} */
00094 
00095 
00096 /* MT RAND FUNCTIONS */
00097 
00098 /*
00099    This is the ``Mersenne Twister'' random number generator MT19937, which
00100    generates pseudorandom integers uniformly distributed in 0..(2^32 - 1)
00101    starting from any odd seed in 0..(2^32 - 1).  This version is a recode
00102    by Shawn Cokus (Cokus@math.washington.edu) on March 8, 1998 of a version by
00103    Takuji Nishimura (who had suggestions from Topher Cooper and Marc Rieffel in
00104    July-August 1997).
00105   
00106    Effectiveness of the recoding (on Goedel2.math.washington.edu, a DEC Alpha
00107    running OSF/1) using GCC -O3 as a compiler: before recoding: 51.6 sec. to
00108    generate 300 million random numbers; after recoding: 24.0 sec. for the same
00109    (i.e., 46.5% of original time), so speed is now about 12.5 million random
00110    number generations per second on this machine.
00111   
00112    According to the URL <http://www.math.keio.ac.jp/~matumoto/emt.html>
00113    (and paraphrasing a bit in places), the Mersenne Twister is ``designed
00114    with consideration of the flaws of various existing generators,'' has
00115    a period of 2^19937 - 1, gives a sequence that is 623-dimensionally
00116    equidistributed, and ``has passed many stringent tests, including the
00117    die-hard test of G. Marsaglia and the load test of P. Hellekalek and
00118    S. Wegenkittl.''  It is efficient in memory usage (typically using 2506
00119    to 5012 bytes of static data, depending on data type sizes, and the code
00120    is quite short as well).  It generates random numbers in batches of 624
00121    at a time, so the caching and pipelining of modern systems is exploited.
00122    It is also divide- and mod-free.
00123   
00124    This library is free software; you can redistribute it and/or modify it
00125    under the terms of the GNU Library General Public License as published by
00126    the Free Software Foundation (either version 2 of the License or, at your
00127    option, any later version).  This library is distributed in the hope that
00128    it will be useful, but WITHOUT ANY WARRANTY, without even the implied
00129    warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
00130    the GNU Library General Public License for more details.  You should have
00131    received a copy of the GNU Library General Public License along with this
00132    library; if not, write to the Free Software Foundation, Inc., 59 Temple
00133    Place, Suite 330, Boston, MA 02111-1307, USA.
00134   
00135    The code as Shawn received it included the following notice:
00136   
00137      Copyright (C) 1997 Makoto Matsumoto and Takuji Nishimura.  When
00138      you use this, send an e-mail to <matumoto@math.keio.ac.jp> with
00139      an appropriate reference to your work.
00140   
00141    It would be nice to CC: <Cokus@math.washington.edu> when you write.
00142   
00143 
00144   
00145    php_uint32 must be an unsigned integer type capable of holding at least 32
00146    bits; exactly 32 should be fastest, but 64 is better on an Alpha with
00147    GCC at -O3 optimization so try your options and see what's best for you
00148 
00149    Melo: we should put some ifdefs here to catch those alphas...
00150 */
00151 #define N             MT_N                 /* length of state vector */
00152 #define M             (397)                /* a period parameter */
00153 #define K             (0x9908B0DFU)        /* a magic constant */
00154 #define hiBit(u)      ((u) & 0x80000000U)  /* mask all but highest   bit of u */
00155 #define loBit(u)      ((u) & 0x00000001U)  /* mask all but lowest    bit of u */
00156 #define loBits(u)     ((u) & 0x7FFFFFFFU)  /* mask     the highest   bit of u */
00157 #define mixBits(u, v) (hiBit(u)|loBits(v)) /* move hi bit of u to hi bit of v */
00158 
00159 /* {{{ php_mt_srand
00160  */
00161 PHPAPI void php_mt_srand(php_uint32 seed TSRMLS_DC)
00162 {
00163         /*
00164            We initialize state[0..(N-1)] via the generator
00165 
00166              x_new = (69069 * x_old) mod 2^32
00167 
00168            from Line 15 of Table 1, p. 106, Sec. 3.3.4 of Knuth's
00169            _The Art of Computer Programming_, Volume 2, 3rd ed.
00170 
00171            Notes (SJC): I do not know what the initial state requirements
00172            of the Mersenne Twister are, but it seems this seeding generator
00173            could be better.  It achieves the maximum period for its modulus
00174            (2^30) iff x_initial is odd (p. 20-21, Sec. 3.2.1.2, Knuth); if
00175            x_initial can be even, you have sequences like 0, 0, 0, ...;
00176            2^31, 2^31, 2^31, ...; 2^30, 2^30, 2^30, ...; 2^29, 2^29 + 2^31,
00177            2^29, 2^29 + 2^31, ..., etc. so I force seed to be odd below.
00178 
00179            
00180            Even if x_initial is odd, if x_initial is 1 mod 4 then
00181 
00182              the          lowest bit of x is always 1,
00183              the  next-to-lowest bit of x is always 0,
00184              the 2nd-from-lowest bit of x alternates      ... 0 1 0 1 0 1 0 1 ... ,
00185              the 3rd-from-lowest bit of x 4-cycles        ... 0 1 1 0 0 1 1 0 ... ,
00186              the 4th-from-lowest bit of x has the 8-cycle ... 0 0 0 1 1 1 1 0 ... ,
00187               ...
00188 
00189            and if x_initial is 3 mod 4 then
00190 
00191              the          lowest bit of x is always 1,
00192              the  next-to-lowest bit of x is always 1,
00193              the 2nd-from-lowest bit of x alternates      ... 0 1 0 1 0 1 0 1 ... ,
00194              the 3rd-from-lowest bit of x 4-cycles        ... 0 0 1 1 0 0 1 1 ... ,
00195              the 4th-from-lowest bit of x has the 8-cycle ... 0 0 1 1 1 1 0 0 ... ,
00196               ...
00197 
00198            The generator's potency (min. s>=0 with (69069-1)^s = 0 mod 2^32) is
00199            16, which seems to be alright by p. 25, Sec. 3.2.1.3 of Knuth.  It
00200            also does well in the dimension 2..5 spectral tests, but it could be
00201            better in dimension 6 (Line 15, Table 1, p. 106, Sec. 3.3.4, Knuth).
00202 
00203            Note that the random number user does not see the values generated
00204            here directly since reloadMT() will always munge them first, so maybe
00205            none of all of this matters.  In fact, the seed values made here could
00206            even be extra-special desirable if the Mersenne Twister theory says
00207            so-- that's why the only change I made is to restrict to odd seeds.
00208         */
00209 
00210         register php_uint32 x = (seed | 1U) & 0xFFFFFFFFU, *s = BG(state);
00211         register int    j;
00212         
00213         for (BG(left) = 0, *s++ = x, j = N; --j;
00214                 *s++ = (x *= 69069U) & 0xFFFFFFFFU);
00215 
00216         /* Seed only once */
00217         BG(mt_rand_is_seeded) = 1;
00218 }
00219 /* }}} */
00220 
00221 /* {{{ php_mt_reload
00222  */
00223 static php_uint32 php_mt_reload(TSRMLS_D)
00224 {
00225         register php_uint32 *p0 = BG(state), *p2 = BG(state) + 2, *pM = BG(state) + M, s0, s1;
00226         register int    j;
00227 
00228         if (BG(left) < -1)
00229                 php_mt_srand(4357U TSRMLS_CC);
00230 
00231         BG(left) = N - 1, BG(next) = BG(state) + 1;
00232 
00233         for (s0 = BG(state)[0], s1 = BG(state)[1], j = N - M + 1; --j; s0 = s1, s1 = *p2++)
00234                 *p0++ = *pM++ ^ (mixBits(s0, s1) >> 1) ^ (loBit(s1) ? K : 0U);
00235 
00236         for (pM = BG(state), j = M; --j; s0 = s1, s1 = *p2++)
00237                 *p0++ = *pM++ ^ (mixBits(s0, s1) >> 1) ^ (loBit(s1) ? K : 0U);
00238 
00239         s1 = BG(state)[0], *p0 = *pM ^ (mixBits(s0, s1) >> 1) ^ (loBit(s1) ? K : 0U);
00240         s1 ^= (s1 >> 11);
00241         s1 ^= (s1 <<  7) & 0x9D2C5680U;
00242         s1 ^= (s1 << 15) & 0xEFC60000U;
00243 
00244         return s1 ^ (s1 >> 18);
00245 }
00246 /* }}} */
00247 
00248 /* {{{ php_mt_rand
00249  */
00250 PHPAPI php_uint32 php_mt_rand(TSRMLS_D)
00251 {
00252         php_uint32 y;
00253 
00254         if (--BG(left) < 0)
00255                 return php_mt_reload(TSRMLS_C);
00256 
00257         y  = *BG(next)++;
00258         y ^= (y >> 11);
00259         y ^= (y <<  7) & 0x9D2C5680U;
00260         y ^= (y << 15) & 0xEFC60000U;
00261 
00262         return y ^ (y >> 18);
00263 }
00264 /* }}} */
00265 
00266 /* {{{ proto void srand([int seed])
00267    Seeds random number generator */
00268 PHP_FUNCTION(srand)
00269 {
00270         long seed;
00271 
00272         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &seed) == FAILURE)
00273                 return;
00274 
00275         if (ZEND_NUM_ARGS() == 0)
00276                 seed = GENERATE_SEED();
00277 
00278         php_srand(seed TSRMLS_CC);
00279 }
00280 /* }}} */
00281 
00282 /* {{{ proto void mt_srand([int seed])
00283    Seeds Mersenne Twister random number generator */
00284 PHP_FUNCTION(mt_srand)
00285 {
00286         long seed;
00287 
00288         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &seed) == FAILURE) 
00289                 return;
00290 
00291         if (ZEND_NUM_ARGS() == 0)
00292                 seed = GENERATE_SEED();
00293 
00294         php_mt_srand(seed TSRMLS_CC);
00295 }
00296 /* }}} */
00297 
00298 
00299 /*
00300  * A bit of tricky math here.  We want to avoid using a modulus because
00301  * that simply tosses the high-order bits and might skew the distribution
00302  * of random values over the range.  Instead we map the range directly.
00303  *
00304  * We need to map the range from 0...M evenly to the range a...b
00305  * Let n = the random number and n' = the mapped random number
00306  *
00307  * Then we have: n' = a + n(b-a)/M
00308  *
00309  * We have a problem here in that only n==M will get mapped to b which
00310  # means the chances of getting b is much much less than getting any of
00311  # the other values in the range.  We can fix this by increasing our range
00312  # artifically and using:
00313  #
00314  #               n' = a + n(b-a+1)/M
00315  *
00316  # Now we only have a problem if n==M which would cause us to produce a
00317  # number of b+1 which would be bad.  So we bump M up by one to make sure
00318  # this will never happen, and the final algorithm looks like this:
00319  #
00320  #               n' = a + n(b-a+1)/(M+1) 
00321  *
00322  * -RL
00323  */    
00324 
00325 /* {{{ proto int rand([int min, int max])
00326    Returns a random number */
00327 PHP_FUNCTION(rand)
00328 {
00329         long min;
00330         long max;
00331         long number;
00332         int  argc = ZEND_NUM_ARGS();
00333 
00334         if (argc != 0 && zend_parse_parameters(argc TSRMLS_CC, "ll", &min, &max) == FAILURE)
00335                 return;
00336 
00337         number = php_rand(TSRMLS_C);
00338         if (argc == 2) {
00339                 RAND_RANGE(number, min, max, PHP_RAND_MAX);
00340         }
00341 
00342         RETURN_LONG(number);
00343 }
00344 /* }}} */
00345 
00346 /* {{{ proto int mt_rand([int min, int max])
00347    Returns a random number from Mersenne Twister */
00348 PHP_FUNCTION(mt_rand)
00349 {
00350         long min;
00351         long max;
00352         long number;
00353         int  argc = ZEND_NUM_ARGS();
00354 
00355         if (argc != 0 && zend_parse_parameters(argc TSRMLS_CC, "ll", &min, &max) == FAILURE)
00356                 return;
00357 
00358         if (!BG(mt_rand_is_seeded)) {
00359                 php_mt_srand(GENERATE_SEED() TSRMLS_CC);
00360         }
00361 
00362         /*
00363          * Melo: hmms.. randomMT() returns 32 random bits...
00364          * Yet, the previous php_rand only returns 31 at most.
00365          * So I put a right shift to loose the lsb. It *seems*
00366          * better than clearing the msb. 
00367          * Update: 
00368          * I talked with Cokus via email and it won't ruin the algorithm
00369          */
00370         number = (long) (php_mt_rand(TSRMLS_C) >> 1);
00371         if (argc == 2) {
00372                 RAND_RANGE(number, min, max, PHP_MT_RAND_MAX);
00373         }
00374 
00375         RETURN_LONG(number);
00376 }
00377 /* }}} */
00378 
00379 /* {{{ proto int getrandmax(void)
00380    Returns the maximum value a random number can have */
00381 PHP_FUNCTION(getrandmax)
00382 {
00383         if (ZEND_NUM_ARGS() != 0) {
00384                 WRONG_PARAM_COUNT;
00385         }
00386 
00387         RETURN_LONG(PHP_RAND_MAX);
00388 }
00389 /* }}} */
00390 
00391 /* {{{ proto int mt_getrandmax(void)
00392    Returns the maximum value a random number from Mersenne Twister can have */
00393 PHP_FUNCTION(mt_getrandmax)
00394 {
00395         if (ZEND_NUM_ARGS() != 0) {
00396                 WRONG_PARAM_COUNT;
00397         }
00398 
00399         /*
00400          * Melo: it could be 2^^32 but we only use 2^^31 to maintain
00401          * compatibility with the previous php_rand
00402          */
00403         RETURN_LONG(PHP_MT_RAND_MAX); /* 2^^31 */
00404 }
00405 /* }}} */
00406 
00407 /*
00408  * Local variables:
00409  * tab-width: 4
00410  * c-basic-offset: 4
00411  * End:
00412  * vim600: noet sw=4 ts=4 fdm=marker
00413  * vim<600: noet sw=4 ts=4
00414  */