Finding factors in known ratio - php

I'm making a calculator for pipe fittings. The idea is that the user inputs the angle of the turn, then the calculator will tell you how many of what fittings to use. I have access to 8°, 11.25°, 22.5°, 45°, and 90° fittings. But, I can simplify it into 8° and 11.25° fittings, since 22.5°, 45°, and 90° are multiples of 11.25. I can take the # of 11.25 degree fittings, then use the following code to break it into larger fittings.
$num90 = floor($angle11 / 90);
$runningtotal = $angle11 - $num90 * 90;
$num45 = floor($runningtotal / 45);
$runningtotal = $runningtotal - $num45 * 45;
$num22 = floor($runningtotal / 22.5);
$runningtotal = $runningtotal - $num22 * 22.5;
$num11 = floor($runningtotal / 11.25);
$runningtotal = $runningtotal - $num11 * 11.25;
echo "You will need:";
echo "<br>";
echo "$num90 -- 90° fittings";
echo "<br>";
echo "$num45 -- 45° fittings";
echo "<br>";
echo "$num22 -- 22.5° fittings";
echo "<br>";
echo "$num11 -- 11.25° fittings";
Basically, I need to solve the equation:
"8x + 11.25y = angle"
Where "angle" is a known value, and X and Y are integers.
I've made a list of all the possible angles using these fittings, so the program will use the closest possible angle to their request (e.g. if they need a 150° turn, they'll be shown the fittings needed for a 150.5° connection, which is possible). That means that X and Y will be whole numbers. I already have the code to select the closest angle, I'm not worried about it.
I've looked into solutions for the Change Making Problem, which deals with something extremely similar. Most of the equations and algorithms they found go way over my head in terms of complexity. I'm a recent high school graduate, so my math level isn't as good as others.
How would I go about solving this equation? Is this maybe too complicated for me, a beginner? Am I overlooking some super simple solution?
Or, should I just use the wolframalpha API to offload the math onto their side?
Any help would be very appreciated!

Try this:
print_r(get_best_fit(150));
function get_best_fit($angle){
$a = 8;
$b = 11.25;
$best_diff = $angle;
$best_fit = [0,0];
for($x = 0; $x <= $angle/$a; $x++){
$y = round(($angle-$a*$x)/$b);
$diff = $angle-($x*$a+$y*$b);
if(abs($diff) < $best_diff){
$best_diff = abs($diff);
$best_fit = [$x,$y];
}
}
return $best_fit;
}
Output
Array ( [0] => 16 [1] => 2 )
So you would need 16 x 8 + 2 x 11.25.

Related

Why is my Python code 100 times slower than the same code in PHP?

