在PHP中查找总和的最接近值的算法

Posted

技术标签:

【中文标题】在PHP中查找总和的最接近值的算法【英文标题】:Algorithm to find nearest values for total sum in PHP 【发布时间】:2015-11-13 15:14:41 【问题描述】:

我有一个 total_sum 变量(例如 20.00)和数组,它不完全等于 total_sum 的组合,但非常接近: array = [array(8=>1.49), array(1=>8.1)](数组可以有更多值) index 是一个乘数键值:(8*1.49 + 1*8.1) = 20.02 != total_sum。 我需要找到将数组值改进为等于total_sum 的算法。 数组的键不能改变,只能改变值。值只需有两位小数(价格/货币)

所以这个示例数组的结果将是:array(8*1.49 + 1*8.8) [8.1 更改为 8.8 所以total_sum now = 20.00]

有人知道这样的问题,或者这个问题有名字吗?

【问题讨论】:

如果您的问题仅限于浮点数(小数位),您可以尝试使用floorceil 函数。这将找到您计算的值上下的整数,并且您将能够紧密匹配它。 不是从数组值的总和创建total_sum变量的选项,那么它们将相等吗? @Dale:total_sum 无法更改.. @Axalix:不,在您的示例中结果将是:array(1=>3),所以总有解决方案 ***.com/questions/2860432/… 【参考方案1】:

所以在过去的一个小时里我玩得很开心:D。这是结果。希望这是你所期待的。从 cmets 我了解到您只想拥有双精度值。这个函数将循环直到它匹配双精度值。 注意:可能有更好的方法,但这是我想到的第一个。

function correction( $total, $input = array(), &$correction = null, $index = 0 )
    if( count( $input ) < $index )
        return;
    
    $total_sum = 0;
    foreach( $input as $multiplier => $value )
        $total_sum += $multiplier * $value;
    
    $remains = 0;
    $i = 0;
    foreach( $input as $multiplier => $value )
        if( $i !== $index )
            $remains += $multiplier * $value;
        
        $i++;
    
    $rest = $total - $remains;
    reset( $input );
    $current_key = 0;
    for( $i = 0; $i < $index; $i++ )
        next( $input );
    
    $current_key = key( $input );
    if( $current_key !== null )
        $value = $rest / $current_key;
        $precision = strlen( $value ) - strpos( $value, '.' ) - 1;
        if( $precision > 2 )
            $index++;
            correction( $total, $input, $correction, $index );
         else 
            $correction = array(
                'index' => $current_key,
                'value' => $value,
            );
        
    

一些样本数据:

$total = 20;
$input = array(
    8 => 1.49,
    1 => 8.1,
);
correction( $total, $input, $correction );
echo '<pre>'; print_r( $correction ); echo '</pre>';

结果:

Array
(
    [index] => 1
    [value] => 8.08
)

另一个示例:

$total = 20;
$input = array(
    8 => 1.49,
    1 => 8.1,
    3 => 2.1,
);

结果:

Array
(
    [index] => 1
    [value] => 1.78
)

LE:

public static function correction( $total, $input = array(), &$correction = null, $index = 0 )
    if( count( $input ) < $index )
        return;
    
    $total_sum = 0;
    foreach( $input as $data )
        // if input is coming from user then
        // you may want to check if indexes are set
        $total_sum += $data['multiplier'] * $data['value'];
    
    $remains = 0;
    $i = 0;
    foreach( $input as $data )
        if( $i !== $index )
            // same check here
            $remains += $data['multiplier'] * $data['value'];
        
        $i++;
    
    $rest = $total - $remains;
    $value = isset( $input[ $index ]['multiplier'] ) && $input[ $index ]['multiplier'] > 0 ?
                $rest / $input[ $index ]['multiplier'] : 0;
    $precision = strlen( $value ) - strpos( $value, '.' ) - 1;
    if( $precision > 2 )
        $index++;
        self::correction( $total, $input, $correction, $index );
     else 
        $correction = array(
            'index' => $index,
            'value' => $value,
        );
    

$total = 68;
$input = array(
    array(
        'multiplier' => 1,
        'value'      => 1.2,
    ),
    array(
        'multiplier' => 8,
        'value'      => 5,
    ),
);

【讨论】:

我编辑了函数,因为我看到了一个小错误。我返回的是循环 $i 而不是正确的数组索引。所以替换值的方法非常简单$input[ $correction['index'] ] = $correction['value'] 还要检查 $correction 是否不为空,因为如果它为空,那么您就没有可能的 2 位小数精度替换。 我还把$current_key = key( $input );移到了循环之外,因为我只需要设置一次你也可以用$current_key变量替换第二个键调用,所以它就像$value = $first / $current_key; agrr 有人减去这个问题,现在我无法为您的帮助添加分数,因为我的个人资料中的分数很少:/ 好的,我在我的答案中添加了另一个编辑,它应该可以完美运行,还向数组添加了索引,以便您可以更轻松地识别每个子数组。希望它运作良好。用法几乎一样$input[ $correction['index'] ]['value'] = $correction['value']【参考方案2】:

这不是特定于 php 的问题。如果您使用浮点数,最简单的解决方案是始终使用以下公式更改数组的第一个值:

first_value = (total_sum - sum_of_all_other_values_and_multipliers) / multiplier_key

如果您使用整数或有限精度,则存在背包问题的一个实例:https://en.wikipedia.org/wiki/Knapsack_problem

【讨论】:

是的,它通常可以工作,但我的问题并不准确。我只需要使用两位小数的浮点数(价格)。但你的答案对正常的浮点数是正确的。 在这种情况下,你有以下问题:en.wikipedia.org/wiki/Knapsack_problem @axalix: array = [array(2 => 1.5)] ? - 我们不是在谈论exact。如果我们在谈论整数,那么您就有一个背包问题。 @Axalix: first_value = (3 - 0) / 2 = 3 = total_sum :) k,另一个 :) array = [array(7 =&gt; 2)]total_sum = 0.37 然后 2 应替换为 0.052857143。您说:我只需要使用两个小数位浮点数,那么您将使用 0.05 x 7 = 0.35

以上是关于在PHP中查找总和的最接近值的算法的主要内容,如果未能解决你的问题,请参考以下文章

高效实施GTIN-13算法

查找带有回溯的数字组合

在总和匹配的两组整数中查找子集的算法

python kayb算法之从一组序列当中获取一组与目标值最接近的算法

将列表分成两部分,它们的总和彼此最接近

查找列表中哪个数字总和等于某个数字的算法