确定硬币组合的算法

时间:2011-05-05 11:50:35

标签: c++ algorithm combinations combinatorics

我最近遇到了一个编程算法的提示,我不知道该怎么做。我以前从来没有真正写过算法,所以我对此有点新意。

问题在于写一个程序来确定收银员根据硬币价值和硬币数量作为变化回馈所有可能的硬币组合。例如,可能有一个带有4个硬币的货币:2美分,6美分,10美分和15美分硬币。有多少组合等于50美分?

我使用的语言是C ++,虽然这并不重要。

编辑:这是一个更具体的编程问题,但我如何分析C ++中的字符串以获取硬币值?它们是在

等文本文档中给出的
4 2 6 10 15 50 

(这种情况下的数字与我给出的例子相对应)

13 个答案:

答案 0 :(得分:7)

这个问题众所周知是硬币变化问题。有关详细信息,请查看thisthis。此外,如果你谷歌“硬币改变”或“动态编程硬币改变”,那么你将获得许多其他有用的资源。

答案 1 :(得分:7)

这是Java中的递归解决方案:

// Usage: int[] denoms = new int[] { 1, 2, 5, 10, 20, 50, 100, 200 };       
// System.out.println(ways(denoms, denoms.length, 200));
public static int ways(int denoms[], int index, int capacity) {
    if (capacity == 0) return 1;
    if (capacity < 0 || index <= 0 ) return 0;
    int withoutItem = ways(denoms, index - 1, capacity); 
    int withItem = ways(denoms, index, capacity - denoms[index - 1]); 
    return withoutItem + withItem;
}

答案 2 :(得分:5)

这似乎有点像分区,除了你不在1:50使用所有整数。它似乎与装箱问题类似,略有不同:

实际上,在考虑之后,它是an ILP,因而是NP难的。

我建议使用动态编程appyroach。基本上,您可以定义一个值“余数”并将其设置为您的目标(例如,50)。然后,在每一步,您都会执行以下操作:

  1. 弄清楚剩下的最大硬币是什么
  2. 如果您(A)包含该硬币或(B)不包括该硬币,请考虑会发生什么。
  3. 对于每个场景,递归。
  4. 因此,如果余数为50且最大硬币值为25和10,那么您将分为两种情况:

    1. Remainder = 25, Coinset = 1x25
    2. Remainder = 50, Coinset = 0x25
    

    下一步(对于每个分支)可能如下所示:

    1-1. Remainder = 0,  Coinset = 2x25 <-- Note: Remainder=0 => Logged
    1-2. Remainder = 25, Coinset = 1x25
    2-1. Remainder = 40, Coinset = 0x25, 1x10
    2-2. Remainder = 50, Coinset = 0x25, 0x10
    

    每个分支将分成两个分支,除非:

    • 余数为0(在这种情况下,您将记录它)
    • 剩余部分小于最小的硬币(在这种情况下你会丢弃它)
    • 没有剩下的硬币(在这种情况下你会丢弃它,因为剩余的!= 0)

答案 3 :(得分:4)

如果你有15,10,6和2美分硬币,你需要找到有多少不同的方式到达50,你可以

  • 计算使用10,6和2
  • 达到50的不同方式
  • 计算使用10,6和2
  • 达到50-15的不同方法
  • 计算使用10,6和2
  • 达到50-15 * 2的不同方式
  • 计算使用10,6和2
  • 达到50-15 * 3的不同方式
  • 总结所有这些结果当然是截然不同的(在第一次我没有使用15c硬币,在第二次我使用了一个,在第三个和第四个三个)。

因此,您基本上可以将问题分解为较小的问题(可能数量较少,硬币较少)。当你只有一种硬币类型时,答案当然是微不足道的(要么你不能完全达到规定的数量,要么你可以用唯一可能的方式)。

此外,您还可以避免使用记忆重复相同的计算,例如,仅使用[6,2]达到20的方式的数量不依赖于是否已使用15 + 15或10达到已支付的30 + 10 + 10,因此较小问题(20,[6,2])的结果可以 存储和重用。

在Python中,这个想法的实现如下

cache = {}

