PHP中优化数字构成查找:最小余数与最优单类型组合算法

碧海醫心
发布: 2025-10-29 10:42:02
原创
843人浏览过

PHP中优化数字构成查找:最小余数与最优单类型组合算法

本文旨在解决如何从给定数字集合中,为目标数字寻找一个最优的单类型构成组合。针对传统贪婪算法在处理非精确整除时可能出现的局限性,我们提出一种改进方法。该方法通过独立计算每个可用构成数能提供的最大倍数及其剩余量,并对结果进行智能排序,以优先选择余数最小且使用次数合理的组合,从而实现更精准的数字分解近似。

在许多实际应用场景中,我们可能需要将一个目标数值分解为由一组预定义数值(构成数)的组合。例如,在库存管理或资金分配中,我们需要从一系列固定面额或尺寸的物品中,尽可能精确地凑出或接近一个目标总量。一个常见的挑战是,当目标数值无法被构成数精确整除时,如何找到一个“最接近”的组合,或者说,一个剩余量最小的组合。

传统贪婪算法的局限性

一种直观的解决方案是采用贪婪算法:总是优先使用当前能使用的最大构成数,并尽可能多地使用它,然后用剩余的量继续这个过程。让我们看一个PHP示例:

<?php
$amount = 3000;
$sizes = array(1300, 1200, 1100, 1000, 950, 900, 800, 700); // 建议降序排列以符合贪婪策略

$result = array();
$remainingAmount = $amount; // 使用一个变量来跟踪剩余量

foreach ($sizes as $size) {
    if ($remainingAmount >= $size) {
        $times = floor($remainingAmount / $size);
        $result[$size] = $times;
        $remainingAmount -= $times * $size;
    } else {
        $result[$size] = 0;
    }
}

echo '<pre>'; print_r($result); echo '</pre>';
?>
登录后复制

对于 $amount = 3000,上述代码的输出将是:

Array
(
    [1300] => 2
    [1200] => 0
    [1100] => 0
    [1000] => 0
    [950] => 0
    [900] => 0
    [800] => 0
    [700] => 0
)
登录后复制

这里,1300 * 2 = 2600,剩余 400。虽然 1300 * 2 是一个有效的组合,但我们知道 1000 * 3 = 3000 是一个更好的选择,因为它没有剩余量。这表明简单的贪婪算法可能无法找到全局最优的近似解,尤其是在寻求最小余数时。其主要原因在于,贪婪算法一旦选择了一个构成数并减去了相应的量,就不会回头考虑其他可能更好的组合。

立即学习PHP免费学习笔记(深入)”;

优化算法:基于最小余数和使用次数的排序

为了克服贪婪算法的局限性,我们可以采用一种不同的策略:不立即减少目标数值,而是针对每个可用的构成数,独立地计算它能提供的最大倍数以及此时产生的剩余量。然后,我们对这些计算结果进行排序,以找到最优的近似解。这里的“最优”定义为:首先余数最小,如果余数相同,则使用次数较少的方案(或根据具体需求选择使用次数较多的方案)。

即构数智人
即构数智人

即构数智人是由即构科技推出的AI虚拟数字人视频创作平台,支持数字人形象定制、短视频创作、数字人直播等。

即构数智人 36
查看详情 即构数智人

算法步骤

  1. 遍历所有构成数: 对于给定的目标数值 $amount 和构成数数组 $sizes 中的每一个 $size。
  2. 计算倍数和余数:
    • 计算当前 $size 能整除 $amount 的最大次数 ($times = floor($amount / $size))。
    • 计算此时的剩余量 ($remainder = $amount - $times * $size)。
  3. 存储结果: 将 $size、$times 和 $remainder 作为一个临时结果存储起来。
  4. 排序结果: 对所有临时结果进行排序。
    • 主要排序键: $remainder,按升序排列(最小余数优先)。
    • 次要排序键: $times,按升序或降序排列,具体取决于业务需求。例如,如果希望在余数相同的情况下优先选择使用次数较少的(更“紧凑”的)组合,则按升序排列。如果希望优先选择使用次数较多的(可能更“分散”的)组合,则按降序排列。在我们的示例中,我们选择升序。

