How can I get maximum value of a function in specific range with php.
For example I have a function:
function f($x){
return pow($x,2);
}
and i want to get maximum value of this function in range (-1,1).
how can I implement this in php or using some other library?
There's no generic way to do this; you have to do the maths yourself.
Maximisation / Minimisation problems are solved by differentiation. In this case, you would get:
d(x^2)/dx = 2*x
The method for calculating the differential depends on your function. It's not that hard for simple functions like this, and Wolfram Alpha (http://www.wolframalpha.com/) will do it for you if you ask it nicely (http://www.wolframalpha.com/input/?i=d%28x%5E2%29%2Fdx).
Then you set that to 0, which tells you when the gradient is 0 (and therefore, it is at a maximum, minimum, or turning point):
2*x = 0
This tells you that you have a point to check at x = 0 (see the "solution" section here: http://www.wolframalpha.com/input/?i=d%28x%5E2%29%2Fdx%3D0). Now check the value of your function at the lower bound, upper bound, and the point(s) this tells you to check, and get the maximum/minimum of all those results. This will be your limit within that range.
$maximumValue = -999999999;
for ($i = -1; $i <= 1; $i++) {
$maximumValue = max($maximumValue, f($i));
}
var_dump($maximumValue);
..should work fine
This will only check for the numbers -1, 0 and 1. If you want more precision, just change $i++ to eg. $i += 0.1:
$maximumValue = -999999999;
for ($i = -1; $i <= 1; $i += 0.1) {
$maximumValue = max($maximumValue, f($i));
}
var_dump($maximumValue);
This will give you -1, -0.9, -0.8 ..... 0.8, 0.9 and 1.
It could also be changed to $i += 0.000000001 if you want to waste a lot of resources, but get a more accurate result.
If you use differentiation you have to deal with the case that there are no local optima. If the function is smooth this would mean that the optima occur on the endpoints. I am not sure how precise this all needs to be,but perhaps if the function is just given by a finite list of pairs you could sort a list which contains all the points where the function is defined.
Related
I really need your help. I have to create a function that takes 2 positive integers as its arguments and returns the numerical palindromes as an array of n numerical palindromes that come after the num, including num. Also, single-digit numbers are not considered numerical palindromes. So the outcome must be like this --> function(4,6) // returns [11,22,33,44]. Function(4, 6) it's an array that will take only 4 elements and the numerical palindromes must be greater than 6. Other examples are function (1, 75) // returns [77] and function (3, 100) // returns [101, 111, 121]
My code so far:
<?php
function createPalindrome($input)
{
$m = $input;
$palin = $input;
while ($m > 1) {
$d = intval($m % 10);
$palin = $palin * 10 + $d;
$m = intval($m / 10);
}
return $palin;
}
function generatePalindromes($x, $n)
{
$arr = [];
$i = 1;
while (($number = createPalindrome($i)) <= $n) {
$arr[] = $number;
$i++;
}
for($j = 0; $j < $x; $j++)
var_dump($arr[$j]);
}
generatePalindromes(4, 77);
The outcome is:
int(1)
int(22)
int(33)
int(44)
Had to modify this answer a fair bit once Giann49 expounded on his question in a comment reply.
This is not the cleanest or most precise way to do this for sure but it will principally function and hopefully help point you in the right direction logically.
function findExceedingPalindromes($palindromeLimit,$startingPoint){
$palindromesFound = 0; //Set an initial counter for number of palindromes found so far.
$palindromeSet = []; //Create an array to contain all the palindromes.
if($palindromeLimit <= 0 || $startingPoint <= 0){ //Both integers need to be positive as stated.
return false; //If they aren't return false. You can return whatever you want to halt execution of the function. This is just an easy example.
}
if($startingPoint < 10){
$startingPoint = 10; //Since single digits aren't valid if the starting number if less than 10 kick it up to 10.
}
while($palindromesFound <= $palindromeLimit){
$startingPoint++; //Since the first palindrome must exceed the starting point increment it up once at the top of the loop.
$reverseNumber = strrev($startingPoint); //reverse the current number.
if($startingPoint === $reverseNumber){
array_push($palindromeSet,$startingPoint);
$palindomresFound++; //If we find a palindome move the number found 1 higher.
}
}
return $palindromeSet;
}
As an explanation.
The first argument is the number of palindromes to generate. The second argument is the number we want to start palindrome generation at then work up from there.
We create two variables. One is to track how many palindromes have been found. The other is an empty array to insert found palindromes into.
You say the two numbers must be positive integers so if they are anything less than 1 we'll want to exit the function. (optional)
You say single digits don't count so if the starting point is less than 10 we'll just move it up to 10 for starters. (optional)
Now we'll start a while loop. While the number of palindromes is less than the number we want to find the loop will keep running.
We add 1 to the starting point right out of the gate because we want to first palindrome to be higher than the starting point if it already is one. As in if 11 is the number set as the point to start searching we want to look at 11 + 1 for starters. (optional)
To check if a number of a palindrome we want to simply reverse it's string. If the strings are the same forward and back obviously it matches the definition of a palindrome. So we'll add that number into the set of found palindromes and move the number found 1 digit higher.
Once the requested number of palindromes are found we'll break the while loop and return the array of what was found.
I am using this PHP routine to calc Pearson Correlation:
function correlation ($x,$y) {
$length = count($x);
$mean1 = array_sum($x)/$length;
$mean2 = array_sum($y)/$length;
$a = $b = 0;
$a2 = $b2 = 0;
$axb = 0;
for ($i = 0; $i < $length; $i++) {
$a = $x[$i]-$mean1;
$b = $y[$i]-$mean2;
$axb +=$a*$b;
$a2 += pow($a,2);
$b2 += pow($b,2);
}
if ($sqrt = sqrt($a2*$b2))
return $axb/$sqrt;
return 0;
}
When I test it for several conditions it returns 0 on exact matchs:
echo correlation([0,0,0,0,0],[0,0,0,0,0]); // Returns 0!!
echo correlation([0,0,0,0,0],[1,1,1,1,1]); // Returns 0!!
echo correlation([1,1,1,1,1],[1,1,1,1,1]); // Returns 0!!
echo correlation([0,0,0,0,0],[9,9,9,9,9]); // Returns 0!!
echo correlation([0,0,0,0,0],[0,1,2,3,4]); // Returns 0 OK
echo correlation([9,9,9,9,9],[0,1,2,3,4]); // Returns 0 OK
echo correlation([0,1,2,3,4],[0,1,2,3,4]); // Returns 1 OK
Why? and How to accomplish that? Thank you!
For info:
A Pearson correlation is a number between -1 and 1 that indicates the
extent to which two variables are linearly related. The Pearson
correlation is also known as the “product moment correlation
coefficient” (PMCC) or simply “correlation”.
Approach 1 (doing at your own):
Using PHP to statistics is a hard path.
First of all, as you're using a weak typed language (you don't need to specify the types on variables), the language can interpret as int so, you need to set all of your variables on type float and execute again to run this. You can have some problems with float in PHP, see here why I talking this: https://3v4l.org/1FU9J
But if you don't mind about high precision, you can modify your precision you can set your round() function or you can set ini_set('precision', 3); to get the precision on your data.
Another thing. If you need precision, you need to use bc extension because floating point in PHP is a problem and can affect your results.
Look more about bc math extension here: https://www.php.net/manual/en/book.bc.php or try to use another language.
Some references about the floating point:
https://www.leaseweb.com/labs/2013/06/the-php-floating-point-precision-is-wrong-by-default/
Problem with Floats! (in PHP)
Approach 2 (using language functions):
And, PHP have some functions to help in this. So, if this isn't a homework to learn or something like this, you can try this: https://www.php.net/manual/en/function.stats-stat-correlation.php
I'm doing a calculation in PHP using bcmath, and need to raise e by a fractional exponent. Unfortunately, bcpow() only accepts integer exponents. The exponent is typically higher precision than a float will allow, so normal arithmetic functions won't cut it.
For example:
$e = exp(1);
$pow = "0.000000000000000000108420217248550443400745280086994171142578125";
$result = bcpow($e, $pow);
Result is "1" with the error, "bc math warning: non-zero scale in exponent".
Is there another function I can use instead of bcpow()?
Your best bet is probably to use the Taylor series expansion. As you noted, PHP's bcpow is limited to raising to integer exponentiation.
So what you can do is roll your own bc factorial function and use the wiki page to implement a Taylor series expansion of the exponential function.
function bcfac($num) {
if ($num==0) return 1;
$result = '1';
for ( ; $num > 0; $num--)
$result = bcmul($result,$num);
return $result;
}
$mysum = '0';
for ($i=0; $i<300; $i++) {
$mysum = bcadd($mysum, bcdiv(bcpow($pow,$i), bcfac($i)) );
}
print $mysum;
Obviously, the $i<300 is an approximation for infinity... You can change it to suit your performance needs.
With $i=20, I got
1.00000000000000000010842021724855044340662275184110560868263421994092888869270293594926619547803962155136242752708629105688492780863293090291376157887898519458498571566021915144483905034693109606778068801680332504212458366799913406541920812216634834265692913062346724688397654924947370526356787052264726969653983148004800229537555582281617497990286595977830803702329470381960270717424849203303593850108090101578510305396615293917807977774686848422213799049363135722460179809890014584148659937665374616
This is comforting since that small of an exponent should yield something really close to 1.0.
Old question, but people might still be interested nonetheless.
So Kevin got the right idea with the Taylor-polynomial, but when you derive your algorithm from it directly, you can get into trouble, mainly your code gets slow for long input-strings when using large cut-off values for $i.
Here is why:
At every step, by which I mean with each new $i, the code calls bcfac($i). Everytime bcfac is called it performs $i-1 calculations. And $i goes all the way up to 299... that's almost 45000 operations! Not your quick'n'easy floating point operations, but slow BC-string-operations - if you set bcscale(100) your bcmul has to handle up to 10000 pairs of chars!
Also bcpow slows down with increasing $i, too. Not as much as bcfac, because it propably uses something akin to the square-and-multiply method, but it still adds something.
Overall the time required grows quadraticly with the number of polynomial terms computed.
So... what to do?
Here's a tip:
Whenever you handle polynomials, especially Taylor-polynomials, use the Horner method.
It converts this: exp(x) = x^0/0! + x^1/1! + x^2/2! + x^3/3! + ...
...into that: exp(x) = ((( ... )*x/3+1 )*x/2+1 )*x/1+1
And suddenly you don't need any powers or factorials at all!
function bc_exp($number) {
$result = 1;
for ($i=299; $i>0; $i--)
$result = bcadd(bcmul(bcdiv($result, $i), $number), 1);
return $result;
}
This needs only 3 bc-operations for each step, no matter what $i is.
With a starting value of $i=299 (to calculate exp with the same precision as kevin's code does) we now only need 897 bc-operations, compared to more than 45000.
Even using 30 as cut-off instead of 300, we now only need 87 bc-operations while the other code still needs 822 for the factorials alone.
Horner's Method saving the day again!
Some other thoughts:
1) Kevin's code would propably crash with input="0", depending on how bcmath handles errors, because the code trys bcpow(0,0) at the first step ($i=0).
2) Larger exponents require longer polynomials and therefore more iterations, e.g. bc_exp(300) will give a wrong answer, even with $i=299, whyle something like bc_exp(3) will work fine and dandy.
Each term adds x^n/n! to the result, so this term has to get small before the polynomial can start to converge. Now compare two consecutive terms:
( x^(n+1)/(n+1)! ) / ( x^n/n! ) = x/n
Each summand is larger than the one before by a factor of x/n (which we used via the Horner method), so in order for x^(n+1)/(n+1)! to get small x/n has to get small as well, which is only the case when n>x.
Inconclusio: As long as the number of iterations is smaller than the input value, the result will diverge. Only when you add steps until your number of iterations gets larger than the input, the algorithm starts to slowly converge.
In order to reach results that can satisfie someone who is willing to use bcmath, your $i needs to be significantly larger then your $number. And that's a huge proplem when you try to calculate stuff like e^346674567801
A solution is to divide the input into its integer part and its fraction part.
Than use bcpow on the integer part and bc_exp on the fraction part, which now converges from the get-go since the fraction part is smaller than 1. In the end multiply the results.
e^x = e^(intpart+fracpart) = e^intpart * e^fracpart = bcpow(e,intpart) * bc_exp(fracpart)
You could even implement it directly into the code above:
function bc_exp2($number) {
$parts = explode (".", $number);
$fracpart = "0.".$parts[1];
$result = 1;
for ($i=299; $i>0; $i--)
$result = bcadd(bcmul(bcdiv($result, $i), $fracpart), 1);
$result = bcmul(bcpow(exp(1), $parts[0]), $result);
return $result;
}
Note that exp(1) gives you a floating-point number which propably won't satisfy your needs as a bcmath user. You might want to use a value for e that is more accurate, in accordance with your bcscale setting.
3) Talking about numbers of iterations: 300 will be overkill in most situations while in some others it might not even be enough. An algorithm that takes your bcscale and $number and calculates the number of required iterations would be nice. Alraedy got some ideas involving log(n!), but nothing concrete yet.
4) To use this method with an arbitrary base you can use a^x = e^(x*ln(a)).
You might want to divide x into its intpart and fracpart before using bc_exp (instead of doing that within bc_exp2) to avoid unneccessary function calls.
function bc_pow2($base,$exponent) {
$parts = explode (".", $exponent);
if ($parts[1] == 0){
$result = bcpow($base,$parts[0]);
else $result = bcmul(bc_exp(bcmul(bc_ln($base), "0.".$parts[1]), bcpow($base,$parts[0]);
return result;
}
Now we only need to program bc_ln. We can use the same strategy as above:
Take the Taylor-polynomial of the natural logarithm function. (since ln(0) isn't defined, take 1 as developement point instead)
Use Horner's method to drasticly improve performance.
Turn the result into a loop of bc-operations.
Also make use of ln(x) = -ln(1/x) when handling x > 1, to guarantee convergence.
usefull functions(don't forget to set bcscale() before using them)
function bc_fact($f){return $f==1?1:bcmul($f,bc_fact(bcsub($f, '1')));}
function bc_exp($x,$L=50){$r=bcadd('1.0',$x);for($i=0;$i<$L;$i++){$r=bcadd($r,bcdiv(bcpow($x,$i+2),bc_fact($i+2)));}return $r;}#e^x
function bc_ln($x,$L=50){$r=0;for($i=0;$i<$L;$i++){$p=1+$i*2;$r = bcadd(bcmul(bcdiv("1.0",$p),bcpow(bcdiv(bcsub($x,"1.0"),bcadd($x,"1.0")),$p)),$r);}return bcmul("2.0", $r);}#2*Sum((1/(2i+1))*(((x-1)/x+1)^(2i+1)))
function bc_pow($x,$p){return bc_exp(bcmul((bc_ln(($x))), $p));}
I am trying to create a function that generates a random integer out of the bytes I get from /dev/urandom. I am doing this in PHP and it currently looks like:
public static function getRandomInteger($min, $max)
{
// First we need to determine how many bytes we need to construct $min-$max range.
$difference = $max-$min;
$bytesNeeded = ceil($difference/256);
$randomBytes = self::getRandomBytes($bytesNeeded);
// Let's sum up all bytes.
$sum = 0;
for ($a = 0; $a < $bytesNeeded; $a++)
$sum += ord($randomBytes[$a]);
// Make sure we don't push the limits.
$sum = $sum % ($difference);
return $sum + $min;
}
Everything works great except that I think it's not calculating the values exactly fair. For example, if you want to have a random value between 0 and 250, it receives one byte and mods it with 250 so the values of 0-6 are more likely to appear than the values of 7-250. What should I do to fix this?
a) If you don't need cryptographically secure random numbers, simply use mt_rand. It will probably suffice for your needs.
b) If you want to stick with your algorithm: Do some remapping: return round($min + $sum / pow(256, $bytesNeeded) * ($max - $min)).
c) As you can see, this requires rounding. That will lead to a not perfectly uniform distribution, I think (though I am not sure about this). Probably the best way is to get the random number as a float and then scale it. Though I have no idea how you get a float from /dev/urandom. That's why I stick with mt_rand and lcg_value.
I would read $difference bytes from /dev/urandom mod $difference and then add $min
Then make sure $max isn't higher than that number.
I am trying to calculate an average without being thrown off by a small set of far off numbers (ie, 1,2,1,2,3,4,50) the single 50 will throw off the entire average.
If I have a list of numbers like so:
19,20,21,21,22,30,60,60
The average is 31
The median is 30
The mode is 21 & 60 (averaged to 40.5)
But anyone can see that the majority is in the range 19-22 (5 in, 3 out) and if you get the average of just the major range it's 20.6 (a big difference than any of the numbers above)
I am thinking that you can get this like so:
c+d-r
Where c is the count of a numbers, d is the distinct values, and r is the range. Then you can apply this to all the possble ranges, and the highest score is the omptimal range to get an average from.
For example 19,20,21,21,22 would be 5 numbers, 4 distinct values, and the range is 3 (22 - 19). If you plug this into my equation you get 5+4-3=6
If you applied this to the entire number list it would be 8+6-41=-27
I think this works pretty good, but I have to create a huge loop to test against all possible ranges. In just my small example there are 21 possible ranges:
19-19, 19-20, 19-21, 19-22, 19-30, 19-60, 20-20, 20-21, 20-22, 20-30, 20-60, 21-21, 21-22, 21-30, 21-60, 22-22, 22-30, 22-60, 30-30, 30-60, 60-60
I am wondering if there is a more efficient way to get an average like this.
Or if someone has a better algorithm all together?
You might get some use out of standard deviation here, which basically measures how concentrated the data points are. You can define an outlier as anything more than 1 standard deviation (or whatever other number suits you) from the average, throw them out, and calculate a new average that doesn't include them.
Here's a pretty naive implementation that you could fix up for your own needs. I purposely kept it pretty verbose. It's based on the five-number-summary often used to figure these things out.
function get_median($arr) {
sort($arr);
$c = count($arr) - 1;
if ($c%2) {
$b = round($c/2);
$a = $b-1;
return ($arr[$b] + $arr[$a]) / 2 ;
} else {
return $arr[($c/2)];
}
}
function get_five_number_summary($arr) {
sort($arr);
$c = count($arr) - 1;
$fns = array();
if ($c%2) {
$b = round($c/2);
$a = $b-1;
$lower_quartile = array_slice($arr, 1, $a-1);
$upper_quartile = array_slice($arr, $b+1, count($lower_quartile));
$fns = array($arr[0], get_median($lower_quartile), get_median($arr), get_median($upper_quartile), $arr[$c-1]);
return $fns;
}
else {
$b = round($c/2);
$a = $b-1;
$lower_quartile = array_slice($arr, 1, $a);
$upper_quartile = array_slice($arr, $b+1, count($lower_quartile));
$fns = array($arr[0], get_median($lower_quartile), get_median($arr), get_median($upper_quartile), $arr[$c-1]);
return $fns;
}
}
function find_outliers($arr) {
$fns = get_five_number_summary($arr);
$interquartile_range = $fns[3] - $fns[1];
$low = $fns[1] - $interquartile_range;
$high = $fns[3] + $interquartile_range;
foreach ($arr as $v) {
if ($v > $high || $v < $low)
echo "$v is an outlier<br>";
}
}
//$numbers = array( 19,20,21,21,22,30,60 ); // 60 is an outlier
$numbers = array( 1,230,239,331,340,800); // 1 is an outlier, 800 is an outlier
find_outliers($numbers);
Note that this method, albeit much simpler to implement than standard deviation, will not find the two 60 outliers in your example, but it works pretty well. Use the code for whatever, hopefully it's useful!
To see how the algorithm works and how I implemented it, go to: http://www.mathwords.com/o/outlier.htm
This, of course, doesn't calculate the final average, but it's kind of trivial after you run find_outliers() :P
Why don't you use the median? It's not 30, it's 21.5.
You could put the values into an array, sort the array, and then find the median, which is usually a better number than the average anyway because it discounts outliers automatically, giving them no more weight than any other number.
You might sort your numbers, choose your preferred subrange (e.g., the middle 90%), and take the mean of that.
There is no one true answer to your question, because there are always going to be distributions that will give you a funny answer (e.g., consider a biased bi-modal distribution). This is why may statistics are often presented using box-and-whisker diagrams showing mean, median, quartiles, and outliers.