BCmath

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 :-)

New version of Pi calculator, break the speed bound!

You think use PHP to calculate the 2000 places after pi in 8 seconds is fast? you haven't see anything yet. This is the 3rd last attempt of finding pi (at least till 2020). I still have 2 more algorithms going to try. This one uses Borwein's quartic convergence algorithm on pi finding.

Borwein's quartic convergence algorithm in pi finding

You want to know the speed right? It uses less than 4 second to find the 2000th place of pi! and it is only slightly slower than the older pi calculator when precision is very low(between 1 to 30).

Feel free to check the source.
This was modified to be part of the BCext class I am working on right now.

function bcpi($precision){
	bcscale($precision+6);
	$a = bcsqrt(2);
	$b = 0;
	$p = bcadd(2, $a);
	$i = 0;
	$count = ceil(log($precision+6)/log(2))-1;
	while($i < $count){
		$sqrt_a = bcsqrt($a);
		$a1 = $a;
		$a = bcdiv(bcadd($sqrt_a,bcdiv(1,$sqrt_a)),2);
		$b_up = bcmul($sqrt_a,bcadd(1,$b));
		$b_down = bcadd($a1,$b);
		$b = bcdiv($b_up, $b_down);
		$p_up = bcmul(bcmul($p,$b),bcadd(1,$a));
		$p_down = bcadd(1, $b);
		$p = bcdiv($p_up, $p_down);
		++$i;
	}
 
return bcadd($p,0,$precision);
}

New Upgrade on BCroot, speed boost

A week ago, I have released a bcpow() no fraction exponent work around. It is good, like all my scripts, but it is not fast. This code could take 12.152509 second to run the following statement on my pc.

bcroot(2,3072);

The main reason of slowness is in the algorithm. There is a bcpow() function that will use the second number passed to bcroot() as the exponent. In that above script, it multiple 2 for over 3000 times!
PHP's bcsqrt() function is very fast, with a little math knowledge you would know that 3072 = 1024 * 3 = 2^10 * 3. The new version of the function takes advantage of the bcsqrt()'s speed. So now, it runs bcsqrt() 10 times and then multiple it with bcpow($whatever, 3). Result? check this out:
0.000928 second.
You might want to know how did I found a number's product that is a power of 2.First of my attempt was do many bcmod() to the number until it returns 1. But then I noticed in binary, a number that have a product that is a power of 2, will have some trailing zeros, the number of trailing zeros is the exponent of 2. The other product will be what remains in the front. It will be great for you to know the function decbin() and bindec(). For example:
decbin(10) = 1010. 1 trailing zeros, so 10 = 2^1 * bindec(101)
decbin(12) = 1100. 2 trailing zeros, so 12 = 2^2 * bindec(11)
decbin(16) = 10000. 4 trailing zeros, so 16 = 2^4 * bindec(1)
Neat...I uses like 10 minute of my free time trying this...
Here is the new version of the function, it still need the bcgetscale() function that exists in my last post about bcroot()

//Version 0.2 of BCRoot
//Change Log: It uses a lot bcsqrt() to make the speed fast
//fix a small decimal bug, where the last decimal could be wrong
 
