Bailey-Borwein-Plouffe in php trouble - php
I am trying to implement the BBP algorithm in php. My code is returning a decimal which i thought was odd as it should be in hex. I was told to convert to decimal from hex by multiplying by 16 also but now its all just wrong. Here is a sample:
$n1=$n2=$n3=$n4=$n5=$n6=$n7=$n8 =0;
$S1=$S2=$S3=$S4=$S5=$S6=$S7=$S8 = 0; //initializing
$k = 0;
$m1= 8*$k + 1;
$m2 = 8*$k + 4;
$m3 = 8*$k + 5;
$m4 = 8*$k = 6;
$b =16;
$e=$n-$k;
while($k<$n){ //Sum 1 of 8
$S1 +=Modular($b, $m1, $e)/$m1; //see Moduler_Expansion.php
$k++;
}
$k = $n +1; //redefine for second sum, and every other
while($k<$limit){ //Sum 2 of 8
$S2 += (pow($b,$n-$k))/($m1);
$k++; //now repeat similar process for each sum.
}
and I repeat the process for each term of BBP then:
$S = 4*($S1 + $S2) - 2*($S3+$S4) -($S5+$S6) - ($S7+$S8);
`
Following the wiki page I then strip the integer and multiply by 16, but for $k =0 I get; 3.4977777777778
and for $k = 1: 7.9644444444448.
I dont think these are right, it could just be i do not know how to interpret th ouput properly. Can anyone offer any advice?
Related
Find the highest product in 4 directions in a matrix
I got this challenge to find the highest product of 4 consecutive numbers on a 20x20 matrix of integers. The numbers are read line by line from a file separated by a space. The products can be in horizontal, vertical and diagonal in both directions My "solution" gives the wrong answer. EDIT: I've updated the code to work without file input and added sample data; also fixed one of my mistakes that were pointed out in the comments $data = [ [89,32,92,64,81,2,20,33,44,1,70,75,39,62,76,35,16,77,22,27], [53,11,6,95,41,51,31,59,8,23,19,13,61,91,48,69,84,52,66,24], [93,72,85,97,21,79,56,5,45,3,65,30,83,87,43,7,34,0,4,14], [29,17,49,9,82,90,55,67,15,63,54,94,12,28,96,37,58,98,86,78], [74,40,50,60,26,99,80,18,10,46,36,68,25,57,47,71,42,73,88,38], [50,22,6,26,18,53,52,5,46,2,89,77,83,48,4,58,45,28,84,81], [49,82,31,14,69,17,91,54,34,40,0,33,30,95,60,44,29,24,85,16], [27,11,76,39,15,86,92,74,99,59,94,12,55,57,38,96,47,32,78,75], [51,20,87,42,62,41,7,35,23,21,71,25,67,97,80,90,88,64,13,70], [19,9,56,43,68,93,65,98,36,3,61,63,10,72,8,73,1,66,79,37], [22,58,52,12,3,41,28,72,42,74,76,64,59,35,85,78,14,27,53,88], [46,80,5,96,7,68,61,69,67,34,36,40,82,26,75,50,29,91,10,2], [30,39,19,48,33,93,1,45,66,98,0,23,62,25,51,71,56,77,24,21], [79,87,94,60,8,32,13,65,4,92,73,9,31,37,17,84,15,90,86,20], [95,6,81,70,47,16,44,83,49,43,55,54,18,63,38,11,97,89,99,57], [95,78,64,58,7,17,53,28,74,86,6,12,54,85,21,94,16,69,25,68], [13,20,41,97,1,2,80,30,0,84,67,45,93,96,82,92,62,33,18,44], [60,77,31,70,76,36,59,38,15,3,91,46,65,73,49,11,8,35,5,52], [61,66,79,40,26,72,89,71,75,99,22,9,43,32,14,81,98,88,87,83], [10,4,23,19,56,57,51,47,50,27,90,63,42,29,24,55,48,37,39,34] ]; $matrix = []; //maximums in possible directions $maxes = [0, 0, 0, 0]; //while ($line = trim(fgets(STDIN))) { while ($line = current($data)) { //the horizontal maxes can be calculated while loading //$array = explode(" ", $line); $array = $line; $hMax = array_product(array_slice($array, 0, 4)); for ($i = 1; $i < (count($array)-4); $i++) { $max = array_product(array_slice($array, $i, 4)); if($max > $hMax) { $hMax = $max; } } if ( $hMax > $maxes[0] ) { $maxes[0] = $hMax; } $matrix[] = $array; next($data); } // the last 3 rows can be skipped for($i = 0; $i < (count($matrix)-4); $i++) { for ($j = 0; $j < (count($matrix[$i])-1); $j++) { $vMax = 1; // vertical $dlMax = 1; // diagonal left $drMax = 1; // diagonal rigth for ($k = 0; $k < 5; $k++) { $vMax *= $matrix[$i + $k][$j]; if ( $j < (count($matrix[$i]) - 4) ) { $drMax *= $matrix[$i + $k][$j + $k]; } if ( $j > 3 ) { $dlMax *= $matrix[$i + $k][$j - $k]; } } if ( $maxes[1] < $vMax ) $maxes[1] = $vMax; // the index used to be 1 - my first mistake if ( $maxes[2] < $dlMax ) $maxes[2] = $dlMax; // the index used to be 1 - my first mistake if ( $maxes[3] < $drMax ) $maxes[3] = $drMax; // the index used to be 1 - my first mistake } } sort($maxes); echo end($maxes).PHP_EOL; Where did my approach go wrong, and how can it be sped up? Are there any math tricks that can be applied here (besides checking for zeros)? EDIT: the solution that the code gives for the current data is 4912231320 is it correct?
I've found 2 major errors, and now the result is a plausible 67352832 I'm considering it solved for that reason, but if anyone comes up with some math trick that simplifies or makes it faster I'll give up the accepted answer. The first mistake was for ($k = 0; $k < 5; $k++) { It should've been for ($k = 0; $k < 4; $k++) { since we are only counting 4 numbers at once, thats why the result was so large compared to 10^8 The second was if ( $j > 3 ) { which should've been if ( $j > 2 ) { which will now include one more diagonal possibility
We can consider the four directions a bottom- or right-most cell can be the last of in a sequence. If m[i][j][k][d] is the highest total for a sequence of length k coming from direction d, then: m[i][j][1][d] = data[i][j] for all d m[i][j][k]['E'] = data[i][j] * m[i][j - 1][k - 1]['E'] m[i][j][k]['NE'] = data[i][j] * m[i - 1][j - 1][k - 1]['NE'] m[i][j][k]['N'] = data[i][j] * m[i - 1][j][k - 1]['N'] m[i][j][k]['NW'] = data[i][j] * m[i - 1][j + 1][k - 1]['NW'] If we traverse north to south, east to west, the needed cells should have already been calculated, and, clearly, we're looking for max(m[i][j][4][d]) for all i, j, d
generating same number with the given length
I have this math assignment that I should make into code. I've tried all I thought of but I couldn't find a solution. All this should be done without using php functions, only math operations. You can use while, for, and such... So I have number for example 9 Now I should create number of the length 9 which would be 999999999 If I had, for example, number 3, then the result should be 333. Any ideas? $gen = -1; while($highest > 0) { $gen = $highest + ($highest * 10); $highest = $highest - 1; } echo $gen;
Here is a method that does not build a string; it uses pure math. (There will be many, many ways to do this task) $x=9; $result=0; for($i=$x; $i; --$i){ // this looping expression can be structured however you wish potato-potatoe $result+=$x*(10**($i-1)); // x times (10 to the power of (i-1)) } echo $result; // 999999999 *note: ** acts like pow() if you want to look it up. Late edit: here is a clever, little loopless method (quietly proud). I am only calling range() and foreach() to demo; it is not an integral component of my method. Demo: https://3v4l.org/GIjfG foreach(range(0,9) as $n){ // echo "$n -> ",(integer)(1/9*$n*(10**$n)-($n/10)),"\n"; // echo "$n -> ",(1/9*$n*(10**$n)-(1/9*$n)),"\n"; // echo "$n -> ",(int)(1/9*10**$n)*$n,"\n"; // echo "$n -> ",(int)(10**$n/9)*$n,"\n"; echo "$n -> ",(10**$n-1)/9*$n,"\n"; } Output: 0 -> 0 1 -> 1 2 -> 22 3 -> 333 4 -> 4444 5 -> 55555 6 -> 666666 7 -> 7777777 8 -> 88888888 9 -> 999999999 1/9 is the hero of this method because it generates .111111111(repeating). From this float number, I am using 10**$n to "shift" just enough 1s to the left side of the decimal point, then multiplying this float number by $n, then the float must be converted to an integer to complete. Per #axiac's comment, the new hero is 10**$n-1 which generates a series of nines to the desired length (no float numbers). Next divide the nines by nine to generate a series of ones which becomes the perfect multiplier. Finally, multiply the series of ones and the input number to arrive at the desired output.
There are two operations you need to accomplish: given a number $number, append the digit $n to it; repeat operation #1 some number of times ($n times). Operation #1 is easy: $number = $number * 10 + $n; Operation #2 is even easier: for ($i = 0; $i < $n; $i ++) What else do you need? Initialization of the variable used to store the computed number: $number = 0; Put them in order and you get: // The input digit // It gives the length of the computed number // and also its digits $n = 8; // The number we compute $number = 0; // Put the digit $n at the end of $number, $n times for ($i = 0; $i < $n; $i ++) { $number = $number * 10 + $n; } // That's all
If intval() is accepted: $result = ''; $input = 9; for($i=0; $i < $input; $i++){ $result .= $input; } $result = intval($result); else: $result = 0; $input = 9; for($i=0; $i < $input; $i++){ $factor = 1; for($j = 0; $j < $i; $j++){ $factor *= 10; } $result += $input * $factor; } => 9 + 90 + 900 + 9000 + 90000...
How to generate random numbers to produce a non-standard distributionin PHP
I've searched through a number of similar questions, but unfortunately I haven't been able to find an answer to this problem. I hope someone can point me in the right direction. I need to come up with a PHP function which will produce a random number within a set range and mean. The range, in my case, will always be 1 to 100. The mean could be anything within the range. For example... r = f(x) where... r = the resulting random number x = the mean ...running this function in a loop should produce random values where the average of the resulting values should be very close to x. (The more times we loop the closer we get to x) Running the function in a loop, assuming x = 10, should produce a curve similar to this: + + + + + + + + + Where the curve starts at 1, peeks at 10, and ends at 100. Unfortunately, I'm not well versed in statistics. Perhaps someone can help me word this problem correctly to find a solution?
interesting question. I'll sum it up: We need a funcion f(x) f returns an integer if we run f a million times the average of the integer is x(or very close at least) I am sure there are several approaches, but this uses the binomial distribution: http://en.wikipedia.org/wiki/Binomial_distribution Here is the code: function f($x){ $min = 0; $max = 100; $curve = 1.1; $mean = $x; $precision = 5; //higher is more precise but slower $dist = array(); $lastval = $precision; $belowsize = $mean-$min; $abovesize = $max-$mean; $belowfactor = pow(pow($curve,50),1/$belowsize); $left = 0; for($i = $min; $i< $mean; $i++){ $dist[$i] = round($lastval*$belowfactor); $lastval = $lastval*$belowfactor; $left += $dist[$i]; } $dist[$mean] = round($lastval*$belowfactor); $abovefactor = pow($left,1/$abovesize); for($i = $mean+1; $i <= $max; $i++){ $dist[$i] = round($left-$left/$abovefactor); $left = $left/$abovefactor; } $map = array(); foreach ($dist as $int => $quantity) { for ($x = 0; $x < $quantity; $x++) { $map[] = $int; } } shuffle($map); return current($map); } You can test it out like this(worked for me): $results = array(); for($i = 0;$i<100;$i++){ $results[] = f(20); } $average = array_sum($results) / count($results); echo $average; It gives a distribution curve that looks like this:
I'm not sure if I got what you mean, even if I didn't this is still a pretty neat snippet: <?php function array_avg($array) { // Returns the average (mean) of the numbers in an array return array_sum($array)/count($array); } function randomFromMean($x, $min = 1, $max = 100, $leniency = 3) { /* $x The number that you want to get close to $min The minimum number in the range $max Self-explanatory $leniency How far off of $x can the result be */ $res = [mt_rand($min,$max)]; while (true) { $res_avg = array_avg($res); if ($res_avg >= ($x - $leniency) && $res_avg <= ($x + $leniency)) { return $res; break; } else if ($res_avg > $x && $res_avg < $max) { array_push($res,mt_rand($min, $x)); } else if ($res_avg > $min && $res_avg < $x) { array_push($res, mt_rand($x,$max)); } } } $res = randomFromMean(22); // This function returns an array of random numbers that have a mean close to the first param. ?> If you then var_dump($res), You get something like this: array (size=4) 0 => int 18 1 => int 54 2 => int 22 3 => int 4 EDIT: Using a low value for $leniency (like 1 or 2) will result in huge arrays, since testing, I recommend a leniency of around 3.
PHP infinity loop [While]
I am having some problems with PHP. I used while to sum a number's digits always that it has more than two digits, some how, it gets into an infinity loop. e.g: 56 = 5 + 6 = 11 = 1+1= 2. Here is the code: $somaP = 0; $numPer = (string)$numPer; //$numPer = number calculated previously while (strlen($numPer) > 1){ for ($j = 0; $j < strlen($numPer); $j++){ $somaP = $somaP + (int)($numPer[$j]); } $numPer = (string) $somaP; } Can anyone help me? Guess it is a simple mistake, but I couldn't fix it.
You need to reset the value of $somaP in your while loop. Currently it continues to increase its value every time through the loop. Try this: $numPer = (string)$numPer; //$numPer = number calculated previously while (strlen($numPer) > 1){ $somaP = 0; for ($j = 0; $j < strlen($numPer); $j++){ $somaP = $somaP + (int)($numPer[$j]); } $numPer = (string) $somaP; }
Take a look at this line: $numPer = (string) $somaP; It seems that the length of $somaP is never lesser (or equal) than 1. So the length of $numPer is never lesser (or equal) than 1.
What are you trying to do? It's unclear to me. This for example would add every number in a string together? E.g "1234" = 1+2+3+4 = 10 $total = 0; for($i = 0; i < strlen($string); $i++){ $total += $string[$i]; } echo $total;
This looks cleaner I would say: $numPer = 56; while ($numPer > 9){ $numPer = array_sum(str_split($numPer)); } echo $numPer; PHP handles all string <> number conversions for you, so no need to do (string) on a number unless really needed.
Simple PHP program requires less time to execute
i had applied for a job recently and the requirement was to complete a test and then interview 2 questions were given for test which was very simple and i did it successfully but still i was told that i have failed the test because the script took more than 18 seconds to complete execution. here is the program i dont understand what else i could do to make it fast. although i have failed the test but still wants to know else i could do? Program language is PHP and i had to do it using command line input here is the question: K Difference Given N numbers , [N<=10^5] we need to count the total pairs of numbers that have a difference of K. [K>0 and K<1e9] Input Format: 1st line contains N & K (integers). 2nd line contains N numbers of the set. All the N numbers are assured to be distinct. Output Format: One integer saying the no of pairs of numbers that have a diff K. Sample Input #00: 5 2 1 5 3 4 2 Sample Output #00:3 Sample Input #01: 10 1 363374326 364147530 61825163 1073065718 1281246024 1399469912 428047635 491595254 879792181 1069262793 Sample Output #01: 0 Note: Java/C# code should be in a class named "Solution" Read input from STDIN and write output to STDOUT. and this is the solution $fr = fopen("php://stdin", "r"); $fw = fopen("php://stdout", "w"); fscanf($fr, "%d", $total_nums); fscanf($fr, "%d", $diff); $ary_nums = array(); for ($i = 0; $i < $total_nums; $i++) { fscanf($fr, "%d", $ary_nums[$i]); } $count = 0; sort($ary_nums); for ($i = $total_nums - 1; $i > 0; $i--) { for ($j = $i - 1; $j >= 0; $j--) { if ($ary_nums[$i] - $ary_nums[$j] == $diff) { $count++; $j = 0; } } } fprintf($fw, "%d", $count);
Your algorithm's runtime is O(N^2) that is approximately 10^5 * 10^5 = 10^10. With some basic observation it can be reduced to O(NlgN) which is approximately 10^5*16 = 1.6*10^6 only. Algorithm: Sort the array ary_nums. for every i'th integer of the array, make a binary search to find if ary_nums[i]-K, is present in the array or not. If present increase result, skip i'th integer otherwise. sort($ary_nums); for ($i = $total_nums - 1; $i > 0; $i--) { $hi = $i-1; $low = 0; while($hi>=$low){ $mid = ($hi+$low)/2; if($ary_nums[$mid]==$ary_nums[$i]-$diff){ $count++; break; } if($ary_nums[$mid]<$ary_nums[$i]-$diff){ $low = $mid+1; } else{ $hi = $mid-1; } } } }
I got the same question for my technical interview. I wonder if we are interviewing for the same company. :) Anyway, here is my answer I came up with (after the interview): // Insert code to get the input here $count = 0; sort ($arr); for ($i = 0, $max = $N - 1; $i < $max; $i++) { $lower_limit = $i + 1; $upper_limit = $max; while ($lower_limit <= $upper_limit) { $midpoint = ceil (($lower_limit + $upper_limit) / 2); $diff = $arr[$midpoint] - $arr[$i]; if ($diff == $K) { // Found it. Increment the count and break the inner loop. $count++; $lower_limit = $upper_limit + 1; } elseif ($diff < $K) { // Search to the right of midpoint $lower_limit = $midpoint + 1; } else { // Search to the left of midpoint $upper_limit = $midpoint - 1; } } } #Fallen: Your code failed for the following inputs: Enter the numbers N and K: 10 3 Enter numbers for the set: 1 2 3 4 5 6 7 8 9 10 Result: 6 I think it has to do with your calculation of $mid (not accounting for odd number)