def howmany(amount, coins):
    prob = tuple([amount] + coins) # Problem signature
    if prob in cache:
        return cache[prob] # We computed this before
    if amount == 0:
        return 1 # It's always possible to give an exact change of 0 cents
    if len(coins) == 1:
        if amount % coins[0] == 0:
            return 1 # We can match prescribed amount with this coin
        else:
            return 0 # It's impossible
    total = 0
    n = 0
    while n * coins[0] <= amount:
        total += howmany(amount - n * coins[0], coins[1:])
        n += 1
    cache[prob] = total # Store in cache to avoid repeating this computation
    return total

print howmany(50, [15, 10, 6, 2])

答案 4 :(得分:1)

至于问题的第二部分,假设您在文件coins.txt中有该字符串:

#include <fstream>
#include <vector>
#include <algorithm>
#include <iterator>

int main() {
    std::ifstream coins_file("coins.txt");
    std::vector<int> coins;
    std::copy(std::istream_iterator<int>(coins_file),
              std::istream_iterator<int>(),
              std::back_inserter(coins));
}

现在,向量coins将包含可能的硬币值。

答案 5 :(得分:1)

对于这么少的硬币,你可以写一个简单的蛮力解决方案。

这样的事情:

#include <iostream>
#include <algorithm>
#include <vector>

using namespace std;

vector<int> v;

int solve(int total, int * coins, int lastI)
{
    if (total == 50) 
    {
        for (int i = 0; i < v.size(); i++)
        {
            cout << v.at(i) << ' ';
        }
        cout << "\n";
        return 1;
    }

    if (total > 50) return 0;

    int sum = 0;

    for (int i = lastI; i < 6; i++)
    {
        v.push_back(coins[i]);
        sum += solve(total + coins[i], coins, i); 
        v.pop_back();
    }

    return sum;
}


int main()
{
    int coins[6] = {2, 4, 6, 10, 15, 50};
    cout << solve(0, coins, 0) << endl;
}

非常脏的蛮力解决方案,可以打印所有可能的组合。

这是一个非常着名的问题,所以请尝试阅读其他人提供的更好的解决方案。

答案 6 :(得分:0)

以下是一种相当愚蠢的方法。您构建映射“值为X的硬币使用Y次”,然后枚举所有可能的组合,并仅选择总和所需总和的那些组合。显然,对于每个值X,您必须检查Y范围从0到所需的总和。这将是相当缓慢的,但将解决您的任务。

答案 7 :(得分:0)

这与the knapsack problem

非常相似

答案 8 :(得分:0)

你基本上必须解决以下等式:50 = a * 4 + b * 6 + c * 10 + d * 15,其中未知数是a,b,c,d。例如,您可以为每个变量计算d =(50 - a * 4 - b * 6 - c * 10)/ 15等等。然后,你开始给出所有可能的值(你应该从具有最小可能值的值开始,这里是d):0,1,2,3,4然后根据当前值开始给出所有可能的值d的值等等。

答案 9 :(得分:0)

向后排序列表:[15 10 6 4 2]

现在50 ct的解决方案可以包含15克拉。 所以解决方案的数量是使用[10 6 4 2](不再考虑15克拉硬币)50 ct的解决方案数量使用35 ct(= 50ct - 15ct)的解决方案数量[15 10 6 4 2]。对这两个子问题重复此过程。

答案 10 :(得分:0)

算法是解决问题的过程,它不必是任何特定的语言。

首先确定输入:

typedef int CoinValue;

set<CoinValue> coinTypes;
int value;

和输出:

set< map<CoinValue, int> > results;

解决你能想到的最简单的案例:

coinTypes = { 1 }; // only one type of coin worth 1 cent
value = 51;

结果应该是:

results = { [1 : 51] }; // only one solution, 51 - 1 cent coins

你会如何解决上述问题?

这个怎么样:

coinTypes = { 2 };
value = 51;

results = { }; // there is no solution

这是怎么回事?

coinTypes = { 1, 2 };
value = { 4 };

results = { [2: 2], [2: 1, 1: 2], [1: 4] }; // the order I put the solutions in is a hint to how to do the algorithm.

答案 11 :(得分:0)

基于Scala中algorithmist.com资源的递归解决方案:

def countChange(money: Int, coins: List[Int]): Int = {
    if (money < 0 || coins.isEmpty) 0
    else if (money == 0) 1
    else countChange(money, coins.tail) + countChange(money - coins.head, coins)
}

答案 12 :(得分:0)

另一个Python版本:

def change(coins, money):
    return (
        change(coins[:-1], money) +
        change(coins, money - coins[-1])
        if money > 0 and coins
        else money == 0
    )