PHP实现

<?php
$amount = 3000;
// 构成数数组,顺序不影响最终结果,因为是独立计算
$sizes = [ 1300, 1200, 1100, 1000, 950, 900, 800, 700 ];

$results = []; // 注意:这里改名为 $results 以避免与 $result 混淆

foreach ($sizes as $size) {
  $times = (int)floor($amount / $size); // 计算当前构成数能使用的最大次数
  $remainder = $amount - $times * $size; // 计算此时的剩余量

  $results[] = [
      'size' => $size,
      'times' => $times,
      'remainder' => $remainder
  ];
}

// 使用 usort 进行自定义排序
usort($results, static function ($item1, $item2): int {
  // 首先按 'remainder' 升序排序
  $comparison = $item1['remainder'] <=> $item2['remainder'];

  // 如果 'remainder' 相同,则按 'times' 升序排序
  // 这样在余数相同的情况下,会优先选择使用次数较少的方案
  return $comparison === 0 ? $item1['times'] <=> $item2['times'] : $comparison;
});

echo '<pre>'; print_r($results); echo '</pre>';
?>
登录后复制

输出分析

运行上述代码,输出结果如下:

Array
(
    [0] => Array
        (
            [size] => 1000
            [times] => 3
            [remainder] => 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] 提供了最佳结果:size 为 1000,times 为 3,remainder 为 0。这正是我们期望的 1000 * 3 = 3000 的完美组合。

值得注意的是,在 remainder 均为 600 的情况下,[5] 元素 (1200 * 2) 比 [6] 元素 (800 * 3) 靠前,这是因为我们设置了次要排序键为 times 的升序,即在余数相同的情况下,优先选择使用次数更少的方案 (2 zuojiankuohaophpcn 3)。

注意事项与扩展

  1. 问题定义: 本文提供的解决方案旨在找到一个单类型构成数(例如 X * N)来最佳近似目标数值。如果您的需求是寻找多种不同类型构成数的组合(例如 A * N1 + B * N2 + C * N3)来达到目标或最小余数,那么这属于更复杂的背包问题或找零问题范畴,通常需要动态规划或回溯算法来解决。本教程的方案不适用于这类多类型组合问题。
  2. 性能考量: 对于较小的构成数数组,这种遍历和排序的方法效率很高。如果构成数数组非常庞大,可能需要考虑更优化的数据结构或算法。
  3. 浮点数处理: 如果目标数值或构成数是浮点数,需要特别注意浮点数比较的精度问题,可能需要使用 bcmath 扩展进行精确计算,而不是直接使用 floor 和减法。
  4. 排序规则: 次要排序键(times)的选择应根据具体业务需求调整。例如,如果更倾向于使用尽可能多的构成数来分解,即使余数相同,可以将 times 设置为降序排序。
  5. 无解情况: 如果目标数值为负数或构成数数组为空,代码需要添加额外的逻辑进行处理,以避免错误或产生无意义的结果。

总结

通过对每个构成数独立进行计算,并结合多级排序策略,我们能够有效地找到给定目标数值的最佳单类型构成组合,尤其是在存在非精确整除情况时。这种方法比简单的贪婪算法更具鲁棒性,能够提供更符合“最小余数”或“最接近”原则的解决方案,为资源分配、库存优化等场景提供了实用的技术支持。

以上就是PHP中优化数字构成查找:最小余数与最优单类型组合算法的详细内容,更多请关注php中文网其它相关文章!

PHP速学教程(入门到精通)
PHP速学教程(入门到精通)

PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载
来源:php中文网
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
最新问题
开源免费商场系统广告
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新 English
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送
PHP中文网APP
随时随地碎片化学习

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号