【问题标题】:Optimizing Find nearest sum of numbers in array to a given number优化查找数组中最接近给定数字的数字总和
【发布时间】:2019-11-27 20:10:55
【问题描述】:

假设我有一个数组 [10000,5000,1000,1000],我想找到与给定数字最接近的数字总和。抱歉解释不好,但这里有一个例子:

假设我有一个数组 [10000,5000,1000,1000] 我想找到最接近的数字,比如 6000。

那么该方法应该返回 5000 和 1000

另一个例子:我们想要最接近 14000 的,那么他应该返回 10000 和 5000

我已经尝试使用下面的代码,这是一个工作,但如果 $desiredSum$numbers 数组 很大。它运行如此缓慢,直到 php 执行超时

$numbers = array(
    10000,5000,1000,1000
);
$desiredSum = 6000;
$minDist = null;
$minDist_I = null;
// Iterate on every possible combination
$maxI = pow(2,sizeof($numbers));
for($i=0;$i<$maxI;$i++) {
    if(!(($i+1) % 1000)) echo ".";

    // Figure out which numbers to select in this 
    $sum = 0;
    for($j=0;$j<sizeof($numbers);$j++) {
        if($i & (1 << $j)) {
            $sum += $numbers[$j];
        }
    }
    $diff = abs($sum - $desiredSum);
    if($minDist_I === null || $diff < $minDist) {
        $minDist_I = $i;
        $minDist = $diff;
    }

    if($diff == 0) break;
}
$chosen = array();
for($j=0;$j<sizeof($numbers);$j++) {
    if($minDist_I & (1 << $j)) $chosen[] = $numbers[$j];
}
echo "\nThese numbers sum to " . array_sum($chosen)  . " (closest to $desiredSum): ";
echo implode(", ", $chosen);
echo "\n";

谁能帮帮我?

【问题讨论】:

  • 这看起来像子集和问题。数组的大小可以有多大?
  • 这不是你之前问过的stackoverflow.com/questions/58918556/…
  • @NigelRen,是的。我之前被问过,但这个解决方案在大阵列上运行缓慢
  • 您是否检查了其他解决方案以了解其执行情况?
  • @vivek_23 大约有 100++ 个元素 .. 因为当我在我的项目上实现时,数据库上也有很多行 .. 然后我将这些行放入 $numbers 数组中......任何想法先生?

标签: php combinations


【解决方案1】:
<?php

function coinChange($numbers,$desiredSum){
    sort($numbers);
    $set = [];
    $set[0] = [];

    for($i = $numbers[0];$i <= $desiredSum;++$i){
        foreach($numbers as $index => $current_number){
            if($i >= $current_number && isset($set[$i - $current_number])){
                if(isset($set[$i - $current_number][$index])) continue;
                $set[$i] = $set[$i - $current_number];
                $set[$i][$index] = true;
                break;
            }
        }
    }

    if(count($set) === 0){
        return [0,[]];
    }

    if(isset($set[$desiredSum])){

        return [
            $desiredSum,
            formatResult($numbers,array_keys($set[$desiredSum]))
        ];
    }else{
        $keys = array_keys($set);
        $nearestSum = end($keys);
        $sum = 0;
        $rev_numbers = array_reverse($numbers);
        $result = [];
        foreach($rev_numbers as $number){
            $sum += $number;
            $result[] = $number;
            if($sum > $nearestSum && abs($nearestSum - $desiredSum) > abs($sum - $desiredSum)){
                $nearestSum = $sum;
                break;
            }else if($sum > $nearestSum && abs($nearestSum - $desiredSum) < abs($sum - $desiredSum)){
                $result = formatResult($numbers,array_keys($set[$nearestSum]));
                break;
            }
        }

        return [
            $nearestSum,
            $result
        ];
    }
}

function formatResult($numbers,$keys){
    $result = [];
    foreach($keys as $key) $result[] = $numbers[$key];
    return $result;
}

print_r(coinChange([10000,5000,1000,1000],14000));
print_r(coinChange([10000,5000,1000,1000],13000));
print_r(coinChange([100000,100000,100000,100000,100000,100000,50000,50000,50000,50000,10000,10000,500,500,500,1000,1000],250000));
print_r(coinChange([100000,100000,100000,100000,100000,100000,50000,50000,50000,50000,10000,10000,500,500,500,1000,1000],179999));

演示: https://3v4l.org/hBGeW

算法:

  • 这类似于coin change问题。

  • 我们首先对数字进行排序。

  • 现在,我们从数组中的最小数字迭代到所需的总和。
  • 在其中,我们遍历数组中的所有元素。
  • 现在,只有我们已经$i - $current_number,我们才能使$i(这是一个总和)。如果我们有前一个,那么我们将$current_number 添加到我们的集合中以求和$i

两种场景:

  • 如果我们可以得出精确的总和,那么我们将按原样返回结果。
  • 如果我们不能,那么有两种可能性:
    • 我们已经在我们的$set 中找到了最接近的总和,这将是最后一个条目。我们将它们保存在一个变量中。
    • 现在,最接近的总和也可能高于所需总和。因此,我们得到较大的和并检查它是否比最接近的最小和更接近,然后比较两者并返回结果。

结果格式:

让我们来看看下面的示例输出:

Array
(
    [0] => 15000
    [1] => Array
        (
            [0] => 10000
            [1] => 5000
        )

)

这只是意味着第一个索引是可能的最接近的总和,而第二个索引处的数组是它从 $numbers 获取的所有元素。

【讨论】:

  • @Aaron 很高兴为您提供帮助:)。但我要求你用不同的输入彻底测试它。
  • ya ya ...我测试了它并且工作完美^_^,但为什么我可以投票给你的答案?...
  • @Aaron 太好了。我希望它不会因为大输入而超出内存限制,并且速度足够快。
  • @Aaron 仅当您的声望为 15 或以上时,您才能投票。 stackoverflow.com/help/privileges/vote-up 目前您的声望为 5,但随着您继续为 SO 做出贡献,您的声望将会增长。
  • 哦,先生..我刚刚发现了一个错误,如果我们将 900 放入我们有 100 = 8 和 1000 = 1 它会给我们全部 100(尽管硬币还不够).. 它应该给我们 1000 然后我们得到零钱 100 ,这是演示:3v4l.org/MtADk
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-08-14
  • 2021-08-09
  • 2012-12-27
  • 1970-01-01
相关资源
最近更新 更多