【问题标题】:How can I improve this algorithm?我该如何改进这个算法?
【发布时间】:2011-01-30 00:19:37
【问题描述】:

我想要完成的是取一个美国货币金额,并将其分解为每张钞票和硬币的数量,以使用每种类型中的最少金额来获得该金额。

我匆忙写了这篇文章,它确实有效,但我觉得它可以改进。此外,我不得不对余数进行四舍五入,因为一旦它达到 (0.13 - (1 * 0.1) 而不是 0.3 之类的东西,我就会得到一个奇怪的结果,它会变成 0.299999995

下面的代码似乎确实有效。

function moneybreak ($amount, $sizebill) {
    // get units of sizecurrency
    $numbills = floor($amount / $sizebill);
    $remainder = $amount - ($numbills * $sizebill);
    $remainder = round($remainder, 2);
    $ret['bills'] = $numbills;
    $ret['remain'] = $remainder;
    return $ret;
}

$amount = 1999.99;
$money = array();
$tmoney = moneybreak($amount, 500);
$money['fivehundred']   = ($tmoney['bills'] > 0) ? $tmoney['bills'] : 0.00;
$tmoney = moneybreak($tmoney['remain'], 100);
$money['onehundred']        = ($tmoney['bills'] > 0) ? $tmoney['bills'] : 0.00;
$tmoney = moneybreak($tmoney['remain'], 20);
$money['twenty']            = ($tmoney['bills'] > 0) ? $tmoney['bills'] : 0.00;
$tmoney = moneybreak($tmoney['remain'], 10);
$money['ten']               = ($tmoney['bills'] > 0) ? $tmoney['bills'] : 0.00;
$tmoney = moneybreak($tmoney['remain'], 5);
$money['five']              = ($tmoney['bills'] > 0) ? $tmoney['bills'] : 0.00;
$tmoney = moneybreak($tmoney['remain'], 1);
$money['one']               = ($tmoney['bills'] > 0) ? $tmoney['bills'] : 0.00;
$tmoney = moneybreak($tmoney['remain'], 0.25);
$money['quarter']           = ($tmoney['bills'] > 0) ? $tmoney['bills'] : 0.00;
$tmoney = moneybreak($tmoney['remain'], 0.1);
$money['dime']              = ($tmoney['bills'] > 0) ? $tmoney['bills'] : 0.00;
$tmoney = moneybreak($tmoney['remain'], 0.05);
$money['nickle']            = ($tmoney['bills'] > 0) ? $tmoney['bills'] : 0.00;
$tmoney = moneybreak($tmoney['remain'], 0.01);
$money['penny']         = ($tmoney['bills'] > 0) ? $tmoney['bills'] : 0.00;

【问题讨论】:

  • 这就是我们不使用浮点值作为货币的原因。
  • 我会将美分存储为整数。至于分解成位,您可以使用模%
  • 您可能对code review 网站感兴趣,该网站最近进入了公开测试阶段。

标签: php currency


【解决方案1】:

您的问题是why you shouldn't use floating-point arithmetic to represent currency 的完美示例。

首先,您需要避免使用浮点数,例如您欠他们很多钱(尽管实际上可能恰恰相反)。为此,我们将使用美分而不是美元进行计算。您可能拥有的任何金额都需要乘以 100。

然后,您可以通过将所有要使用的货币单位(100 美元、50 美元、20 美元,...)列出到一个数组中,然后按降序排序,以便首先出现最大的货币单位。

<?php

// money units, in cents
// (reflecting common Canadian currency)
$units = array(10000, 5000, 2000, 1000, 500, 200, 100, 25, 10, 5, 1);
function moneyBreak($amount, $units)
{
    // ensure the array is sorted in descending order
    rsort($units);
    $itemsOfEach = array();
    // loop through all the money units
    foreach ($units as $unit)
    {
        // you'll try to use this money unit for the largest possible
        // amount of money
        $itemsOfEach[$unit] = intval($amount / $unit);
        // and once you're done, you'll continue with the remainder
        $amount %= $unit;
    }
    return $itemsOfEach;
}

?>

这是一个例子:

$amount = 32347; // $323.47
$result = moneyBreak($amount, $units);
print_r($result);
/* prints this:
Array
(
    [10000] => 3
    [5000] => 0
    [2000] => 1
    [1000] => 0
    [500] => 0
    [200] => 1
    [100] => 1
    [25] => 1
    [10] => 2
    [5] => 0
    [1] => 2
)
*/

【讨论】:

  • 感谢您提供的详细程度。我现在意识到为什么对货币使用整数值是一种更安全的解决方案。
【解决方案2】:

如果我正确理解了问题,可以快速完成您想做的事情。

$amount = 1999;
$values = array(500, 100, 20, 10, 5, 1);
$result = array();

foreach($values as $value) {
    $result[$value] = floor($amount / $value);
    $amount = $amount % $value;
}

只要你认为合适,也可以使用较小的金额。

编辑:实际上,考虑一下地板部分会在低于 1 时断开,因此您需要添加检查 $amount

【讨论】:

    【解决方案3】:

    好吧,既然你一遍又一遍地做同样的事情,你可以这样做:

    $denominations = array(
                          'fivehundred'=>500,
                          'onehundred'=>100,
                          'twenty'=>20,
                          'etc'=>'etc' //The rest of the denominations
                          );
    
    $amount = 1999.99;
    $money = array();
    
    foreach($denominations as $word => $num)
    {
         $tmoney = moneybreak($amount, $num);
         $money[$word] = ($tmoney['bills'] > 0) ? $tmoney['bills'] : 0.00;
    }
    

    不过,zneak 的回答很好。如果结合包含我的面额密钥的数组,并将他的foreach($units as $units) 修改为foreach($units as $key =&gt; $unit),您可以获得一个具有每个单位的正确密钥的数组(“一百”,“五”等。 )。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-10-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-01-27
      • 1970-01-01
      • 2014-07-03
      • 2017-06-12
      相关资源
      最近更新 更多