使四舍五入的值加起来等于期望的结果


Make rounded values add up to desired product

这是一个需要思考的编程问题,可以应用于任何编程语言。

假设你有一个数组:

$arr=array(10,7,4,4,3,2,1,1,1,1,1,1);

这个数组是动态的,可以是任何一组数字。

还有一个乘数,比如0.6

目标是一个接一个地显示每个数字,以这样一种方式,它们的加起来尽可能接近总乘子。在本例中,数字相加为36,*0.6得到21.6

这里有个问题:

  1. 你必须四舍五入每个值(只有整数),所以你的真正目标是使数字加起来尽可能接近22。
  2. 您不能使用任何函数来求解数组的和。你只能循环一次。

最基本的尝试是

$multiplier = 0.6;
$sum=0;
foreach($arr AS $value){
$sum+=round($multiplier * $value);
}

但这将不起作用,因为1*0.6总是四舍五入到1。

我认为这样做是可能的:

$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 
}

我不知道从这里该去哪里。你们怎么看?

尝试:

$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);
}

但没有工作…

计算foreach循环每次迭代开始时$sum_rounded$sum_real的平均值。如果$sum_rounded的平均值较大,则使用floor()代替round()

EDIT:实际上根本不需要计算平均值,因为两个数字都会除以相同的数字,这并不影响哪个数字更大。同时,将将要加1的$value添加到比较中。

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;
}

我想我可以说服你循环两次。

$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] ++;
}

这是一个1-pass方法:

$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