Make rounded values add up to desired product - php

This is a programming question that will require some thinking, and could really be applied to any programming language.
Suppose you have the array:
$arr=array(10,7,4,4,3,2,1,1,1,1,1,1);
This array is dynamic and could be any set of numbers.
You also have a multiplier, lets say 0.6.
The goal is to display each number, one by one, in such a manner that they add as close to the total*multiplier as possible. In this case, the numbers add to 36, *0.6 gives you 21.6
Here's the catch:
You have to round each value (whole numbers only), so your real goal is to make the numbers add as close to 22 as possible.
You cannot use any functions to solve for the sum of the array. You can only loop through it once.
The most basic attempt would be
$multiplier = 0.6;
$sum=0;
foreach($arr AS $value){
$sum+=round($multiplier * $value);
}
But this will not work since 1*0.6 will always round to 1.
I think it is possible with something like this:
$multiplier = 0.6;
$sum=0;
foreach($arr AS $value){
$real=$multiplier * $value;
$rounded=round($multiplier * $value);
$sum_rounded += $rounded;
$sum_real += $real;
//Somehow compare the two sums and break the foreach
}
I don't know where to go from here. What do you guys think?
Tried this:
$sum_real=0;
$sum_round=0;
$count=0;
foreach($rows AS $arr){
$count+=1;
$real_val=$arr*$multiplier;
$sum_round+=round($real_val);
$sum_real+=$real_val;
$avg_round=$sum_round/$count;
$avg_real = $sum_real/$count;
$val = ($avg_round>$avg_real) ? floor($real_val) : round($real_val);
}
But did not work...I think it is getting close though.

Calculate the average for both $sum_rounded and $sum_real at the beginning of each iteration of the foreach loop. If the average for $sum_rounded is larger, use floor() instead of round().
EDIT: You don't actually need to calculate the average at all, since the numbers will both be divided by the same number and that doesn't affect which one is larger. Also, add the $value that is about to be incremented to the comparisons.
foreach($arr AS $value){
$real=$multiplier * $value;
$rounded=round($multiplier * $value);
if($sum_rounded + $rounded > $sum_real + $real) {
$sum_rounded += floor($multiplier * $value);
}
else {
$sum_rounded += $rounded;
}
$sum_real += $real;
}

I think I can talk you into looping twice.
$arr = array(10,7,4,4,3,2,1,1,1,1,1,1);
$out = [];
$multiplier = 0.6;
$sum = round(array_sum($arr) * $multiplier); // that's a loop!
foreach ($arr as $value) {
$value = round($multiplier * $value);
array_push($out, $value);
$sum -= $value;
}
while ($sum > 0) {
$sum --;
$out[$sum] --;
}
while ($sum < 0) {
$sum ++;
$out[-$sum] ++;
}

Here's a 1-pass method:
$orig_sum = 0;
$rounded_sum = 0;
$new_arr = array();
for ($i = 0; $i < count($arr)-1; $i++) {
$orig_sum += $arr[$i];
$new_elt = round($arr[$i] * $multiplier);
$rounded_sum += $new_elt;
$new_arr[] = $new_elt;
}
$orig_sum += $arr[$i];
$expected_rounded_sum = round($orig_sum * $multiplier);
$new_arr[] = $expected_rounded_sum - $rounded_sum;
CODEPAD

Related

PHP: Getting random combinations to specified input

I am trying to display possibilities for additions of specific numbers but have not been getting the right results.
<?php
$num3 = 20;
$set = null;
$balance = $num3;
$dig = mt_rand(1,5);
for($i = $balance; $i > 0; $i -= $dig ){
echo $dig.'<br>';
if($i < 1){
$set .= $i;
$balance = 0;
}
else{
$set .= $dig.'+';
$dig = mt_rand(1,5);
}
}
echo $set.'='.$num3;
?>
Here are some of the outputs:
2+5+1+4+5+3+=20
1+4+3+5+3+=20
3+1+1+2+3+4+4+1+3+=20
Appreciate any pointers. Thank in advance...
Ok, even though the requirement isn't completely clear, here's an approach:
(edit: demonstrating prevention of endless loop)
$max_loops = 1000;
$balance = 20;
$found = [];
while($balance > 0 && $max_loops-- > 0) {
$r = mt_rand(1, 5);
if ($balance - $r >= 0) {
$found[] = $r;
$balance -= $r;
}
}
echo implode(' + ', $found) . ' = '.array_sum($found);
Note: This code has a small risk of getting caught in an endless loop... though it's doubtful that it'll ever happen :)
Edit: Now the loop contains a hard-limit of 1000 iterations, after which the loop will end for sure...
To provoke an endless loop, set $balance = 7 and modify mt_rand(4, 5).
You can use a recursive function for this:
function randomAddends($target, $maxAddend = 5, $sum = 0, $addends = [])
{
// Return the array of addends when the target is reached
if ($target <= $sum) {
return $addends;
}
// generate a new random addend and add it to the array
$randomAddend = mt_rand(1, min($maxAddend, $target - $sum));
$addends[] = $randomAddend;
// make the recursive call with the new sum
return randomAddends($target, $maxAddend, $sum + $randomAddend, $addends);
}