I have two points (x1 and x2) and want to generate a normal distribution in a given step count. The sum of y values for the x values between x1 and x2 is 1. To the actual problem:
I'm fairly new to Python and wonder why the following code produces the desired result, but about 100x slower than the same program in PHP. There are about 2000 x1-x2 pairs and about 5 step values per pair.
I tried to compile with Cython, used multiprocessing but it just improved things 2x, which is still 50x slower than PHP. Any suggestions how to improve speed to match at least PHP performance?
from scipy.stats import norm
import numpy as np
import time
# Calculates normal distribution
def calculate_dist(x1, x2, steps, slope):
points = []
range = np.linspace(x1, x2, steps+2)
for x in range:
y = norm.pdf(x, x1+((x2-x1)/2), slope)
points.append([x, y])
sum = np.array(points).sum(axis=0)[1]
norm_points = []
for point in points:
norm_points.append([point[0], point[1]/sum])
return norm_points
start = time.time()
for i in range(0, 2000):
for j in range(10, 15):
calculate_dist(0, 1, j, 0.15)
print(time.time() - start) # Around 15 seconds or so
Edit, PHP Code:
$start = microtime(true);
for ($i = 0; $i<2000; $i++) {
for ($j = 10; $j<15; $j++) {
$x1 = 0; $x2 = 1; $steps = $j; $slope = 0.15;
$step = abs($x2-$x1) / ($steps + 1);
$points = [];
for ($x = $x1; $x <= $x2 + 0.000001; $x += $step) {
$y = stats_dens_normal($x, $x1 + (($x2 - $x1) / 2), $slope);
$points[] = [$x, $y];
}
$sum = 0;
foreach ($points as $point) {
$sum += $point[1];
}
$norm_points = [];
foreach ($points as &$point) {
array_push($norm_points, [$point[0], $point[1] / $sum]);
}
}
}
return microtime(true) - $start; # Around 0.1 seconds or so
Edit 2, profiled each line and found that norm.pdf() was taking 98% of time, so found a custom normpdf function and defined it, now time is around 0.67s which is considerably faster, but still around 10x slower than PHP. Also I think redefining common functions goes against the idea of Pythons simplicity?!
The custom function (source is some other Stackoverflow answer):
from math import sqrt, pi, exp
def normpdf(x, mu, sigma):
u = (x-mu)/abs(sigma)
y = (1/(sqrt(2*pi)*abs(sigma)))*exp(-u*u/2)
return y
The answer is, you aren't using the right tools/data structures for the tasks in python.
Calling numpy functionality has quite an overhead (scipy.stats.norm.pdf uses numpy under the hood) in python and thus one would never call this functions for one element but for the whole array (so called vectorized computation), that means instead of
for x in range:
y = norm.pdf(x, x1+((x2-x1)/2), slope)
ys.append(y)
one would rather use:
ys = norm.pdf(x,x1+((x2-x1)/2), slope)
calculating pdf for all elements in x and paying the overhead only once rather than len(x) times.
For example to calculate pdf for 10^4 elements takes less than 10 times more time than for one element:
%timeit norm.pdf(0) # 68.4 µs ± 1.62 µs
%timeit norm.pdf(np.zeros(10**4)) # 415 µs ± 12.4 µs
Using vectorized computation will not only make your program faster but often also shorter/easier to understand, for example:
def calculate_dist_vec(x1, x2, steps, slope):
x = np.linspace(x1, x2, steps+2)
y = norm.pdf(x, x1+((x2-x1)/2), slope)
ys = y/np.sum(y)
return x,ys
Using this vectorized version gives you a speed-up around 10.
The problem: norm.pdf is optimized for long vectors (nobody really cares how fast/slow it is for 10 elements if it is very fast for one million elements), but your test is biased against numpy, because it uses/creates only short arrays and thus norm.pdf cannot shine.
So if it is really about small arrays and you are serious about speeding it up you will have to roll out your own version of norm.pdf Using cython for creating this fast and specialized function might be worth a try.

Struggling with maths... How to reverse calculate a percentage?

I believe this is a language agnostic question and more focused on math, however I prefer PHP. I know how to calculate percentages the normal (forward) way:
$percent = 45.85;
$x = 2000000;
$deduction = ($percent / 100) * $x; // 917,000
$result = $x - $deduction; // 1,083,000
What I would like to do, is be able to reverse the calculation (assuming I only know the $percent and $result), for example...
54.15% of x = 1,083,000
How do I calculate x? I know the answer is 2,000,000, but how do I program it to arrive at that answer?
I found a similar question & solution through Google but I just don't understand how to implement it...
You can do
1,083,000 * 100 / 54.15
In PHP, it will be
$x = $result * 100 / $percent
When you say 54.15% of x = 1083000, you mean 0.5415 * x = 1083000. To solve for x, divide 0.5415 from both sides: x = 1083000 / 0.5415. The PHP is:
$p = 54.15;
$r = 108300;
// First, make p a number, not a percent
$p = $p/100; // I would actually use $p/= 100;
// Now, solve for x
$x = $r/$p;

How to optimise a Exponential Moving Average algorithm in PHP?

