Random

A Large Random Number Generator

I used a lot of time to make it, after I failed last time, I thought there are better ways to work around. Here is how it works:
1. Subtract the minimal value from the maximal value
2. Generate the numbers for the digits by using rand()
3. Combine the digits
4. Check if it is too large or not, if is, redo 2, if not,go on.
5. Add the minimal value to the random value.

In worst case, by chance, 10 random number need to be generated before we get a good one. Numbers like 100000, 10000.
In best case, only one will be generated, numbers like 99999, 9999
In average, one out of 5 generation, because average number are 500000

function bcrand($min, $max){
	bcscale(0);
	if(bccomp($max,$min)!=1){
		return 0;
	}
	$top = bcsub($max,$min);
	$rand = bcadd($top, 1);
	$length = strlen($top);
 
		$n = 0;
		while(9*$n <= $length){
			if($length - 9*$n >= 9){
				$rand_part[] = rand(0,999999999);
			}else{
				$j = 0; $foo = '';
				while($j < $length-9*$n){
					$foo .= '9';
					++$j;
				}
				$foo += 0;
				$rand_part[] = rand(0,$foo);
			}
			++$n;
		}
		$i = 0;
		$rand ='';
		$count = count($rand_part);
		while($i < $count){
			$rand .= $rand_part[$i];
			++$i;
		}
	while(bccomp($rand,$top)==1){
		$rand = substr($rand,1,strlen($rand)).rand(0,9);
	}
	return bcadd($rand,$min);
}

I saw another function, also called bcrand(), in PHPRPC[CHINESE], a implementation of RPC in PHP, you can find it in the bcmath.php inside the file. Here is the function for the people who can not read the Chinese website.

function bcrand($n, $s) {
    $lowBitMasks = array(0x0000, 0x0001, 0x0003, 0x0007,
                         0x000f, 0x001f, 0x003f, 0x007f,
                         0x00ff, 0x01ff, 0x03ff, 0x07ff,
                         0x0fff, 0x1fff, 0x3fff, 0x7fff);
 
    $r = $n % 16;
    $q = floor($n / 16);
    $result = '0';
    $m = '1';
    for ($i = 0; $i < $q; $i++) {
        $rand = mt_rand(0, 0xffff);
        if (($q - 1 == $i) and ($r == 0) and ($s == 1)) {
            $rand |= 0x8000;
        }
        $result = bcadd(bcmul($m, $rand), $result);
        $m = bcmul($m, '65536');
    }
    if ($r != 0) {
        $rand = mt_rand(0, $lowBitMasks[$r]);
        if ($s == 1) {
            $rand |= 1 << ($r - 1);
        }
        $result = bcadd(bcmul($m, $rand), $result);
    }
    return $result;
}

This version is different because it returns a random n-byte number, for example, bcrand(20,0) could return 634834.
I found mine is pretty fast :-)

Failed to make a large random number generator

I have tried to make a system that can generate a large random number, and I have encounter with problems. Here is the code that work when you first see it, but then you find there will be a great flaw in the system. I will show you step by step:

function bcrand($min, $max, $rand='rand'){
//ok, first, we subtract $max with $min
//because in the end we are going to
//add the number with $min
$rand_max = bcsub($max,$min);
$max_len = strlen($max);
 
 
 
$rand_num[0] = $rand(0,$max[0]);
  if($rand_num[0] == $max[0]){
    $ismax = 1;
}
 
$i = 1;
do{
  if($ismax){
    $rand_num[$i] = $rand(0,$max[$i]);
    if($rand_num[$i] == $max[$i]){
      $ismax = 1;
    }
  }else{
    $rand_num[$i] = $rand(0,9);
  }
  ++$i;
  usleep(mt_rand(0,10));
}while($i < $max_len);
 
 
//make the $rand_num array into a string
$i = 0;
do{
$return_num .= $rand_num[$i];
++$i;
}while($i<$max_len);
//add the returning number with the min
//number, it also take out the prefix zeros
$return_num = bcadd($return_num,$min);
return $return_num;
}

