【问题标题】:PHP largest whole number from sum of unsorted arrayPHP未排序数组总和中的最大整数
【发布时间】:2011-09-06 13:09:09
【问题描述】:

谁能告诉我从未排序的数组中求和的最大整数的最佳方法?

例如

{0.1, 0.2, 0.9, 0.5}

Largest whole number possible is 1 (0.1 + 0.9).

{0.9, 0.2, 0.5, 0.3, 0.9}

Largest possible is 2 (0.9 + 0.9 + 0.2)

谢谢

更新

我已经接受了我使用的方法,但是下面的一些方法在编程上是正确的

【问题讨论】:

  • 嗯我认为这与背包问题有关:en.wikipedia.org/wiki/Knapsack_problem 但是,可能有更好的解决方案,因为您比“0-1背包问题”更严格。
  • @Matt 如果集合没有任何可能的整数和怎么办.. 返回 0?即{0.1, 0.1, 0.1, 0.2}
  • @Matt 还是 {0.7, 0.7, 0.7} ?不能由此得出整数,那么您希望输出什么?
  • 好点,我正在使用的数据集将长达数千,所以我怀疑我会发现这个问题,但我想要覆盖驴子,需要返回 FALSE
  • @Matt,数千人?这将扼杀十几头驴子,遍历所有可能的组合。对于一个包含十个值的示例数据集,将有超过 100 次计算。

标签: php math sum


【解决方案1】:

这样的?

 $array=array(0.1, 0.2, 0.9, 0.5);
 arsort($array);
 while($highest=array_shift($array)){
   foreach($array as $val){
    $thisval=($highest+val);
    if(round($thisval)==($thisval)){
     return $thisval;
   }
 }
}

编辑:@Felix Kling 你是绝对正确的,添加了一个 while 循环

【讨论】:

  • ?我不熟悉语法:-/
  • @trey:我的意思是,如果你有这些价值观呢?您的代码仅将最高值与所有其他代码相加。在这种情况下,产生整数的总和是 0.70.3,但您永远不会测试这些。
  • 这很好,但它是否处理涉及超过 2 个数字的总和?例如,{0.1, 0.2, 0.6, 0.7} - 最大的整数和将是 0.7 + 0.2 + 0.1
  • 嗯,真的……也许先求和然后再尝试删除值?我不确定在没有大量迭代的情况下最有效的处理方法是什么
  • @trey:因此这是一个背包问题。除了蛮力,没有简单的解决方案。
【解决方案2】:

我还没有为此编写代码,但伪代码如下。一般的想法是您计算组合(x 选择 y...http://en.wikipedia.org/wiki/Combination),然后对这些组合中的每一个求和。你迭代这个数组的长度,然后取你的最大值。

我也确信有一些关于贪婪和短路这个循环的优化。

$len = count($decimals);
$maxVals = array();

for( $choose = $len; $choose > 1; $choose-- ) {
    // get $decimals choose $choose

    // loop and sum each choose array, checking for an integer sum

    // store max if exists
}

return sum($maxVals);

【讨论】:

    【解决方案3】:

    可以考虑一下。

    $sum = array_sum($arr);
    $floor_sum = floor($sum);
    if ($sum = $floor_sum){
    return $sum
    }else{
    
    for ($i = 0; $i < sizeof($arr); $i++){
      if ($sum - $arr[$i] = $floor_sum){
         return $sum - $arr[i];
      }else{
        for ($x = 0; $x < sizeof($arr)- 1; $x++){
          if ($x != $i){
            if ($sum - $arr[$i] - $arr[$x] = $floor_sum){
             return $sum - $arr[$i] - $arr[$x];
            }else {
                //Iterate more times
            }
          }
        }
      }
    }
    
    }
    

    以上我都有,但一定有更简单的方法吗?

    【讨论】:

    • 该死,这假设 $floor_sum 实际上是可能的
    • 我想我可以从 $floor_sum 中减去 1,然后重新开始......有人有免费的 BlueGene 吗?
    【解决方案4】:

    这段代码大部分用于获取数组的排列。我确信它可以优化,但这是在四核单 Xeon 服务器上计算 45 毫秒内长度为 4、5 和 6 的 3 个数组。当我添加一个带有 7 个小数的第 4 个数组时,跳转到大约 220 毫秒,如果我添加一个带有 8 个小数的第 5 个数组,则跳转到惊人的 2 秒。

    基本上,所有这些都是获取包含浮点数的数组的所有排列,将每个排列键逐个键相加,如果总和是大于当前整数的整数,则更新该数字。最终返回最大可能的数字。

    $set1 = array(0.1, 0.2, 0.9, 0.5);
    $set2 = array(0.9, 0.2, 0.5, 0.3, 0.9);
    $set3 = array(0.9, 0.2, 0.5, 0.3, 0.9, 0.4);
    
    function largestWholeNumber($decimals){
        echo "Calculating largest whole number for decimal set '"
        . implode(",", $decimals)
        . "'<br />";
        $perms = perms($decimals);
        $answer = 0;
        foreach($perms as $dec_array){
            $current_guess = 0;
            foreach($dec_array as $dec){
                $current_guess += $dec;
                if (!preg_match("/[^0-9]+/", $current_guess)){
                    if ($answer < $current_guess){
                        $answer = $current_guess;
                        echo "New whole number found " 
                        ."'$answer'"
                        ." with permutation: <br />";
                        print_r($dec_array);
                        echo "<br />";
                    }
                }
            }
        }
        echo "Result: $answer<br /><br />";
        return $answer;
    }
    
    function factorial($int){
    if($int < 2) {
           return 1;
    }
    
    for($f = 2; $int-1 > 1; $f *= $int--);
    
    return $f;
    }
    
    
    function perms($arr) {
        $p = array();
        for ($i=0; $i < factorial(count($arr)); $i++) {
            $p[] = perm($arr, $i);
        }
        return $p;
    }
    
    
    function perm($arr, $nth = null) {
    
        if ($nth === null) {
            return perms($arr);
        }
    
        $result = array();
        $length = count($arr);
    
        while ($length--) {
            $f = factorial($length);
            $p = floor($nth / $f);
            $result[] = $arr[$p];
            array_delete_by_key($arr, $p);
            $nth -= $p * $f;
        }
    
        $result = array_merge($result,$arr);
        return $result;
    }
    function array_delete_by_key(&$array, $delete_key, $use_old_keys = FALSE) {
    
        unset($array[$delete_key]);
    
        if(!$use_old_keys) {
            $array = array_values($array);
        }
    
        return TRUE;
    }
    
    largestWholeNumber($set1); // 1
    largestWholeNumber($set2); // 2
    largestWholeNumber($set3); // 3
    

    数组置换函数的功劳归于dirk dot avery a t gmailhttp://php.net/manual/en/function.shuffle.php

    【讨论】:

    • 添加了一个回声,这样你就可以看到它是如何得出答案的。
    【解决方案5】:

    这是一个值得思考的大问题。在我的脑海中,这是我要使用的伪代码:

    1. A = 元素数组。
    2. A' = 已排序的元素数组。
    3. S = 所有元素的总和。
    4. 当 A' 不为空时:
      1. V = S 的整数部分。
      2. R = S 的余数 = S - floor(S)
      3. 制作 A', X 的临时副本。
      4. 从 X 的最大值到最小值迭代为 X[i]:
        1. 如果 X[i]
        2. 如果 R == 0,我们找到了解决方案。 V 是整数,X 包含加数。停止。
      5. 如果 X 为空,则无解。停止。
      6. 将 S 减去 A' 中的最大值。 S = S - Max(A')。从 A' 中删除 Max(A')。
    5. 返回到第 4 步。

    当然,我必须看看这是否真的有效。这是我为测试它而编写的(非常杂乱、质量差的)代码:

    <?php
    $AA = $A = array(0.1, 0.2, 0.9, 0.5);
    
    bcscale(8);
    
    sort($AA, SORT_NUMERIC);
    echo 'A = ' . implode(', ', $A), PHP_EOL;
    echo 'A\' = ' . implode(', ', $AA), PHP_EOL;
    
    $S = array_sum($AA);
    echo 'S = ' . $S, PHP_EOL;
    
    while (count($AA)) {
        $V = floor($S);
        echo 'V = ' . $V, PHP_EOL;
    
        $R = bcsub($S, $V);
        echo 'R = ' . $R, PHP_EOL;
    
        $X = $AA;
        $XX = array();
    
        // Look for the largest value that is less than or equal to R.
        for ($i = count($X) - 1; $i >= 0; $i--) {
            echo 'X[i] = ' . $X[$i] . ', R = ' . $R, PHP_EOL;
            $c = bccomp($X[$i], $R);
            if ($c > 0) {
                continue;
            }
            $XX[] = $X[$i];
            $R = bcsub($R, $X[$i]);
            unset($X[$i]);
    
            if (bccomp($R, '0', strlen($R)) == 0) {
                echo 'Largest whole number sum: ' . $V, PHP_EOL;  
                echo 'Elements: ' . implode(', ', $X), PHP_EOL;   
                break 2;
            }
        }
    
        if (count($X) == 0) {
            echo 'No sums to a whole number are possible.', PHP_EOL;
            break;
        }
    
        $t = array_pop($AA);
        $S = bcsub($S, $t);
    }
    
    echo 'S = ' . $S, PHP_EOL;
    ?>
    

    这是一个丑陋的 O(N^2) 算法,但它应该是正确的。任何人都可以看到会失败的初始起始数组吗?

    为了好玩,我尝试了一个包含 50 个元素的数组,将第一行替换为以下几行:

    $A = array();
    for ($i = 0; $i < 50; $i++) {
        $A[] = mt_rand(1, 99) / 100.0;
    }
    $AA = $A;
    

    乍一看,它看起来是正确的 - 我将把验证留给其他人;)

    【讨论】:

      【解决方案6】:

      我建议对整个数组求和,然后找到小数部分等于整个总和的最小总和。除非数字在小数点后具有非常高的精度,否则无论找到确切数字的方法是什么,这种反转都应该节省大量计算。

      此外,对数组进行排序并从最小的数字开始贪婪可能会产生不错的结果。然而,最优解很大程度上取决于初始集的性质。您能否就您期望的数字提供更详细的说明?

      【讨论】:

      • 感谢大卫,这与我所追求的相似。老实说,准确性不是问题,但会很好。所以,我对数组求和,然后对它进行升序排序,并从总和中取出数组中的第一个数字并检查它是否是一个 int。它不会总是让我得到最高的数字,但足以满足我的需要。谢谢
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-11-27
      • 1970-01-01
      • 1970-01-01
      • 2015-09-16
      相关资源
      最近更新 更多