Practical PHP Programming

随机化

int rand ( [int min, int max])
int mt_rand ( [int min, int max])
int getrandmax ( )
int mt_getrandmax ( )
void srand ( [int seed])
void mt_srand ( [int seed])

有时候你需要在你的代码中添加一些随机的行为:在访问你网站的时候给出不同的欢迎语时;编写一个游戏时;或者在通过hash来保证数据安全时。任何一种情况下,随机化都是简单且有效的方式,而且仅仅需要两个函数:rand()mt_rang()

两个函数做的事情是一样的,需要的参数也是一样的,那这两者的区别在哪?好吧,rand()是一个基本的随机函数,速度很快,但并不是真正的“随机”:它产生的数字相对来说有可预见性。而mt_rand()则不同,,它更加复杂:“mt”部分的意思是“Mersenne Twister”,也就是它所使用的随机算法的名字。mt_rand()以牺牲速度为代价,产生更加“随机”的数字。

正如上面所提到的,二者都有相同的参数:两个参数都是可选的,分别是要返回随机数的下界和上界。如果不提供参数,PHP将会返回一个值在1到某个很大的数之间的随机数。下面来看个例子:

<?php
    $random = rand();
    $randrange = rand(1,10);
    $mtrandrange = mt_rand(1,100);
?>

注意,传入的两个数也是被包含在返回区间内的。也就是说$randrange的值可能是包含1和10在内的1到10之间的任何数字。

如果没有传递任何参数,直接调用rand()mt_rand()的话,PHP将会返回1到某个很大的数之间的值。想要知道rand()的最大的数的话,调用getrandmax()就可以了。对mt_rand()而言,对应的就是mt_getrandmax()

既然已经知道随机数是怎么回事了,就来看一个例子,访客访问网站的时候产生随机的问候语:

<?php
    switch(rand(1,6)) {
        case 1:
            $greet = 'Hello!'; break;
        case 2:
            $greet = 'Welcome!'; break;
        case 3:
            $greet = 'Greetings!'; break;
        case 4:
            $greet = 'Salutations!'; break;
        case 5:
            $greet = 'Good day!'; break;
        case 6:
            $greet = 'Yo!'; break;
    }
    print $greet;
?>

上面的代码中,我们在进入switch之前,并没有将rand()的结果赋值给一个变量,当然,为了便于阅读的话,你也可以那么做。

需要注意的是,随机化的速度和你传入的值的大小无关:对rand()函数而言,rand(1, 3)rand(1, 10000000)的返回速度是一样的。而mt_rand()的效率大约比rand()低50%,也就是说,只有你在有特殊需求的时候才应该用它。

为了说明这两个函数运行的多快,同是也为了说明传入值的大小对运行速度没有影响,来运行一下下面的代码:

<?php
    $START = time();
    for ($i = 1; $i < 1000000; ++$i) {
        $j = rand(1,100);
    }
    $END = time() - $START;
    print "Short rand() took $END seconds\n";

    $START = time();
    for ($i = 1; $i < 1000000; ++$i) {
        $j = mt_rand(1,100);
    }
    $END = time() - $START;
    print "Short mt_rand() took $END seconds\n";

    $START = time();
    for ($i = 1; $i < 1000000; ++$i) {
        $j = rand(1,10000000);
    }
    $END = time() - $START;
    print "Long rand() took $END seconds\n";

    $START = time();
    for ($i = 1; $i < 1000000; ++$i) {
        $j = mt_rand(1,10000000);
    }
    $END = time() - $START;
    print "Long mt_rand() took $END seconds\n"
?>

大多数随机数产生器需要一个“种子”:因为产生的随机数并不是真正的随机,因此需要用一个初始值来进行初始化。这叫做伪随机,意思是它们实际上并不是随机的。种子的值用来产生第一个随机数,第一个随机数用来产生第二个随机数,第二个用来产生第三个,依此类推。也就是说,如果你提供的是相同的种子的话,你得到的将是一串“相同”的随机数。

这实际上也是一种优势。很多年前,在BBC Micro上有一个叫Elite的流行的游戏,其中的玩家被允许在一个由8个星系组成的大的宇宙中飞行,每个星系有数以千计的恒星系统,每个恒星系统里面有确定数目的行星和独特的经济情况等。整个宇宙需要被装到22K大小的内存中。这是怎么实现的呢?很简单:每次给系统的随机数发生器提供相同的种子,这样每次产生的宇宙就是一模一样的了。

如果你想每次都产生出一串相同的随机数的话,只要将种子设定为一个固定值就可以了。例如,下面的这段代码不管你怎么运行,得到的永远是相同的“随机”数:

<?php
    mt_srand(123456);
    echo mt_rand(1, 100), "\n";
    echo mt_rand(1, 100), "\n";
    echo mt_rand(1, 100), "\n";
?>

如果你没有设定随机数发生器的话,PHP将会给你设定一个。