如何转换货币金额进行更改?

时间:2019-09-05 00:55:14

标签: algorithm coin-change

  

给出一美元金额,将其转换为欧元硬币和纸币。您得到了美元   金额作为论点,并表示美元对欧元的汇率为1.30。给你   欧元面额是500法案,200法案,100法案,50法案,20法案,10法案,5法案,2   纸币,1纸币,50美分,25美分,10美分,5美分,2美分,1美分。转换那个   美元金额成为最少的纸币和硬币金额。 (转换数字美元   金额(例如$ 10.00)等值的欧元纸币和硬币。

免责声明:这是我收到的作业问题。

我考虑过使用while循环来解决它,该循环遍历每个面额并将其从值中减去。像这样:

while(amount > 0){
  if(amount - denomination[index] > 0) {
     amount -= denomination[index];
  }else{
     index++;
  }
}

但是其他消息来源告诉我,通过动态编程可以解决硬币找零问题。我很困惑

4 个答案:

答案 0 :(得分:2)

对于这种特定的标称集集更改问题,可以像您一样通过贪婪方法解决。

对于值相差两次(例如1,2,4,8 ...)的集合也是如此,但是规则并不简单,就像@ Patrick87在注释中注意到的那样。适当的货币系统称为“规范的”,但要确定给定的系统是否规范,并不容易:example of discussion

对于任意值,贪婪方法可能会失败
({[1,5,15,20]为{{1}提供20+5+5,而sum=30更好)

这就是为什么通常应该通过动态编程来解决硬币找零问题的原因

答案 1 :(得分:1)

此答案可能还不够“学术”,但是使用JavScript可以将其简化为Array.reduce()的简单应用程序(假设“贪婪”方法适用,将是< / em>(对于欧元系统):

change=amnt=>(c,d,i)=>{var rest=amnt%d;
 if (rest!=amnt) {c[i]=(amnt-rest)/d; amnt=rest;}
 return c };

var rate=110.36; // Euro cents per USD
var res=document.querySelector('#result');

document.querySelector('#USD').onkeyup=ev=>{
 var cents=Math.round(ev.target.value*90.78); // amount in Euro cents
 var denom=[50000,20000,10000,5000,2000,1000,
            5000,2000,1000,500,100,50,20,10,5,2,1];
 var coins=denom.reduce(change(cents),[]);

 res.innerHTML=cents/100+' €<br>'
   +coins.map((n,i)=>n+'x'+(denom[i]>99?denom[i]/100+'€':denom[i]+'ct'))
         .filter(v=>v).join(', ');
}
USD <input type="text" value="13" id="USD">
<div id="result"></div>

答案 2 :(得分:0)

传统上,像设计这样的货币硬币更改问题是动态编程问题。这是一个示例,其中您的方法将以简单的前提为相似问题产生错误的答案:

  

给定无限数量的7 $钞票,5 $钞票,4 $钞票和1 $钞票,以及价格为N $的某些商品,找到购买商品的最佳方式,以便您使用最少的可能有账单。

现在,如果在上一个问题中设置 N = 12 ,您将看到您的算法确实会将12 $分解为1个票据7美元和另一个5美元。但是,如果我设置 N = 9 ,那么您会注意到,当最优解为时,您的算法会将9 $分解为7 $的票据和两张1 $的票据。一张5美元的钞票和一张4美元的钞票

那么您的解决方案正确吗?原来是。这仅仅是因为您的帐单以您的greedy解决方案始终有效的方式给出(为了确保100%的正确性,我对它进行了高达100000.00 $的测试)。我敢肯定,您可以在网上找到资源,这些资源可以告诉您为什么账单值集可以与贪婪算法一起使用的确切原因,而且很遗憾,我无法为您提供令人满意的解释。这是与此问题相关的discussion

尽管您可以使用贪婪算法来解决问题,但动态编程(DP)方法也将给出正确的答案,幸运的是,如果您感到困惑,那么有很多资源可以教您有关DP的知识,例如GeeksForGeeks。如果您在执行DP解决方案时遇到问题,则将代码发布到here

答案 3 :(得分:0)

通常,在硬币系统中确定最佳表示的问题是weakly NP-hard。可能,对于世界上所有当代硬币系统,贪婪算法都可以正常工作。当代硬币系统最经常使用所谓的二进制十进制模式1-2-5。但是您的示例有25美分,需要仔细查看。

但是首先,让我们证明适用于贪婪算法的1-2-5模式。观察到他们的LCM为10,这意味着我们只需要检查[1..9]中的数字。

1 = 1,0,0  4 = 0,2,0  7 = 0,1,1
2 = 0,1,0  5 = 0,0,1  8 = 1,1,1
3 = 1,1,0  6 = 1,0,1  9 = 0,2,1

因此,这种模式是贪婪的。现在,我们将注意力转向前六个面额50、25、10、5、2、1。这里我们有相同的LCM-50。我写了a program来检查这一点:

#include <iostream>
#include <array>
#include <iomanip>
#include <algorithm>
#include <numeric>
#include <iterator>

bool IsOptimal(const int sum, const int numberOfCoins, std::array<int, 6>::const_iterator begin, std::array<int, 6>::const_iterator end) {
    if (sum < 0 || numberOfCoins == 0) return true;
    for (auto it = begin; it < end; ++it) {
        const int nextSum = sum - *it;
        if (nextSum == 0) return numberOfCoins == 1;
        if (!IsOptimal(nextSum, numberOfCoins - 1, it, end))
            return false;
    }
    return true;
}

int main() {
    const std::array<int, 6> kDenoms = { 1,2,5,10,25,50 };
    for (int i = 1; i < 50; ++i) {
        std::array<int, 6> change = { 0 };
        int sum = 0;
        while (sum != i) {
            auto it = std::upper_bound(kDenoms.cbegin(), kDenoms.cend(), i - sum);
            ++change[--it - kDenoms.cbegin()];
            sum += *it;
        }
        const bool isOptimal = IsOptimal(sum, std::accumulate(change.cbegin(), change.cend(), 0), kDenoms.cbegin(), kDenoms.cend());
        std::cout << std::setw(2) << i << ": ";
        std::copy(change.cbegin(), change.cend() - 1, std::ostream_iterator<int>(std::cout, ","));
        std::cout << change.back() << " " << std::boolalpha << isOptimal << std::endl;
    }
    return 0;
}

那么,基本上,我们知道些什么?我们知道,所有数量少于50的我们都可以使用贪婪算法进行攻击以获得最优解。

观察到,所有大于50的面额都可以被50整除,因此它们不会干扰50、25、10、5、2、1。我们还证明了贪心算法适用于模式1-2-5,因此适用于贪婪算法的整个面额。