Access PHP Array item value

I have the following script that divides the total by the number of people and stores the results in an array.
Instead of dumping the $result array at the end, I wonder if I could echo out the value of each array item in the for loop as it goes?
// initial conditions
$total = 1001;
$people = 5;
// count what is the minimal value, that all the results will have
$value = floor($total / $people);
$result = array_fill(0, $people, $value);
// distribute missing "+1"s as needed in the result
$overheads = $total - $value * $people;
for ($i = 0; $i < $overheads; $i++) {
$result[$i]++;
}
// voila...
var_dump($result);
So, the above script should loop out:
200
200
200
200
201
Technically speaking, you can't "echo out the value of each array item
in the for loop as it goes" because you're not filling the arrays
in a loop, you're filling with array_fill(). The loop you have is to
work with the remainder. If you echo out there, you'll get partial
results. So either you keep your code and echo the values at the end,
with a simple line:
foreach ($result as $r) echo "$r\n";
Or you modify your code to fill the arrays in another manner. Which
might be interesting. Note that you're forgetting about the modulus
operator, %, for the remainder of division. This line:
$overheads = $total - $value * $people;
Could be written simply as:
$overheads = $total % $people;
An idea to fill in a loop:
// initial conditions
$total = 1001;
$people = 5;
$value = floor($total/$people);
$overheads = $total % $people;
foreach (range(1, $people) as $p) {
$r = $value;
if ($overheads) {
# if there is still remainders:
$r++;
$overheads--;
}
# echo the value and also store it
echo "$r\n";
$result[] = $r;
}
// initial conditions
$total = 1001;
$people = 5;
// count what is the minimal value, that all the results will have
$value = floor($total / $people);
$result = array_fill(0, $people, $value);
// distribute missing "+1"s as needed in the result
$overheads = $total - $value * $people;
$i = 1;
foreach($result as $res)
{
if ($i < $people){
echo $res . "\n";
}else{
echo $res + 1 . "\n";
}
$i++;
}
// voila...

Algorithm for exclude combinations with unique value using php

The algoritham i'm trying different combinations of values will able to give my exact or approx output sum of values
I have attached image for the detail explanation , I have created column total as sum of each row value and finally I have sum all the total value, the whole total sum value is to be my expected output value.
So I'm trying to take a combination of each row sum and like to get total sum value
My algorithm i have searched in google below
function extractList($array, &$list, $temp = array()) {
if (count($temp) > 0 && ! in_array($temp, $list))
$list[] = $temp;
for($i = 0; $i < sizeof($array); $i ++) {
$copy = $array;
$elem = array_splice($copy, $i, 1);
if (sizeof($copy) > 0) {
$add = array_merge($temp, array($elem[0]));
sort($add);
extractList($copy, $list, $add);
} else {
$add = array_merge($temp, array($elem[0]));
sort($add);
if (! in_array($temp, $list)) {
$list[] = $add;
}
}
}
}
echo "<pre>";
$sum = 32 ; //SUM
$array = array(5.14327,5.72355,5.91794,4.8209,8.69933,4.12977,4.12977,2.92791,2.36829,2.21819,1.33759,1.72278,1.72278,0.589,1.06405,0.6387,0.6387,1.68995,2.51669,3.97842,2.38058,2.17175,4.88264,5.84811,6.14215);
$list = array();
# Extract All Unique Conbinations
extractList($array, $list);
#Filter By SUM = $sum
$list = array_filter($list,function($var) use ($sum) { return(array_sum($var) == $sum);});
#Return Output
print_r($list);
Attached Image here
You need to decide how you determine approximate equality. A percentage? Or an absolute amount? That's what you need in your filter lambda function.
// outside lambda
$error = $sum * 5 / 100;// 5%, or
$error = 0.02;// an absolute
...
// inside lambda
return abs(array_sum($var) - $sum) <= $error;