The concept is pretty easy, generate each digit of the number, and then put them together.
The first problem I meet is there could be a digit larger than the max number I specified. To solve that problem, I made the it generate the first digit equal or less than the max number's first digit first. and if the first digit is the max digit possible, the next digit will follow the same way.
This worked out fine, but if you really think about it, this is not right.
Suppose chose a number between 0 and 1000, if it choses the first digit, there is half of the chance of choosing 1, and lead to half of the chance of getting 1000, and half of the chance get the rest 999 numbers.
So I still have to work on that a bit more.

RandomLib - A class for randomness

This is the first time I make a class :shy2:. I start to make this class because I found my random functions are hard to manage.
The class can be used to select one or a group of item(strings, objects, anything) from the entire collection of items. It include randomly select, randomly select unique.
It also include function to randomly select weighted items and randomly select unique weighted items. Weighted items are items assigned a weight, so it can show up more frequently than ones with lower weight.

My English sucks so, I think this version to describe the class is much better:

PHP Classes:
This class can be used to pick a random item from a collection.

It can add to a collection of items that can be values of any type: strings, numbers, objects, etc.. Each item may have a probability weight.

The class can pick one or more random items from the collection. When picking multiple items it may allow repetition of the same item or assure that all the picked items are unique.

It can also pick random items considering their probability weight, so those with greater weight are more likely to be picked.

This could be great on making PHP games.

Here is the highlighted source code:

<?php
/*********************************
RandomLib Version 1.3
Programmed by : Chao Xu(Mgccl)
E-mail        : mgcclx@gmail.com
Website       : http://mgccl.com
Info          : Please email me if there is any feature you want
or there is any bugs. I will fix them as soon as possible.
Change        :
1.3
Add the rand() method, it choses which random method to be used
on generate random numbers.
1.2
Add truerand(), get a random number from http://www.random.org
1.1
Add some BC functions and the power of generate
very large random numbers
ToDo         :Optimize BCrand speed, chose the random handler
*********************************/
 
