Calculating Percentages Error - php
I am trying to fix this percent calculation, however it is just stumping me today.
Here is the code:
$entries = GFAPI::get_entries($form['id'], $search_criteria);
$score = 0;
$max = 0;
$percentage = array();
if(!empty($entries)) {
foreach ($entries as $entry) {
$score = GFSurvey::get_field_score($form_fields, $entry);
$max = end($form_fields['choices']);
if(empty($max['score'])) {
unset($form_fields['choices'][key($form_fields['choices'])]);
$max = end($form_fields['choices']);
}
$max = $max['score'];
$percentage[] = ($score / $max ) * 100;
}
}
$average = round(array_sum($percentage) / count($percentage), 2);
I have the form and i have Not Applicable radio buttons on the form. When a client fills out the form, sometimes on certain questions they need to be N/A because they do not apply and that does not need to counted in the overall total score.
So that is the report generated which the % is incorrect. That percent should read: 94%. In this picture you will see if you click on the graph you can see this:
Graph Once Clicked
It is showing the people who answered this question, and there are 20. There are a total of 5 max points for each person, or in this case I have the N/A box set for blank, which returns 0. What it is doing is totaling all the possible points which are 100. (20 people and 5 max points)
What I need it to do is NOT count the blank fields and in return give me for example in the image Graph Once Clicked there are only 5 people that answered so max points are 25. the total points is 23.5 so 23.5 / 25.
How about this code ? The total percentage of answered questions is in variable $total_percentage.
$entries = GFAPI::get_entries($form['id'], $search_criteria);
$score = 0;
$max = 0;
$total_max = $total_score = 0;
$percentage = array();
if(!empty($entries)) {
foreach ($entries as $entry) {
$score = GFSurvey::get_field_score($form_fields, $entry);
$max = end($form_fields['choices']);
if(empty($max['score'])) {
unset($form_fields['choices'][key($form_fields['choices'])]);
$max = end($form_fields['choices']);
}
$max = $max['score'];
if ($max) {
$total_score += $score;
$total_max += $max;
}
$percentage[] = ($score / $max ) * 100;
}
}
$average = round(array_sum($percentage) / count($percentage), 2);
$total_percentage = ($total_max > 0) ? round($total_score/$total_max/100, 2) : 0;
Related
Achieve 921 and 999 by 39 loops as whole numbers
I am in a situation where I need to achieve divide 921/39 and by giving whole numbers (39 times for loop), achieve 921 again. $packagesCount = count($packages); // = 39 $averageWeight = 921/$packagesCount; // = 23.6153846154 foreach ($packages as $package) { $package['Weight'] = "<whole number>"; } The reason is, I need to give the api whole numbers but the total should be 921. Thus, I can't give round numbers. One way I thought of is: $packagesCount = count($packages); // = 39 $averageWeight = 921/$packagesCount; // = 23.6153846154 $remainder = ceil($averageWeight); // = 24 foreach ($packages as $package) { $package['Weight'] = floor($averageWeight); if ($remainder > 0) { $package['Weight'] += 1; $remainder -= 1; } } But trying it with 999 total weight doesn't work with this approach; instead of 999 in the end, it gives 39 * 25 + 26 = 1001. For 999, I should use 39 * 25 + 24 = 999 but how?
I think you need intdiv and modulo %: $packagesCount = count($packages); $averageWeight = $totalWeight/$packagesCount; $wholeWeight = intdiv($totalWeight,$packagesCount); $weightRest = $totalWeight % $packagesCount; // totalWeight = wholeWeight * packagesCount + weightRest $i = 0; foreach ($packages as $key => $package) { $w = $wholeWeight; if ($i < $weightRest) { $w += 1; } $i+= 1; $packages[$key]['Weight'] = $w; } The idea is that each package while have at least intdiv weight and first weightRest packages will have +1 to their weight. In such a way you will exactly match your totalWeight. See also an online demo P.S. PHP is not my language so the code might be very non-idiomatic. Still I hope it conveys the idea.
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
If-else statement to count some variable
I need some help to solved this algorithm problem with How a Person is popular in his city. My situation How the algorithm should work like If a person "mark" has 500 friends in his city out of 500,000. (500/500,000)*50,000 = 5 So 5 in 50,000 people Know him right. But When friends count increase the 50,000 should decrease If "sam" has 1000 friends then (1000/500,000)*25000 = 5 So 5 in 25000 people know his name Yes we could implement this in if/else condition If so then i have to write 500 lines of code. Is there another way to do this in PHP? <?php $totalfriends = 100; $totali = 5000000; $name = "Sam"; if ($totalfriends >= 100 && $totalfriends <= 499 ) { $r = ($totalfriends/$totali)*50000; echo round($r),' ',"in 50K People on City regonize this Profile"; }else if ($totalfriends >= 500 && $totalfriends <= 999) { $r = ($totalfriends/$totali)*25000; echo round($r),' ',"in 25K People on City know".$name; }else{ echo ""; } ?>
is this what you are looking for? foreach([100, 500, 543, 1000, 5000, 51000, 500000] as $my_friends) echo '5 in '. getScoreOf($my_friends) . "<br>"; function getScoreOf($my_friends){ $of = 5; $total = 5e5; //that's 500,000 ;) $step = 100; //minimum step, so output is not "4604" but "4600" $out_of = $total / $my_friends * $of; return $out_of > $step? round($out_of / $step) * $step: round($out_of); } run it in sandbox edit: solution merged with original code <?php $of = 5; $totalfriends = 100; $name = "Sam"; echo $of ." in ". getScoreOf($of, $totalfriends) ." people in city know ". $name; function getScoreOf($of, $my_friends){ $total = 5e6; //that's 5,000,000 ;) $step = 100; //minimum step, so output is not "4604" but "4600" $out_of = $total / $my_friends * $of; return $out_of > $step? round($out_of / $step) * $step: round($out_of); }
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.
Generate an array in PHP of random number not close to the X previous element
I want to generate in PHP an array of random numbers, but each number should not be the same as any of the X (for example 2 ) numbers bofore it and not even close to any of them by a define range (for example 5). So for example: I need numbers between 1 and 100 i've set my "range" to 5 the first two generated number are 20 and 50. the third number will be a random number between 1 and 100, excluding all the numbers between 15 and 25, and between 45 and 55. I can't figure out a function to achieve it. Ideally I want to call something like this: getRandomNumbers( $min, $max, $previous, $range); where $previous is the number of previous elements to take in consideration when generating the next one and $range is the "proximity" to those number where I don't want the next number to be. I hope I explained in a decent way my request. :) Please, add a comment if you have any question about it.
I just came up with this: function getRandomNumbers($min, $max, $previous, $range) { static $generated = array(); $chunk = array_slice($generated, -$previous); // Added this infinite loop check to save you some headache. if (((($max - $min + 1) / (($range * 2) + 1)) + 1) <= $previous) { die("Values set have the potential of running into an infinite loop. Min: $min, Max: $max, Previous: $previous, Range: $range"); } while(true) { $number = rand($min, $max); $found = true; foreach ($chunk as $value) { if (in_array($number, range($value-$range, $value+$range))) { $found = false; } } if ($found) { $generated[] = $number; return $number; } } } Test it using this: for ($i = 1; $i < 25; $i++) { echo getRandomNumbers(1, 100, 5, 5) . "<br />"; } PHPFiddle Link: http://phpfiddle.org/main/code/51ke-4qzs Edit: Added a check to prevent a possible infinite loop. For example: if you set the following values: $min = 1; $max = 100; $previous = 5; $range = 12; echo getRandomNumbers($min, $max, $previous, $range); Then let's say, in a really unfortunate situation it would generate 13, 38, 63 and 88. So the 5th number cannot be anything between 1 and 25, 26 and 50, 51 and 75, 76 and 100. So it would result in an infinite loop. I've updated the PHPFiddle link as well.
getRandomNumbers( $previous, $range ) { //I'm assuming that previous will be an array of your previous X that you don't want to be close to $num = getRandomNumber() //However you are doing this now foreach( $previous as $key => $value ) { if ( ( $value - $range ) > $num && ( $value + $range ) < $num ) { return getRandomNumbers($previous, $range); } } //You need to also replace a value in previous return num; }