Calculate from an array the number that is equal or higher and closest to a given number

I need to calculate from a given array the number that is equal or higher and closest to a given number in PHP. Example:
Number to fetch:
6.85505196
Array to calculate:
3.11350000
4.38350000
4.04610000
3.99410000
2.86135817
0.50000000
Only correct combination should be:
3.99410000 + 2.86135817 = 6.85545817
Can somebody help me? It's been 3 hours I'm getting mad!
UPDATE: I finally finished my code as following:
$arr = array(3.1135, 4.3835, 4.0461, 3.9941, 2.86135817, 0.5);
$fetch = 6.85505196;
$bestsum = get_fee($arr, $fetch);
print($bestsum);
function get_fee($arr, $fetch) {
$bestsum = 999999999;
$combo = array();
$result = array();
for ($i = 0; $i<count($arr); $i++) {
combinations($arr, $i+1, $combo);
}
foreach ($combo as $idx => $arr) {
$sum = 0;
foreach ($arr as $value) {
$result[$idx] += $value;
}
if ($result[$idx] >= $fetch && $result[$idx] < $bestsum) $bestsum = $result[$idx];
}
return $bestsum;
}
function combinations($arr, $level, &$combo, $curr = array()) {
for($j = 0; $j < count($arr); $j++) {
$new = array_merge($curr, array($arr[$j]));
if($level == 1) {
sort($new);
if (!in_array($new, $combo)) {
$combo[] = $new;
}
} else {
combinations($arr, $level - 1, $combo, $new);
}
}
}
I hope the following example might help you. Please try this
<?php
$array = array(
"3.11350000",
"4.38350000",
"4.04610000",
"3.99410000",
"2.86135817",
"0.50000000"
);
echo "<pre>";
print_r($array);// it will print your array
for($i=0; $i<count($array); $i++)
{
$j=$i+1;
for($j;$j<count($array); $j++)
{
$sum = $array[$i] + $array[$j];
// echo $array[$i]. " + ".$array[$j]." = ".$sum."<br>"; //this will display all the combination of sum
if($sum >= 6.85505196 && ($sum <= round(6.85505196)) )//change the condition according to your requirement
{
echo "The correct combinations are:<br/><br/>";
echo "<b>". $array[$i]. " + ".$array[$j]." = ".$sum."<b>";
echo "<br/>";
}
}
echo "<br/>";
}
?>
We will get the result as below
Array
(
[0] => 3.11350000
[1] => 4.38350000
[2] => 4.04610000
[3] => 3.99410000
[4] => 2.86135817
[5] => 0.50000000
)
The correct combinations are:
4.04610000 + 2.86135817 = 6.90745817
3.99410000 + 2.86135817 = 6.85545817
You should do it in two steps:
a. Work out (or look up) an algorithm to do the job.
b. Implement it.
You don't say what you've managed in the three hours you worked on this, so here's a "brute force" (read: dumb) algorithm that will do the job:
Use a variable that will keep your best sum so far. It can start out as zero:
$bestsum = 0;
Try all single numbers, then all sums of two numbers, then all sums of three numbers, etc.: Every time you find a number that meets your criteria and is better than the current $bestsum, set $bestsum to it. Also set a second variable, $summands, to an array of the numbers you used to get this result. (Otherwise you won't know how you got the solution). Whenever you find an even better solution, update both variables.
When you've tried every number combination, your two variables contain the best solution. Print them out.
That's all. It's guaranteed to work correctly, since it tries all possibilities. There are all sorts of details to fill in, but you can get to work and ask here for help with specific tasks if you get stuck.
Thank you all for your help!
My code is working pretty cool when is needed to fetch one or two numbers (addition) only. But can't figure out how to add more combinations up to the total count of elements in my given array.
I mean if there are, let's say, 8 numbers in my array I want to try all possible combinations (additions to each other) as well.
My actual code is:
$bestsum = 1000000;
for ($i = 0; $i < count($txinfo["vout"]); $i++) {
if ($txinfo["vout"][$i]["value"] >= $spent && $txinfo["vout"][$i]["value"] < $bestsum) {
$bestsum = $txinfo["vout"][$i]["value"];
}
}
for($i = 0; $i < count($txinfo["vout"]); $i++) {
$j = $i + 1;
for($j; $j < count($txinfo["vout"]); $j++) {
$sum = $txinfo["vout"][$i]["value"] + $txinfo["vout"][$j]["value"];
if($sum >= $spent && $sum < $bestsum) {
$bestsum = $sum;
}
}
}
$fee = bcsub($bestsum, $spent, 8);
print("Fee: ".$fee);
New updated code.
<?php
$x = 6.85505196;
$num = array(3.1135, 4.3835, 4.0461, 3.9941, 2.86135817, 0.5);
asort($num); //sort the array
$low = $num[0]; // lowest value in the array
$maxpossible = $x+$low; // this is the maximum possible answer, as we require the number that is equal or higher and closest to a given number
$num = array_values($num);
$iterations = $x/$num[0]; // possible combinations loop, to equate to the sum of the given number using the lowest number
$sum=$num;
$newsum = $sum;
$k=count($num);
for($j=0; $j<=$iterations; $j++){
$l = count($sum);
for($i=0; $i<$l; $i++){
$genSum = $sum[$j]+$sum[$i];
if($genSum <= $maxpossible){
$newsum[$k] = $genSum;
$k++;
}
}
$newsum = array_unique($newsum);
$newsum = array_values($newsum);
$k = count($newsum);
$sum = $newsum;
}
asort($newsum);
$newsum = array_values($newsum);
for($i=0; $i<count($newsum); $i++){
if($x<=$newsum[$i]){
echo "\nMaximum Possible Number = ".$newsum[$i];
break;
}
}
?>

Pair the elements in an array by two's then find the difference and sum

Let's say I have this array
$number = [2,1,4,3,6,2];
First pair the elements on an array by two's and find their difference
so this is the output in the first requirement
$diff[] = [1,1,4];
Second sum all the difference
this is the final output
$sum[] = [6];
Conditions:
the array size is always even
the first element in a pair is always greater than the second one, so their is no negative difference
What I've done so far is just counting the size of an array then after that I don't know how to pair them by two's. T_T
Is this possible in php? Is there a built in function to do it?
One line:
$number = [2,1,4,3,6,2];
$total = array_sum(array_map(function ($array) {
return current($array) - next($array);
}, array_chunk($number, 2)));
echo $total;
This should work fine:
<?
$number = array(2,1,4,3,6,2);
for($i=0;$i<count($number); $i+=2){
$dif[] = $number[$i] - $number[$i+1];
}
print_r($dif);
$sum = 0;
foreach ($dif as $item){
$sum += $item;
}
echo 'SUM = '.$sum;
?>
Working CODE
If you want all the different stages kept,
$numbers = [2,1,4,3,6,2];
$diff = [];
for($i=0,$c=count($numbers);$i<$c;$i+=2)
{
$diff[] = $numbers[$i]-$numbers[$i+1];
}
$sum = array_sum($diff);
Else, to just get the total and bypass the diff array:
$numbers = [2,1,4,3,6,2];
$total = 0;
for($i=0,$c=count($numbers);$i<$c;$i+=2)
{
$total += $numbers[$i]-$numbers[$i+1];
}
I have got this far it gives the required solution.
$arr = array(2,1,4,3,6,2);
$temp = 0;
$diff = array();
foreach ($arr as $key => $value) {
if($key % 2 == 0) {
$temp = $value;
}
else {
$diff[] = $temp - $value;
}
}
print_R($diff);
print 'Total :' . array_sum($diff);
Note : Please update if any one knows any pre-defined function than can sorten this code.
Please check and see if this works for you.
<?php
$sum=0;
$number = array(2,1,4,3,6,2);
for ($i=0;$i<=count($number);$i++) {
if ($i%2 == 1 ) {
$sum = $sum + $number[$i-1] - $number[$i];
}
}
print $sum;
?>
Well with your conditions in mind I came to the following
$number = [2,1,4,3,6,2];
$total = 0;
for($i = 0; $i < count($number); $i+=2) {
$total += $number[$i] - $number[$i + 1];
}
Try this one:
$number = array(2,1,4,3,6,2);
$diff = array();
$v3 = 0;
$i=1;
foreach($number as $val){
if ($i % 2 !== 0) {
$v1 = $val;
}
if ($i % 2 === 0) {
$v2 = $val;
$diff[] = $v1-$v2;
$v3+= $v1-$v2;
}
$i++;
}
print $v3;//total value
print_r($diff); //diff value array

Categories