class random{
var $data = array();
var $rand_op = 'rand';
 
function rand($min = 0, $max = 1, $amount = 1){
	if($this->rand_op=='rand'){
		if($amount == 1){
			return rand($min, $max);
		}else{
			$i = 0;
			while($i< $amount){
				$rand[] =rand($min, $max);
				++$i;
			}
			return $rand;
		}
	}elseif($this->rand_op=='mt_rand'){
		if($amount == 1){
			return mt_rand($min, $max);
		}else{
			$i = 0;
			while($i< $amount){
				$rand[] =mt_rand($min, $max);
				++$i;
			}
			return $rand;
		}
	}elseif($this->rand_op=='truerand'){
		return $this->truerand($min, $max, $amount);
	}
}
function add($string,$weight=1){
    $this->data[] = array('s' => $string, 'w' => $weight);
}
function optimize(){
    foreach($this->data as $var){
        if($new[$var['s']]){
            $new[$var['s']] += $var['w'];
        }else{
            $new[$var['s']] = $var['w'];
        }
    }
    unset($this->data);
    foreach($new as $key=>$var){
        $this->data[] = array('s' => $key, 'w' => $var);
    }
}
 
function select($amount=1){
    if($amount == 1){
        $rand = array_rand($this->data);
        $result = $this->data[$rand]['s'];
    }else{
        $i = 0;
        while($i<$amount){
            $result[] = $this->data[array_rand($this->data)]['s'];
            ++$i;
        }
    }
    return $result;
}
 
function select_unique($amount=1){
    if($amount == 1){
        $rand = array_rand($this->data);
        $result = $this->data[$rand]['s'];
    }else{
        $rand = array_rand($this->data, $amount);
        foreach($rand as $var){
            $result[] = $this->data[$var]['s'];
        }
    }
    return $result;
}
 
function select_weighted($amount=1){
    $count = count($this->data);
    $i = 0;
    $max = -1;
    while($i < $count){
        $max += $this->data[$i]['w'];
        ++$i;
    }
    if(1 == $amount){
        $rand = $this->rand(0, $max);
        $w = 0; $n = 0;
        while($w <= $rand){
            $w += $this->data[$n]['w'];
            ++$n;
        }
        $key = $this->data[$n-1]['s'];
    }else{
        $i = 0;
        while($i<$amount){
            $random[] = $this->rand(0, $max);
            ++$i;
        }
        sort($random);
        $i = 0;
        $n = 0;
        $w = 0;
        while($i<$amount){
            while($w<=$random[$i]){
                $w += $this->data[$n]['w'];
                ++$n;
            }
            $key[] = $this->data[$n-1]['s'];
            ++$i;
        }
    }
    return $key;
}
 
function bc_select_weighted($amount=1){
    $count = count($this->data);
    $i = 0;
    $max = -1;
    while($i < $count){
        $max = bcadd($this->data[$i]['w'],$max);
        ++$i;
    }
    if(1 == $amount){
        $rand = $this->bcrand(0, $max);
        $w = 0; $n = 0;
        while(bccomp($w,$rand) == 0||bccomp($w,$rand)== -1){
            $w = bcadd($this->data[$n]['w'],$w);
            ++$n;
        }
        $key = $this->data[$n-1]['s'];
    }else{
        $i = 0;
        while($i<$amount){
            $random[] = $this->bcrand(0, $max);
            ++$i;
        }
        natsort($random);
        $i = 0;
        $n = 0;
        $w = 0;
        while($i<$amount){
            while(bccomp($w,$random[$i]) == 0||bccomp($w,$random[$i])== -1){
                $w = bcadd($this->data[$n]['w'],$w);
                ++$n;
            }
            $key[] = $this->data[$n-1]['s'];
            ++$i;
        }
    }
    return $key;
}
 
 
 
function select_weighted_unique($amount=1){
	if($amount == 1){
		return $this->select_weighted(1);
	}
    $count = count($this->data);
    $i = 0;
    if($amount >= $count){
        while($i < $count){
            $return[] = $this->data[$i]['s'];
            ++$i;
        }
        return $return;
    }else{
        $max = -1;
        while($i < $count){
            $max += $this->data[$i]['w'];
            ++$i;
        }
 
        $i = 0;
        while($i < $amount){
            $max -= $sub;
            $w = 0;
            $n = 0;
            $num = $this->rand(0,$max);
            while($w <= $num){
                $w += $this->data[$n]['w'];
                ++$n;
            }
            $sub = $this->data[$n-1]['w'];
            $key[] = $this->data[$n-1]['s'];
 
            array_splice($this->data, $n-1, 1);
            ++$i;
        }
        return $key;
    }
}
 
function bc_select_weighted_unique($amount=1){
	if($amount == 1){
		return $this->bc_select_weighted(1);
	}
    $count = count($this->data);
    $i = 0;
    if($amount >= $count){
        while($i < $count){
            $return[] = $this->data[$i]['s'];
            ++$i;
        }
        return $return;
    }else{
        $max = -1;
        while($i < $count){
            $max = bcadd($this->data[$i]['w'],$max);
            ++$i;
        }
 
        $i = 0;
        while($i < $amount){
            $max = bcsub($max,$sub);
            $w = 0;
            $n = 0;
            $num = $this->bcrand(0,$max);
            //BCCOMP!!!!!!
            while(bccomp($w,$num) == 0 || bccomp($w,$num) == -1){
                $w = bcadd($this->data[$n]['w'],$w);
                ++$n;
            }
            $sub = $this->data[$n-1]['w'];
            $key[] = $this->data[$n-1]['s'];
 
            array_splice($this->data, $n-1, 1);
            ++$i;
        }
        return $key;
    }
}
 
function bcrand($min, $max){
	bcscale(0);
	if(bccomp($max,$min)!=1){
		return 0;
	}
	$top = bcsub($max,$min);
	$rand = bcadd($top, 1);
	$length = strlen($top);
 
		$n = 0;
		while(9*$n <= $length){
			if($length - 9*$n >= 9){
				$rand_part[] = $this->rand(0,999999999);
			}else{
				$j = 0; $foo = '';
				while($j < $length-9*$n){
					$foo .= '9';
					++$j;
				}
				$foo += 0;
				$rand_part[] = $this->rand(0,$foo);
			}
			++$n;
		}
		$i = 0;
		$rand ='';
		$count = count($rand_part);
		while($i < $count){
			$rand .= $rand_part[$i];
			++$i;
		}
	while(bccomp($rand,$top)==1){
		$rand = substr($rand,1,strlen($rand)).$this->rand(0,9);
	}
	return bcadd($rand,$min);
}
 
function truerand($min, $max, $amount=1){
	if($amount == 1){
		$fp = fopen("http://www.random.org/cgi-bin/randnum?num="
.$amount."&min="."$min"."&max="."$max"."&col=1", "r");
		$num = fread($fp, 4096*$amount);
		fclose($fp);
		return $num;
	}else{
		if($amount > 10000){
			return false;
		}
		$fp = fopen("http://www.random.org/cgi-bin/randnum?num="
.$amount."&min="."$min"."&max="."$max"."&col=1", "r");
		$num = fread($fp, 4096*$amount);
		fclose($fp);
		$num = explode("\n", $num);
		unset($num[count($num)-1]);
		return $num;
	}
}
 
}
?>

