Skip to content

Instantly share code, notes, and snippets.

@imbyc
Last active July 27, 2023 08:09
Show Gist options
  • Save imbyc/551845efc33c16d18973677001c7c20a to your computer and use it in GitHub Desktop.
Save imbyc/551845efc33c16d18973677001c7c20a to your computer and use it in GitHub Desktop.
[PHP实现简单找零功能] #找零

使用示例:

// 示例使用
try {
    $amount = 1.60; // 表示 1.60 的金额

    $coins = [
        ['den' => 100, 'qty' => 2], // 2个面额为 100 的硬币
        ['den' => 50, 'qty' => 1], // 1个面额为 50 的硬币
        ['den' => 20, 'qty' => 3], // 3个面额为 20 的硬币
        ['den' => 10, 'qty' => 3], // 3个面额为 10 的硬币
        ['den' => 5, 'qty' => 1], // 1个面额为 5 的硬币
        ['den' => 0.1, 'qty' => 50], // 50个面额为 1毛 的硬币
        // 可以继续添加其他硬币面额和数量
    ];

    $change = makeChange($amount, $coins);
    print_r($change);
} catch ( \InvalidArgumentException $e ) {
    echo "Error: " . $e->getMessage();
}
<?php
/**
* 用于计算找零的函数。
*
* @param float $amount 待找零的金额(必须为非负数)
* @param array $coins 可用的硬币面额和数量,格式要求:
* [
* ['den' => float, 'qty' => int],
* ['den' => float, 'qty' => int],
* // 可以继续添加其他硬币面额和数量
* ]
* 注意:面额('den')必须为正数,数量('qty')必须为非负整数。
* @return array 找零所使用的硬币组成的数组
* @throws \InvalidArgumentException 当 $amount 不是非负数或 $coins 格式不满足要求时抛出异常
*/
function makeChange($amount, $coins)
{
// 检查金额是否为非负数
if (!is_numeric($amount) || $amount < 0) {
throw new \InvalidArgumentException("无效的金额。请提供非负数。");
}
// 根据面额从高到低排序
usort($coins, function ($a, $b) {
return $b['den'] <=> $a['den'];
});
$totalAvailableAmount = 0;
foreach ($coins as $coin) {
// 检查硬币面额和数量是否满足要求
if (!is_numeric($coin['den']) || $coin['den'] <= 0 ||
!is_int($coin['qty']) || $coin['qty'] < 0) {
throw new \InvalidArgumentException("无效的硬币格式。每个硬币的 'den' 必须是正数,'qty' 必须是非负整数。");
}
$totalAvailableAmount += $coin['den'] * $coin['qty'];
}
// 检查可用硬币是否足够找零
if ($amount > $totalAvailableAmount) {
throw new \InvalidArgumentException("可用硬币数量不足以找零。");
}
$change = [];
$amountInCents = (string)($amount * 100); // 将金额转换为最小单位(如分)
foreach ($coins as &$coin) {
$denominationInCents = (string)($coin['den'] * 100); // 将面额转换为最小单位(如分)
// 使用 bccomp() 来比较金额,避免浮点数精度问题
while (\bccomp($amountInCents, $denominationInCents, 2) >= 0 && $coin['qty'] > 0) {
$amountInCents = \bcsub($amountInCents, $denominationInCents, 2);
$change[] = $coin['den'];
$coin['qty']--;
}
}
// 检查是否有剩余金额无法找零
if (\bccomp($amountInCents, '0', 2) > 0) {
$change = []; // 无法找零,清空结果数组
}
return $change;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment