Attempting to calculate pi using chudnovsky algorithm in php - php

I've been attempting to create some pi approximations using different methods, and the Chudnovsky algorithm looked fun, but I have hit a roadblock.
Here's the code:
$pi =0;
for ($k = 0; $k < 1; $k++) {
$pi += (($k%2==1?-1.0:1.0) * gmp_fact(6 * $k) * (13591409.0 + (545140134.0 * $k))) / (gmp_fact(3.0 * $k) * pow(gmp_fact($k), 3.0) * pow(640320.0, 3.0 * $k + 3.0/2.0));
}
$pi *= 12;
echo 1/$pi;
Running this gives me 301.59289474461.
I am new to php and thought this would be good training, so forgive me if this is amateur work.
I also realise there is a similar post in c++, which I attempted and was successful, but this is still broke. I've used stack exchange to solve many other problems, so i hope you can help!

Make sure you have gmp extension installed, and Always use gmp_strval() when using gmp functions in php to avoid getting the address of the gmp_fact object multiplied by other variables.
From the php.net : http://php.net/manual/en/function.gmp-fact.php#refsect1-function.gmp-fact-returnvalues
$pi =0;
for ($k = 0; $k < 20; $k++) {
$pi += (($k%2==1?-1.0:1.0) * gmp_strval(gmp_fact(6 * $k)) * (13591409.0 + (545140134.0 * $k))) / ( gmp_strval(gmp_fact(3.0 * $k)) * pow( gmp_strval(gmp_fact($k)), 3.0) * pow(640320.0, 3.0 * $k + 3.0/2.0));
}
$pi *= 12;
echo 1/$pi;

Related

Count by formula on php

I want to ask your help. I don't remember the Math, but I need to count by formula
I am going to make the loops like that.
$incomeDepositDay = $totalSum * $percent / ($typePerm *100);
for ($i = 1; $i <= $term; $i++) {
$incomeDepositTax = $percent/100 * $typePerm * ($totalSum * $i + ($incomeDepositDay * (1 - 13/100)));
}
But I see, that my code isn't correct. But I don't understand, where?
You don't need a loop, as the formula is a straightforward calculation.
You have a couple of errors in your sum. Try:
$incomeDepositDay = $totalSum * $percent / ($typePerm *100);
$incomeDepositTax = $percent/(100 * $typePerm) * ($totalSum * $i + ($i - 1) * $incomeDepositDay * (1 - 13/100));

How to calculate MACD using PHP?

I get similar MACD and Signal values, using the following class in Binance:
I got this code from:
https://github.com/hurdad/doo-forex/blob/master/protected/class/Technical%20Indicators/MACD.php
How can I modify the class to arrive at the exact value?
You can use the php-trader lib, note that it works as CLI only.
But this is fairly simple math:
MACD = EMA26 - EMA12
/*
* Exponential moving average (EMA)
*
* The start of the EPA is seeded with the first data point.
* Then each day after that:
* EMAtoday = α⋅xtoday + (1-α)EMAyesterday
*
* where
* α: coefficient that represents the degree of weighting decrease, a constant smoothing factor between 0 and 1.
*
* #param array $numbers
* #param int $n Length of the EPA
* #return array of exponential moving averages
*/
function exponentialMovingAverage( $numbers, $n)
{
$m = count($numbers);
$α = 2 / ($n + 1);
$EMA = [];
// Start off by seeding with the first data point
$EMA[] = $numbers[0];
// Each day after: EMAtoday = α⋅xtoday + (1-α)EMAyesterday
for ($i = 1; $i < $m; $i++) {
$EMA[] = ($α * $numbers[$i]) + ((1 - $α) * $EMA[$i - 1]);
}
return $EMA;}

PHP interpreter micro-optimizations in code

By stumbling on this so thread i decided to write similar test in PHP.
My test code is this:
// Slow version
$t1 = microtime(true);
for ($n = 0, $i = 0; $i < 20000000; $i++) {
$n += 2 * ($i * $i);
}
$t2 = microtime(true);
echo "n={$n}\n";
// Optimized version
$t3 = microtime(true);
for ($n = 0, $i = 0; $i < 20000000; $i++) {
$n += $i * $i;
}
$n *= 2;
$t4 = microtime(true);
echo "n={$n}\n";
$speedup = round(100 * (($t2 - $t1) - ($t4 - $t3)) / ($t2 - $t1), 0);
echo "speedup: {$speedup}%\n";
Results
in PHP 2 * ($i * $i) version runs quite similar like 2 * $i * $i,
so PHP interpreter isn't optimizing bytecode as JVM in Java
Even when I optimized code manually - I've got ~ 8% speedup, when
Java's version gets ~ 16% speedup. So PHP version gets about 1/2 speedup factor of that in Java's code.
Rationale for optimization
I will not go into many details, but ratio of multiplications in optimized and un-optimized code is ->
1 summation : 3/4
2 summations: 4/6
3 summations: 5/8
4 summations: 6/10
...
And in general:
where n is number of summations in a loop. To be formula useful to us - we need to calculate limit of it when N approaches infinity (to replicate situation that we do A LOT of summations in a loop). So :
So we get conclusion that in optimized code there must be 50% less multiplications.
Questions
Why PHP interpreter isn't applying code optimization ?
Why PHP speedup factor is just half of that in Java ?
It's time to analyze PHP opcodes which are generated by PHP interpreter. For that you need to install VLD extension and use it from command line to generate opcodes of php script at hand.
Opcode analysis
Seems that $i++ is not the same as ++$i in terms of opcodes and memory usage. Statement $i++; generates opcodes:
POST_INC ~4 !1
FREE ~4
Increases counter by 1 and saves previous value into memory slot #4. Then, because this value is never used - frees it from memory. Question - why do we need to store value if it is never used ?
Seems that indeed there is a loop penalty, so we can gain additional performance by performing loop unrolling.
Optimized test code
Changing POST_INC into ASSIGN_ADD (which don't saves additional info in memory) and performing loop unrolling, gives use such test code :
while (true) {
// Slow version
$t1 = microtime(true);
for ($n = 0, $i = 0; $i < 2000; $i+=10) {
// loop unrolling
$n += 2 * (($i+0) * ($i+0));
$n += 2 * (($i+1) * ($i+1));
$n += 2 * (($i+2) * ($i+2));
$n += 2 * (($i+3) * ($i+3));
$n += 2 * (($i+4) * ($i+4));
$n += 2 * (($i+5) * ($i+5));
$n += 2 * (($i+6) * ($i+6));
$n += 2 * (($i+7) * ($i+7));
$n += 2 * (($i+8) * ($i+8));
$n += 2 * (($i+9) * ($i+9));
}
$t2 = microtime(true);
echo "{$n}\n";
// Optimized version
$t3 = microtime(true);
for ($n = 0, $i = 0; $i < 2000; $i+=10) {
// loop unrolling
$n += ($i+0) * ($i+0);
$n += ($i+1) * ($i+1);
$n += ($i+2) * ($i+2);
$n += ($i+3) * ($i+3);
$n += ($i+4) * ($i+4);
$n += ($i+5) * ($i+5);
$n += ($i+6) * ($i+6);
$n += ($i+7) * ($i+7);
$n += ($i+8) * ($i+8);
$n += ($i+9) * ($i+9);
}
$n *= 2;
$t4 = microtime(true);
echo "{$n}\n";
$speedup = round(100 * (($t2 - $t1) - ($t4 - $t3)) / ($t2 - $t1), 0);
$table[$speedup]++;
echo "****************\n";
foreach ($table as $s => $c) {
if ($s >= 0 && $s <= 20)
echo "$s,$c\n";
}
}
Results
Script aggregates number of times CPU hit into one or other speedup value.
When CPU hits vs Speedup is drawn as a graph, we get such picture:
So it is most likely that script will get 10% speedup. This means that our optimizations resulted in +2% speedup (compared to original scripts 8%).
Expectations
I'm pretty sure that all these things i've done - could be done automatically by a PHP JIT'er. I don't think that it's hard to change automatically a pair of POST_INC/FREE opcodes into one PRE_INC opcode when generating binary executable. Also it's not a miracle that PHP JIT'er could apply loop unrolling. And this is just a start of optimizations !
Hopefully there will be a JIT'er in PHP 8.0

Implementing Vincenty's Formula in PHP

I've been attempting to implement Vincenty's formulae with the following:
/* Implemented using Vincenty's formulae from http://en.wikipedia.org/wiki/Vincenty%27s_formulae,
* answers "Direct Problem".
* $latlng is a ('lat'=>x1, 'lng'=>y1) array
* $distance is in miles
* $angle is in degrees
*/
function addDistance($latlng, $distance, $bearing) {
//variables
$bearing = deg2rad($bearing);
$iterations = 20; //avoid too-early termination while avoiding the non-convergant case
//knowns
$f = EARTH_SPHEROID_FLATTENING; //1/298.257223563
$a = EARTH_RADIUS_EQUATOR_MILES; //3963.185 mi
$phi1 = deg2rad($latlng['lat']);
$l1 = deg2rad($latlng['lng']);
$b = (1 - $f) * $a;
//first block
$tanU1 = (1-$f)*tan($phi1);
$U1 = atan($tanU1);
$sigma1 = atan($tanU1 / cos($bearing));
$sinalpha = cos($U1)*sin($bearing);
$cos2alpha = (1 - $sinalpha) * (1 + $sinalpha);
$usquared = $cos2alpha * (($a*$a - $b*$b) / 2);
$A = 1 + ($usquared)/16384 * (4096+$usquared*(-768+$usquared*(320 - 175*$usquared)));
$B = ($usquared / 1024)*(256*$usquared*(-128 + $usquared * (74 - 47*$usquared)));
//the loop - determining our value
$sigma = $distance / ($b * $A);
for($i = 0; $i < $iterations; ++$i) {
$twosigmam = 2*$sigma1 + $sigma;
$delta_sigma = $B * sin($sigma) * (cos($twosigmam)+(1/4)*$B*(cos(-1 + 2*cos(cos($twosigmam))) - (1/6)*$B*cos($twosigmam)*(-3+4*sin(sin($sigma)))*(-3+4*cos(cos($twosigmam)))));
$sigma = $distance / ($b * $A) + $delta_sigma;
}
//second block
$phi2 = atan((sin($U1)*cos($sigma)+cos($U1)*sin($sigma)*cos($bearing)) / ((1-$f) * sqrt(sin($sinalpha) + pow(sin($U1)*sin($sigma) - cos($U1)*cos($sigma)*cos($bearing), 2))));
$lambda = atan((sin($sigma) * sin($bearing)) / (cos($U1)*cos($sigma) - sin($U1)*sin($sigma)*cos($bearing)));
$C = ($f / 16)* $cos2alpha * (4+$f*(4-3*$cos2alpha));
$L = $lambda - (1 - $C) * $f * $sinalpha * ($sigma + $C*sin($sigma)*(cos($twosigmam)+$C*cos($sigma)*(-1+2*cos(cos($twosigmam)))));
$alpha2 = atan($sinalpha / (-sin($U1)*sin($sigma) + cos($U1)*cos($sigma)*cos($bearing)));
//and return our results
return array('lat' => rad2deg($phi2), 'lng' => rad2deg($lambda));
}
var_dump(addDistance(array('lat' => 93.129, 'lng' => -43.221), 20, 135);
The issue is that the results are not reasonable - I'm getting variances of up to 20 latitude and longitude keeping the distance at 20. Is it not in units of elliptical distance on the sphere? Am I misunderstanding something, or is my implementation flawed?
There are a number of errors in transcription from the wikipedia page Direct Problem section:
Your u2 expression has 2 in the denominator where it should have b2;
Your A and B expressions are inconsistent about whether the initial fraction factor needs to be parenthesised to correctly express a / b * c as (a/b) * c - what happens without parentheses is a php syntax issue which I don't know the answer to, but you should favour clarity;
You should be iterating "until there is no significant change in sigma", which may or may not happen in your fixed number of iterations;
There are errors in your DELTA_sigma formula:
on the wikipedia page, the first term inside the square bracket [ is cos sigma (-1 etc, whereas you have cos (-1 etc, which is very different;
in the same formula and also later, note that cos2 x means (cos x)(cos x), not cos cos x!
Your phi_2 formula has a sin($sinalpha) where it should have a sin($sinalpha)*sin($sinalpha);
I think that's all.
Have you tried this:
https://github.com/treffynnon/Geographic-Calculations-in-PHP

Does PHP's range('A', 'Z') return a static array?

I was overlooking some code that I had written to generate an A-Z navigation on a product page, and the method in which it was done was a for loop; using ascii octals 65-91 and PHP's chr() function. I wondered if there was a simpler and/or more efficient way of doing this, and I discovered that PHP's range() function supports alphabetical ranges.
After I wrote my test code to compare the different methods, a few questions came to mind:
Does PHP store a static array of the alphabet?
How can I profile more deeply to look below the PHP layer to see
what's happening?
I have a cachegrind of the PHP script that can be attached if necessary, in addition to environment config. For those who might want to know the machine specs in which it was executed, here are some links:
root#workbox:~$ lshw
http://pastebin.com/cZZRjJcR
root#workbox:~$ sysinfo
http://pastebin.com/ihQkkPAJ
<?php
/*
* determine which method out of 3 for returning
* an array of uppercase alphabetic characters
* has the highest performance
*
* +++++++++++++++++++++++++++++++++++++++++++++
*
* 1) Array $alpha = for($x = 65; $x < 91; $x++) { $upperChr[] = chr($x); }
* 2) Array $alpha = range(chr(65), chr(90);
* 3) Array $alpha = range('A', 'Z');
*
* +++++++++++++++++++++++++++++++++++++++++++++
*
* test runs with iterations:
*
* 10,000:
* - 1) upperChrElapsed: 0.453785s
* - 2) upperRangeChrElapsed: 0.069262s
* - 3) upperRangeAZElapsed: 0.046110s
*
* 100,000:
* - 1) upperChrElapsed: 0.729015s
* - 2) upperRangeChrElapsed: 0.078652s
* - 3) upperRangeAZElapsed: 0.052071s
*
* 1,000,000:
* - 1) upperChrElapsed: 50.942950s
* - 2) upperRangeChrElapsed: 10.091785s
* - 3) upperRangeAZElapsed: 8.073058s
*/
ini_set('max_execution_time', 0);
ini_set('memory_limit', 0);
define('ITERATIONS', 1000000); // 1m loops x3
$upperChrStart = microtime(true);
for($i = 0; $i <= ITERATIONS; $i++) {
$upperChr = array();
for($x = 65; $x < 91; $x++) {
$upperChr[] = chr($x);
}
}
$upperChrElapsed = microtime(true) - $upperChrStart;
// +++++++++++++++++++++++++++++++++++++++++++++
$upperRangeChrStart = microtime(true);
for($i = 0; $i <= ITERATIONS; $i++) {
$upperRangeChr = range(chr(65), chr(90));
}
$upperRangeChrElapsed = microtime(true) - $upperRangeChrStart;
// +++++++++++++++++++++++++++++++++++++++++++++
$upperRangeAZStart = microtime(true);
for($i = 0; $i <= ITERATIONS; $i++) {
$upperRangeAZ = range('A', 'Z');
}
$upperRangeAZElapsed = microtime(true) - $upperRangeAZStart;
printf("upperChrElapsed: %f\n", $upperChrElapsed);
printf("upperRangeChrElapsed: %f\n", $upperRangeChrElapsed);
printf("upperRangeAZElapsed: %f\n", $upperRangeAZElapsed);
?>
Does PHP waste memory keeping an array of letters? I would doubt it. range() will work on a wide variety of values too.
If performance is an issue in such a case, you might want to declare the array outside of the loop so that it can be re-used. However, large gains rarely come from micro-optimizations. Using profiling on larger applications to get significant gains.
As for profiling at a lower level, you can simply use valgrind on PHP CLI. I've also seen it used on an apache process.
Related: How to profile my C++ application on linux

Categories