Please download and see the example:
RandomLib Class Version 1.3(PHP Classes Hosted)

RandomLib Class Version 1.0(This site)

RandomLib in hotscripts.com:

Click here to rate!

The Forge of the best Weighted Random function

just normal random? that's 1999? what time it is now?
Weighted Random allows one thing have more chance to pop up than the other! and you define the weight(chance)!

When I first met PHP, I tried to make a lot of scripts, weighted random is one of them. I soon forgot about it after I build it. Now, after a long time of reading other people's code, I think I can improve my first version a lot.
My first version

function rand_string_pro($array){
	foreach($array as $var){
		$i = 0;
		while($i < $var['w']){
			$string[] = $var['s'];
			$i++;
		}
	}
return $string[array_rand($string)];
}

The input is a array like this(where $i can be any interger)

$array[$i]['s'] = 'string';
$array[$i]['w'] = 30;//only interger

I thought that code is perfect, until weeks ago I saw the flaws and start writing improvements. The list of flaws:
*huge weight can slow the script down, even produce memory overflow
*huge string with reasonable amount of weight can produce memory overflow too
*it does not seems FAST...

This list does not include error handling, because I don't think I need to program something to notice kind experienced programmers. Yes Yes, I know it is a bad act.

I did some improvement and released my 1.0 version

/*
        Weighted Random Ver 1.0 by Mgccl(mgcclx@gmail.com)
        Usage: input an numbered array
        $array[$i][?s?] is the string you want to return
        $array[$i][?w'] is the weight of the string
        you can allow the function to find the GCD
        sometimes it speeds up the script
        To use GCD, use the function like
        rand_string_pro($array, true);
 
        Note: You need the math functions I wrote in
        order to use GCD, you can find them here:
        /2006/11/21/some-math-functions/
        */