I'm trying to retrieve the last EMA of a large dataset (15000+ values). It is a very resource-hungry algorithm since each value depends on the previous one. Here is my code :
$k = 2/($range+1);
for ($i; $i<$size_data; ++$i) {
$lastEMA = $lastEMA + $k * ($data[$i]-$lastEMA);
}
What I already did:
Isolate $k so it is not computed 10000+ times
Keep only the latest computed EMA, and not keep all of them in an array
use for() instead of foreach()
the $data[] array doesn't have keys; it's a basic array
This allowed me to reduced execution time from 2000ms to about 500ms for 15000 values!
What didn't work:
Use SplFixedArray(), this shaved only ~10ms executing 1,000,000 values
Use PHP_Trader extension, this returns an array containing all the EMAs instead of just the latest, and it's slower
Writing and running the same algorithm in C# and running it over 2,000,000 values takes only 13ms! So obviously, using a compiled, lower-level language seems to help ;P
Where should I go from here? The code will ultimately run on Ubuntu, so which language should I choose? Will PHP be able to call and pass such a huge argument to the script?
Clearly implementing with an extension gives you a significant boost.
Additionally the calculus can be improved as itself and that gain you can add in whichever language you choose.
It is easy to see that lastEMA can be calculated as follows:
$lastEMA = 0;
$k = 2/($range+1);
for ($i; $i<$size_data; ++$i) {
$lastEMA = (1-$k) * $lastEMA + $k * $data[$i];
}
This can be rewritten as follows in order to take out of the loop as most as possible:
$lastEMA = 0;
$k = 2/($range+1);
$k1m = 1 - $k;
for ($i; $i<$size_data; ++$i) {
$lastEMA = $k1m * $lastEMA + $data[$i];
}
$lastEMA = $lastEMA * $k;
To explain the extraction of the "$k" think that in the previous formulation is as if all the original raw data are multiplied by $k so practically you can instead multiply the end result.
Note that, rewritten in this way, you have 2 operations inside the loop instead of 3 (to be precise inside the loop there are also $i increment, $i comparison with $size_data and $lastEMA value assignation) so this way you can expect to achieve an additional speedup in the range between the 16% and 33%.
Further there are other improvements that can be considered at least in some circumstances:
Consider only last values
The first values are multiplied several times by $k1m = 1 - $k so their contribute may be little or even go under the floating point precision (or the acceptable error).
This idea is particularly helpful if you can do the assumption that older data are of the same order of magnitude as the newer because if you consider only the last $n values the error that you make is
$err = $EMA_of_discarded_data * (1-$k) ^ $n.
So if order of magnitude is broadly the same we can tell that the relative error done is
$rel_err = $err / $lastEMA = $EMA_of_discarded_data * (1-$k) ^ $n / $lastEMA
that is almost equal to simply (1-$k) ^ $n.
Under the assumption that "$lastEMA almost equal to $EMA_of_discarded_data":
Let's say that you can accept a relative error $rel_err
you can safely consider only the last $n values where (1 - $k)^$n < $rel_err.
Means that you can pre-calculate (before the loop) $n = log($rel_err) / log (1-$k) and compute all only considering the last $n values.
If the dataset is very big this can give a sensible speedup.
Consider that for 64 bit floating point numbers you have a relative precision (related to the mantissa) that is 2^-53 (about 1.1e-16 and only 2^-24 = 5.96e-8 for 32 bit floating point numbers) so you cannot obtain better than this relative error
so basically you should never have an advantage in calculating more than $n = log(1.1e-16) / log(1-$k) values.
to give an example if $range = 2000 then $n = log(1.1e-16) / log(1-2/2001) = 36'746.
I think that is interesting to know that extra calculations would go lost inside the roundings ==> it is useless ==> is better not to do.
now one example for the case where you can accept a relative error larger than floating point precision $rel_err = 1ppm = 1e-6 = 0.00001% = 6 significant decimal digits you have $n = log(1.1e-16) / log(1-2/2001) = 13'815
I think is quite a little number compared to your last samples numbers so in that cases the speedup could be evident (I'm assuming that $range = 2000 is meaningful or high for your application but thi I cannot know).
just other few numbers because I do not know what are your typical figures:
$rel_err = 1e-3; $range = 2000 => $n = 6'907
$rel_err = 1e-3; $range = 200 => $n = 691
$rel_err = 1e-3; $range = 20 => $n = 69
$rel_err = 1e-6; $range = 2000 => $n = 13'815
$rel_err = 1e-6; $range = 200 => $n = 1'381
$rel_err = 1e-6; $range = 20 => $n = 138
If the assumption "$lastEMA almost equal to $EMA_of_discarded_data" cannot be taken things are less easy but since the advantage cam be significant it can be meaningful to go on:
we need to re-consider the full formula: $rel_err = $EMA_of_discarded_data * (1-$k) ^ $n / $lastEMA
so $n = log($rel_err * $lastEMA / $EMA_of_discarded_data) / log (1-$k) = (log($rel_err) + log($lastEMA / $EMA_of_discarded_data)) / log (1-$k)
the central point is to calculate $lastEMA / $EMA_of_discarded_data (without actually calculating $lastEMA nor $EMA_of_discarded_data of course)
one case is when we know a-priori that for example $EMA_of_discarded_data / $lastEMA < M (for example M = 1000 or M = 1e6)
in that case $n < (log($rel_err/M)) / log (1-$k)
if you cannot give any M number
you have to find a good idea to over-estimate $EMA_of_discarded_data / $lastEMA
one quick way could be to take M = max(data) / min(data)
Parallelization
The calculation can be re-written in a form where it is a simple addition of independent terms:
$lastEMA = 0;
$k = 2/($range+1);
$k1m = 1 - $k;
for ($i; $i<$size_data; ++$i) {
$lastEMA += $k1m ^ ($size_data - 1 - $i) * $data[$i];
}
$lastEMA = $lastEMA * $k;
So if the implementing language supports parallelization the dataset can be divided in 4 (or 8 or n ...basically the number of CPU cores available) chunks and it can be computed the sum of terms on each chunk in parallel summing up the individual results at the end.
I do not go in detail with this since this reply is already terribly long and I think the concept is already expressed.
Building your own extension definitely improves performance. Here's a good tutorial from the Zend website.
Some performance figures: Hardware: Ubuntu 14.04, PHP 5.5.9, 1-core Intel CPU#3.3Ghz, 128MB RAM (it's a VPS).
Before (PHP only, 16,000 values) : 500ms
C Extension, 16,000 values : 0.3ms
C Extension (100,000 values) : 3.7ms
C Extension (500,000 values) : 28.0ms
But I'm memory limited at this point, using 70MB. I will fix that and update the numbers accordingly.

How can I do calculations in base 12 in PHP

I have built a simple modular scale calculator, where I can enter a base number (say font size or line height) and an important number (maybe column width, page width, or another font size) and select a ratio (golden ratio for example) and the calculator will display a double stranded scale for use in page layout. see example below
I have been toying with the idea of allowing users to input points and picas and then displaying the scale in one or the other.
The problem is that picas are base 12 numbers (12 points to a pica), I figured if I could just convert the input (something like 16p6) to base 12 I could do the calculation and go from there.
I just can't work out how to do basic calculations in another base. I'm really just messing around to see what I can come up with, let me know if you think I'm barking up the wrong tree.
So my question is this how do I do calculations in base 12?
<?php
// basic modular scale calculation
$goldenRatio = 1.618;
$baseNumber = 16;
$i = 0;
while ($i <= 10) {
echo round($baseNumber,1). "<br>";
$baseNumber = $baseNumber * $goldenRatio;
$i++;
}
echo "<hr><br>";
// Attempt at base 12 calculation
$a=base_convert(16,10,12);
$b=base_convert(12,10,12);
$r = ($a*$b);
echo $a."*".$b."=";
echo $r;
I'm really just messing around to se what I can come up with, let me know if you think I'm barking up the wrong tree.
Update
To solve the problem of converting Picas to base points from a string like '12p6' I ended up using regex to first test if Picas and Points had been supplied the split the Picas and Points.
function isPica($data) {
if (preg_match('/^[0-9]+(?i)p([0-1]?[0-9])?$/i',$data)) {
return true;
}
return false;
}
function makePoints($data) {
$data = preg_replace('/^([0-9]+)((?i)p)(([0-1]?[0-9])?)$/i','$1.$3',$data);
$data = explode('.',$data);
$points = floor($data[0] * 12);
$points = $data[1] + $points;
return $points;
}
Modular Scale Calculator
Git Hub — Modular Scale Calculator
base_convert just converts the string representation. You can't do calculations using strings of numbers in base 12 in php. When dealing with imperial units, you usually have multiple "bases" to deal with. So it has to be done manually. When you're doing calculations, the base doesn't matter.
Convert all the different units to the smallest one (points). $a = 3*12 + 7;//3picas, 7points.
Do the calculations.
Convert back to original units.
$points = (int)$val % 12;
$picas = (int)($val / 12);
or
$picas = floor($val / 12);
$points = $val - 12*$picas;

How to solve a math equation in a programming language?

I need help to solve this formula ((n * 2) + 10) / (n + 1) = 3, preferably in PHP. (The numbers 2, 10 and 3 should be variables that can be changed.)
I'm able to solve this equation on paper quite easily. However, when I try to implement this in PHP, I'm not sure where to start. I've done several Google queries and searches on here and nothing seems to help. I'm missing the proper approach to deal with this problem.
Any tips and pointers would be great, and if you provide the exact code, please explain how you got to this result.
You're wanting to solve an equation, not implement it. There's a difference. Implementing the equation would be as simple as typing it in. You'd probably want to make it an equality operator (==) though.
Equation solvers are complicated, complicated things. I wouldn't try to make one when there are such good ones ( http://en.wikipedia.org/wiki/Comparison_of_computer_algebra_systems ) lying around.
You can use http://pear.php.net/package/PHP_ParserGenerator/redirected to parse the math expressions into a syntax tree, then do the maths.
((n * 2) + 10) / (n + 1) = 3 would look like:
The idea is to bring on the right subtree (here ...) all the numbers, and on the left all the unknownws, just as you'd do on paper.
In the end you'll have:
+
/ \
n -7
which is 0. And there you have your solution, for any math expression (with one unknown variable).
I'll leave the algorithm to you.
<?php
// ((x * n) + y)/(n + 1) = z)
// => n=(y-z)/(z-x)
function eq ($x=0,$y=0,$z=0)
{
if ($z!=$x)
{
$n=($y-$z)/($z-$x);
} else
{
$n='NAN';
}
return $n;
}
?>
(My algebra is old and flakey but I think this is right)
how about using brute-force??!?! might be slow and not exact:
$step = 0.00001;
$err = 0.1; //error margin
$start = 0;
$response = 3;
for($i = $start;$i <= 3;$i += $step){
if((($i * 2) + 10) / ($i + 1) >= $response - $err){
echo "the answer is $i";
}
}
You could improove this answer.. on every loop you could calculate the distance between the current answer and the desired answer, and adjust the parameters acording to that..
This reminds me my old A.I. class =)
Good Luck
Here's how to solve that equation in C# with the Symbolism computer algebra library:
var n = new Symbol("n");
(((n * 2) + 10) / (n + 1) == 3)
.IsolateVariable(n)
.Disp();
The following is displayed on the console when that code is executed:
n == 7

Categories