function bcroot($a, $n, $scale='default'){
    $default = bcgetscale();//Get the scale
    if($scale == 'default'){
        $scale = $default;//use default scale
    }
    if($n & ($n-1)){//check if $n is the power of 2, return 0 if is
        //decbin is the reason this function can't have number
        //larger than 2^31
        $bin = decbin($n);
		$i = strlen($bin)-1;
		$pow = 0;
		while($i){
			if($bin[$i]==='0'){
				++$pow; --$i;
			}else{
				break;
			}
		}
		$n = bcdiv($n, bcpow(2, $pow),0);
       //now use Newton?s method to find the number
        bcscale($scale+15);
        $x = 1;
        $k = 0;
        $limit = ceil(log($scale+15)/log(2))+1;
        while($k<$limit){
            $t1 = bcdiv(1,$n);
            $t21 = bcmul(bcsub($n,1),$x);
            $t22 = bcdiv($a,bcpow($x, $n-1));
            $t2 = bcadd($t21,$t22);
            $x = bcmul($t1, $t2);
            ++$k;
        }
        $i = 0;
        while($i < $pow){
            $x = bcsqrt($x,$scale+3);
            ++$i;
        }
        bcscale($default);
        return bcadd($x,0,$scale);
    }else{
    //here use many bcsqrt, because this is FAST
        $i = 0;
        $pow = log($n)/log(2);
        while($i < $pow){
            $a = bcsqrt($a,$scale+3);
            ++$i;
        }
        return bcadd($a,0,$scale);
    }
}

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.

Introduction to BCMath

What is BCMath and why should anyone use it?
BC stand for Binary Calculator. BCMath can break free from PHP's limit on numbers and theoretically possible on calculating infinite large and infinite precision numbers. It receives numbers as strings and output them as string. There is not much of a reason to use BCMath, because PHP are not made to do those very mathy calculations. But if you want, this tutorial is for you.
What are the functions in BCMath?
There are only 10 functions in BCMath, there are all basic ones, you can find the equivalent in PHP math operators.
bcadd
Adds two numbers together, equivalent to "+". Optional third parameter choses how much precision the function is going to calculate.
Example:

bcadd('333','666');//return 999
 
bcadd('0.9','0.01',1);//return 0.9

bccomp
Compare two numbers, return a integer value states the 1nd value is larger(1), equal(0) or smaller(-1). Optional third parameter choses how much precision the function is going to count.
Example:

bccomp('30','50');//return -1
 
bccomp('50','30');//return 1
 
bccomp('50.05','50',1);//return 0

bcdiv
Divide the first number to the 2nd number, return a string result. Equivalent to "/". Optional third parameter choses how much precision the function is going to count.
Example:

bcdiv('1','3',3);//return 0.333

bcmod
Find the modules of a number. first number is the number get module by the 2nd one. return a string. Equivalent to "%".
Example:

bcmod('1','3');//return 0

bcmul
Multiply the first number to the second one. Return a string. Optional third parameter choses how much precision the function is going to count. Equivalent to "*".
Example:

bcmul('1','3',5);return 3.00000

bcpow
The first number is the base and the second is the exponent. Optional third parameter choses how much precision the function is going to count. Equivalent to the pow() in PHP. Note that the function only take in integers as exponents(check out this post to see how to solve the problem) unlike pow(). and the function can't have exponents lager than 2^31.
Example:

bcpow('3','2',2);//return 9.00
bcpow('3','0.9');//return 1
bcpow('3','999999999999999999999999999999999999');//return 1

bcpowmod
bcpowmod is equivalent to a bcpow then bcmod, like this:

bcpowmod($base, $exponent, $mod);
bcmod(bcpow($base, $exponent), $mod);

This function can have exponent over 2^31 and have a optional forth parameter choses how much precision the function is going to count.

bcscale
bcscale set the default precision of all bc functions. Input must be a integer, and it return true when success and false when fail.
Example:

bcscale(3);
bcadd('3','3');//return 6.000

bcsqrt
Calculate the square root of the input number, optional 2nd parameter choses how much precision the function is going to count. Equivalent to sqrt() function.
Example:

bcsqrt('2',5);//return 1.41421

bcsub
Subtract the 2nd number from the first number. optional 3nd parameter choses how much precision the function is going to count. Equivalent to "-".
Example:

bcsub('3','2',2);//return 1.00

What else do I need to know?
BCMath only have 10 functions, so you have to write your own function, like one that do sin() and cos(). That's when you will find algorithms really important.
There is another math extension called GMP, quite like BCMath but much more powerful(built in functions saves you time).

Syndicate content
Honey Pot that kill bots