
本文探讨了如何在给定一组预设数值中,为目标数字寻找最佳的单一组成元素及其倍数,以实现最小化余数。通过分析初始贪婪算法的局限性,我们提出并实现了一种基于遍历、计算与自定义排序的优化策略,确保优先匹配无余数或最小余数的组合,从而高效地找到最接近目标值的构成方案。
在软件开发中,经常会遇到需要将一个目标数值分解为一系列预设构成元素的问题。例如,计算特定金额可以由哪些面额的钞票组成,或者一个总容量可以由哪些规格的容器填充。一个常见的挑战是,当目标数值不能被某个单一构成元素完美整除时,如何找到最接近的构成方案,即产生最小余数的方案。
假设我们有一个目标金额 $amount (例如 3000),以及一组允许的构成元素 $sizes (例如 [1300, 1200, 1100, 1000, 950, 900, 800, 700])。我们的目标是找出 $sizes 中哪个元素,通过乘以某个整数倍数,能够最接近 $amount,同时使余数最小。
一个直观但存在缺陷的初始方法是采用“贪婪算法”:从最大的构成元素开始,尽可能多地减去它,然后对剩余的金额重复此过程。
<?php
$amount = 3000;
$sizes = array(1300, 1200, 1100, 1000, 950, 900, 800, 700); // 假设已按降序排列
$result = array();
$currentAmount = $amount; // 使用一个临时变量来保存当前待分解的金额
foreach ($sizes as $size) {
    $times = floor($currentAmount / $size);
    if ($times > 0) {
        $result[$size] = $times;
        $currentAmount -= $times * $size;
    }
}
echo '<pre>'; print_r($result); echo '</pre>';
?>对于 $amount = 3000,上述代码的输出将是:
立即学习“PHP免费学习笔记(深入)”;
Array
(
    [1300] => 2
)这个结果表明使用了两个 1300,总计 2600,剩余 400。然而,我们可能期望得到的是使用三个 1000,总计 3000,余数为 0 的方案。这揭示了贪婪算法的局限性:它只关注当前步骤的最优选择,而可能错过全局最优解。在这种情况下,因为它优先使用了最大的 1300,导致无法发现 1000 * 3 这种更优的组合。
为了克服贪婪算法的局限性,我们需要一种方法来全面评估 $sizes 数组中的每一个构成元素,并根据其产生的余数和使用次数进行排序。核心思路是:
我们将使用 PHP 来实现这一优化策略:
<?php
$amount = 3000; // 目标金额
$sizes = [ 1300, 1200, 1100, 1000, 950, 900, 800, 700 ]; // 允许的构成元素
$evaluations = []; // 用于存储所有构成元素的评估结果
foreach ($sizes as $size) {
  $times = (int)floor($amount / $size); // 计算该元素能使用的次数
  $remainder = $amount - $times * $size; // 计算剩余金额
  $evaluations[] = [
      'size' => $size,        // 构成元素的值
      'times' => $times,      // 使用次数
      'remainder' => $remainder // 剩余金额
  ];
}
// 使用 usort 进行自定义排序
usort($evaluations, static function ($item1, $item2): int {
  // 首先比较余数:余数小的排在前面
  $comparison = $item1['remainder'] <=> $item2['remainder'];
  // 如果余数相同,则比较使用次数:次数少的排在前面
  return $comparison === 0 ? $item1['times'] <=> $item2['times'] : $comparison;
});
echo '<pre>'; print_r($evaluations); echo '</pre>';
?>运行上述代码,我们将得到一个按优化规则排序的结果数组:
Array
(
    [0] => Array
        (
            [size] => 1000
            [times] => 3
            [remainder] => 0   // 最优结果:余数为0
        )
    [1] => Array
        (
            [size] => 950
            [times] => 3
            [remainder] => 150   // 次优结果
        )
    [2] => Array
        (
            [size] => 700
            [times] => 4
            [remainder] => 200
        )
    [3] => Array
        (
            [size] => 900
            [times] => 3
            [remainder] => 300
        )
    [4] => Array
        (
            [size] => 1300
            [times] => 2
            [remainder] => 400
        )
    [5] => Array
        (
            [size] => 1200
            [times] => 2         // 与下一个元素的余数相同
            [remainder] => 600
        )
    [6] => Array
        (
            [size] => 800
            [times] => 3         // 余数相同,但使用次数更多,因此排在后面
            [remainder] => 600
        )
    [7] => Array
        (
            [size] => 1100
            [times] => 2
            [remainder] => 800
        )
)从输出中可以看到,第一个元素 [0] 即为我们寻找的最佳构成方案:使用 1000 这个构成元素 3 次,恰好等于目标金额 3000,余数为 0。这正是我们希望通过优化算法找到的结果。
通过对目标金额和所有可能构成元素进行全面评估,并结合自定义排序逻辑,我们能够有效地找到在给定构成元素集合中,能以最小余数(或无余数)最接近目标金额的单一构成方案。这种方法避免了贪婪算法可能导致的局部最优解问题,提供了一个更健壮和灵活的数值构成匹配策略。
以上就是优化PHP数值构成:最小化余数的元素匹配算法的详细内容,更多请关注php中文网其它相关文章!
 
                        
                        PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!
 
                Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号