function rand_string_pro($seed, $gcd = false){
        $count = count($seed);
        if($gcd == true){
                foreach($seed as $var){
                        $gcd_array[] = $var[?w?];
                }
                $gcd = math_gcd_array($gcd_array);
                if($gcd != 1){
                        $i = 0;
                        while($i < $count){
                                $seed[$i][?w?] /= $gcd;
                                ++$i;
                        }
                }
                unset($gcd, $gcd_array);
        }
 
        $i = 0;
        while($i < $count){
                $n = 0;
                while($n < $seed[$i][?w?]){
                        $key[] = $i;
                        ++$n;
                }
                ++$i;
        }
        return $seed[$key[mt_rand(0, count($key)-1)]][?s?];
}

The new version have these improvements:
* Weight divided by the GCD so we have smaller weights
* Produce a new array to store the key, and the key refer to the "key name" of the variable seed. No more huge array in the size of value
* Used ++$i instead of $i++, small speed up

But there is still a flaw in the system. It still creates a huge array if the GCD is small compare to the weight. The performance could still be slow.

I released the version 1.1, more like a usability improvement version

/*
        Weighted Random Ver 1.1 by Mgccl(mgcclx@gmail.com)
        Update: Nov/25/06 allow non-weighted random, separate the
        string storing array and the weight storing array
        Usage: input $array[$i] = 'string' format(where $i is a number)
        $array[$i]is the string you want to return
        $weight[$i]is the weight of the string
        if one of the $array[$i] does not have a
        $weight[$i] as a match, it automatically
        set $weight[$i] as 1
        To allow use weighted function, call the function like this
        rand_string_pro($array, $weight);
        you can allow the function to find the GCD
        sometimes it speeds up the script
        To use GCD, use the function like
        rand_string_pro($array, $weight, true);
 
        Note: You need the math functions I wrote in
        order to use GCD, you can find them here:
        /2006/11/21/some-math-functions/
        */
 
function rand_string_pro($seed, $weighted = false, $gcd = false){
        $count = count($seed);
   if($weighted === false){
      return $seed[mt_rand(0, $count - 1)];
   }else{
       if($gcd === true){
            if(count($weighted) != $count){
            }else{
               foreach($weighted as $var){
                  $gcd_array[] = $var;
               }
               $gcd = math_gcd_array($gcd_array);
               if($gcd != 1){
                  $i = 0;
                   while($i < count($gcd_array)){
                       $weighted[$i] /= $gcd;
                      ++$i;
                   }
               }
                   unset($gcd, $gcd_array);
           }
       }
           $i = 0;
           while($i < $count){
              if(empty($weighted[$i])){
                 $key[] = $i;
              }else{
                   $n = 0;
                   while($n < $weighted[$i]){
                           $key[] = $i;
                           ++$n;
                   }
               }
                   ++$i;
           }
           return $seed[$key[mt_rand(0, count($key)-1)]];
      }
}

this version separated the input of the string array and the weight array
it also made the function usable when there is no weight present.

And finally, Version 1.2 is where I made the speed to the max.

/*
        Weighted Random Ver 1.2 by Mgccl(mgcclx@gmail.com)
        http://mgccl.com
        Update: Nov/29/06
        Faster Speed
        allow non-weighted random, separate the
        string storing array and the weight storing array
        Usage: input $array[$i] = 'string' format(where $i is a number)
        $array[$i]is the string you want to return
        $weight[$i]is the weight of the string
        if one of the $array[$i] does not have a
        $weight[$i] as a match, it automatically
        set $weight[$i] as 1
        To allow use weighted function, call the function like this
        rand_string_pro($array, $weight);
        */
 
function rand_string_pro($seed, $weighted = false){
        $count = count($seed);
        if($weighted === false){
                return $seed[mt_rand(0, $count - 1)];
        }else{
                $i = 0; $n = 0;
                $num = mt_rand(0, array_sum($weighted) + ($count-count($weighted)));
                while($i < $count){
                        if(empty($weighted[$i])){
                                ++$n;
                        }else{
                        $n += $weighted[$i];
                    }
                    if($n >= $num){
                        break;
                    }
                    ++$i;
                }
                return