数か月前にインタビュー準備のために書いていたコードを見つけました。
私が持っていたコメントによると、それはこの問題を解決しようとしていました:
セント単位のドル価値(200 = 2ドル、1000 = 10ドルなど)が与えられた場合、ドル価値を構成するコインのすべての組み合わせを見つけます。ペニー(1¢)、ニッケル(5¢)、ダイム(10¢)、クォーター(25¢)のみが許可されています。
たとえば、100が指定された場合、答えは次のようになります。
4 quarter(s) 0 dime(s) 0 nickel(s) 0 pennies
3 quarter(s) 1 dime(s) 0 nickel(s) 15 pennies
etc.
これは反復的方法と再帰的方法の両方で解決できると思います。私の再帰的な解決策は非常にバグが多く、他の人がこの問題をどのように解決するのか疑問に思っていました。この問題の難しい部分は、可能な限り効率的にすることでした。
私はずっと前にこれを調べましたが、あなたは私の それについてのちょっとした説明 を読むことができます。 Mathematicaソース です。
生成関数を使用することにより、問題に対する閉形式の定時間解を得ることができます。 Graham、Knuth、PatashnikのConcrete Mathematicsはこのための本であり、問題に関するかなり広範な議論が含まれています。基本的に、n番目の係数がnドルの変更方法の数である多項式を定義します。
記事の4〜5ページでは、Mathematica(またはその他の便利なコンピューター代数システム)を使用して、3行のコードで数秒で10 ^ 10 ^ 6ドルの答えを計算する方法を示しています。
(そして、これは75Mhz Pentiumで数秒であるほどかなり前でした...)
注:これはウェイの数のみを表示します。
Scala関数:
def countChange(money: Int, coins: List[Int]): Int =
if (money == 0) 1
else if (coins.isEmpty || money < 0) 0
else countChange(money - coins.head, coins) + countChange(money, coins.tail)
私は再帰的な解決策を好むでしょう。金額のリストがあります。最小の金額で残りの通貨額を均等に分割できる場合は、これで問題ありません。
基本的に、最大額面から最小額面に移動します。
再帰的に、
ここに、あなたの問題のpythonバージョンを200セントで示します。 1463通りの方法があります。このバージョンでは、すべての組み合わせと最終カウントの合計が印刷されます。
#!/usr/bin/python
# find the number of ways to reach a total with the given number of combinations
cents = 200
denominations = [25, 10, 5, 1]
names = {25: "quarter(s)", 10: "dime(s)", 5 : "nickel(s)", 1 : "pennies"}
def count_combs(left, i, comb, add):
if add: comb.append(add)
if left == 0 or (i+1) == len(denominations):
if (i+1) == len(denominations) and left > 0:
if left % denominations[i]:
return 0
comb.append( (left/denominations[i], demoninations[i]) )
i += 1
while i < len(denominations):
comb.append( (0, denominations[i]) )
i += 1
print(" ".join("%d %s" % (n,names[c]) for (n,c) in comb))
return 1
cur = denominations[i]
return sum(count_combs(left-x*cur, i+1, comb[:], (x,cur)) for x in range(0, int(left/cur)+1))
count_combs(cents, 0, [], None)
Scala関数:
def countChange(money: Int, coins: List[Int]): Int = {
def loop(money: Int, lcoins: List[Int], count: Int): Int = {
// if there are no more coins or if we run out of money ... return 0
if ( lcoins.isEmpty || money < 0) 0
else{
if (money == 0 ) count + 1
/* if the recursive subtraction leads to 0 money left - a prefect division hence return count +1 */
else
/* keep iterating ... sum over money and the rest of the coins and money - the first item and the full set of coins left*/
loop(money, lcoins.tail,count) + loop(money - lcoins.head,lcoins, count)
}
}
val x = loop(money, coins, 0)
Console println x
x
}
すべての組み合わせを表示するように要求した問題を解決するためのいくつかの絶対に簡単なC++コードがあります。
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
if (argc != 2)
{
printf("usage: change amount-in-cents\n");
return 1;
}
int total = atoi(argv[1]);
printf("quarter\tdime\tnickle\tpenny\tto make %d\n", total);
int combos = 0;
for (int q = 0; q <= total / 25; q++)
{
int total_less_q = total - q * 25;
for (int d = 0; d <= total_less_q / 10; d++)
{
int total_less_q_d = total_less_q - d * 10;
for (int n = 0; n <= total_less_q_d / 5; n++)
{
int p = total_less_q_d - n * 5;
printf("%d\t%d\t%d\t%d\n", q, d, n, p);
combos++;
}
}
}
printf("%d combinations\n", combos);
return 0;
}
しかし、組み合わせの数を単に計算するという副次的な問題については非常に興味があります。閉形式の方程式があると思う。
これは本当に古い質問ですが、Javaで他のすべてのものよりも小さいように思える再帰的な解決策を思い付きました。
public static void printAll(int ind, int[] denom,int N,int[] vals){
if(N==0){
System.out.println(Arrays.toString(vals));
return;
}
if(ind == (denom.length))return;
int currdenom = denom[ind];
for(int i=0;i<=(N/currdenom);i++){
vals[ind] = i;
printAll(ind+1,denom,N-i*currdenom,vals);
}
}
改善点:
public static void printAllCents(int ind, int[] denom,int N,int[] vals){
if(N==0){
if(ind < denom.length) {
for(int i=ind;i<denom.length;i++)
vals[i] = 0;
}
System.out.println(Arrays.toString(vals));
return;
}
if(ind == (denom.length)) {
vals[ind-1] = 0;
return;
}
int currdenom = denom[ind];
for(int i=0;i<=(N/currdenom);i++){
vals[ind] = i;
printAllCents(ind+1,denom,N-i*currdenom,vals);
}
}
コードはこの問題を解決するためにJavaを使用していますが、これも動作します...ループが多すぎるため、この方法はお勧めできませんが、実際には簡単な方法です。
public class RepresentCents {
public static int sum(int n) {
int count = 0;
for (int i = 0; i <= n / 25; i++) {
for (int j = 0; j <= n / 10; j++) {
for (int k = 0; k <= n / 5; k++) {
for (int l = 0; l <= n; l++) {
int v = i * 25 + j * 10 + k * 5 + l;
if (v == n) {
count++;
} else if (v > n) {
break;
}
}
}
}
}
return count;
}
public static void main(String[] args) {
System.out.println(sum(100));
}
}
サブ問題は、典型的なダイナミックプログラミングの問題です。
/* Q: Given some dollar value in cents (e.g. 200 = 2 dollars, 1000 = 10 dollars),
find the number of combinations of coins that make up the dollar value.
There are only penny, nickel, dime, and quarter.
(quarter = 25 cents, dime = 10 cents, nickel = 5 cents, penny = 1 cent) */
/* A:
Reference: http://andrew.neitsch.ca/publications/m496pres1.nb.pdf
f(n, k): number of ways of making change for n cents, using only the first
k+1 types of coins.
+- 0, n < 0 || k < 0
f(n, k) = |- 1, n == 0
+- f(n, k-1) + f(n-C[k], k), else
*/
#include <iostream>
#include <vector>
using namespace std;
int C[] = {1, 5, 10, 25};
// Recursive: very slow, O(2^n)
int f(int n, int k)
{
if (n < 0 || k < 0)
return 0;
if (n == 0)
return 1;
return f(n, k-1) + f(n-C[k], k);
}
// Non-recursive: fast, but still O(nk)
int f_NonRec(int n, int k)
{
vector<vector<int> > table(n+1, vector<int>(k+1, 1));
for (int i = 0; i <= n; ++i)
{
for (int j = 0; j <= k; ++j)
{
if (i < 0 || j < 0) // Impossible, for illustration purpose
{
table[i][j] = 0;
}
else if (i == 0 || j == 0) // Very Important
{
table[i][j] = 1;
}
else
{
// The recursion. Be careful with the vector boundary
table[i][j] = table[i][j-1] +
(i < C[j] ? 0 : table[i-C[j]][j]);
}
}
}
return table[n][k];
}
int main()
{
cout << f(100, 3) << ", " << f_NonRec(100, 3) << endl;
cout << f(200, 3) << ", " << f_NonRec(200, 3) << endl;
cout << f(1000, 3) << ", " << f_NonRec(1000, 3) << endl;
return 0;
}
C(i、J)を、セットJの値を使用してiセントを作成する組み合わせのセットとします。
Cを次のように定義できます。
(first(J)は決定論的にセットの要素を取ります)
それはかなり再帰的な関数になります...メモ化を使用する場合は合理的に効率的です;)
ユニークな組み合わせの問題を回避するためのセミハック-降順を強制する:
$ denoms = [1,5,10,25] def all_combs(sum、last) sum == 0 の場合1を返します$ denoms.select {| d |を返しますd&le sum && d&le last} .inject(0){| total、denom | total + all_combs(sum-denom、denom)} end
これはメモされないので遅くなりますが、アイデアは得られます。
# short and sweet with O(n) table memory
#include <iostream>
#include <vector>
int count( std::vector<int> s, int n )
{
std::vector<int> table(n+1,0);
table[0] = 1;
for ( auto& k : s )
for(int j=k; j<=n; ++j)
table[j] += table[j-k];
return table[n];
}
int main()
{
std::cout << count({25, 10, 5, 1}, 100) << std::endl;
return 0;
}
var countChange = function (money,coins) {
function countChangeSub(money,coins,n) {
if(money==0) return 1;
if(money<0 || coins.length ==n) return 0;
return countChangeSub(money-coins[n],coins,n) + countChangeSub(money,coins,n+1);
}
return countChangeSub(money,coins,0);
}
これがPythonでの私の答えです。再帰を使用しません。
def crossprod (list1, list2):
output = 0
for i in range(0,len(list1)):
output += list1[i]*list2[i]
return output
def breakit(target, coins):
coinslimit = [(target / coins[i]) for i in range(0,len(coins))]
count = 0
temp = []
for i in range(0,len(coins)):
temp.append([j for j in range(0,coinslimit[i]+1)])
r=[[]]
for x in temp:
t = []
for y in x:
for i in r:
t.append(i+[y])
r = t
for targets in r:
if crossprod(targets, coins) == target:
print targets
count +=1
return count
if __== "__main__":
coins = [25,10,5,1]
target = 78
print breakit(target, coins)
出力例
...
1 ( 10 cents) 2 ( 5 cents) 58 ( 1 cents)
4 ( 5 cents) 58 ( 1 cents)
1 ( 10 cents) 1 ( 5 cents) 63 ( 1 cents)
3 ( 5 cents) 63 ( 1 cents)
1 ( 10 cents) 68 ( 1 cents)
2 ( 5 cents) 68 ( 1 cents)
1 ( 5 cents) 73 ( 1 cents)
78 ( 1 cents)
Number of solutions = 121
両方:すべての額面を高から低に反復し、額面の1つを取り、必要な合計から差し引いて、残りを再帰します(利用可能な額面を現在の反復値以下に制限します)。
これは非常に古い質問です。私は適切な答えを探していましたが、シンプルで満足できるものは見つかりませんでした。しばらく時間がかかりましたが、何かを書き留めることができました。
function denomination(coins, original_amount){
var original_amount = original_amount;
var original_best = [ ];
for(var i=0;i<coins.length; i++){
var amount = original_amount;
var best = [ ];
var tempBest = [ ]
while(coins[i]<=amount){
amount = amount - coins[i];
best.Push(coins[i]);
}
if(amount>0 && coins.length>1){
tempBest = denomination(coins.slice(0,i).concat(coins.slice(i+1,coins.length)), amount);
//best = best.concat(denomination(coins.splice(i,1), amount));
}
if(tempBest.length!=0 || (best.length!=0 && amount==0)){
best = best.concat(tempBest);
if(original_best.length==0 ){
original_best = best
}else if(original_best.length > best.length ){
original_best = best;
}
}
}
return original_best;
}
denomination( [1,10,3,9] , 19 );
これはJavaScriptソリューションであり、再帰を使用します。
通貨システムで許可されている場合、単純な 貪欲アルゴリズム は、最も価値の高い通貨から始めて、できるだけ多くの各コインを受け取ります。
それ以外の場合、この問題は本質的に knapsack problem であるため、最適なソリューションをすばやく見つけるには動的プログラミングが必要です。
たとえば、通貨システムに{13, 8, 1}
というコインがある場合、貪欲なソリューションは{13, 8, 1, 1, 1}
として24の変更を行いますが、真の最適なソリューションは{8, 8, 8}
です。
編集:私は、1ドルで変更を行うためのすべての方法をリストするのではなく、最適に変更を行っていると思った。私の最近のインタビューでは、どのように変更を加えるかを尋ねられたので、質問を読み終える前に先に進みました。
Scalaプログラミング言語では、次のようにします。
def countChange(money: Int, coins: List[Int]): Int = {
money match {
case 0 => 1
case x if x < 0 => 0
case x if x >= 1 && coins.isEmpty => 0
case _ => countChange(money, coins.tail) + countChange(money - coins.head, coins)
}
}
O'reily著の「Python For Data Analysis」という本で、このきれいなコードを見つけました。遅延実装とint比較を使用し、小数を使用して他の単位に変更できると仮定します。どのように機能するか教えてください!
def make_change(amount, coins=[1, 5, 10, 25], hand=None):
hand = [] if hand is None else hand
if amount == 0:
yield hand
for coin in coins:
# ensures we don't give too much change, and combinations are unique
if coin > amount or (len(hand) > 0 and hand[-1] < coin):
continue
for result in make_change(amount - coin, coins=coins,
hand=hand + [coin]):
yield result
C#関数は次のとおりです。
public static void change(int money, List<int> coins, List<int> combination)
{
if(money < 0 || coins.Count == 0) return;
if (money == 0)
{
Console.WriteLine((String.Join("; ", combination)));
return;
}
List<int> copy = new List<int>(coins);
copy.RemoveAt(0);
change(money, copy, combination);
combination = new List<int>(combination) { coins[0] };
change(money - coins[0], coins, new List<int>(combination));
}
次のように使用します。
change(100, new List<int>() {5, 10, 25}, new List<int>());
以下を印刷します。
25; 25; 25; 25
10; 10; 10; 10; 10; 25; 25
10; 10; 10; 10; 10; 10; 10; 10; 10; 10
5; 10; 10; 25; 25; 25
5; 10; 10; 10; 10; 10; 10; 10; 25
5; 5; 10; 10; 10; 10; 25; 25
5; 5; 10; 10; 10; 10; 10; 10; 10; 10; 10
5; 5; 5; 10; 25; 25; 25
5; 5; 5; 10; 10; 10; 10; 10; 10; 25
5; 5; 5; 5; 10; 10; 10; 25; 25
5; 5; 5; 5; 10; 10; 10; 10; 10; 10; 10; 10
5; 5; 5; 5; 5; 25; 25; 25
5; 5; 5; 5; 5; 10; 10; 10; 10; 10; 25
5; 5; 5; 5; 5; 5; 10; 10; 25; 25
5; 5; 5; 5; 5; 5; 10; 10; 10; 10; 10; 10; 10
5; 5; 5; 5; 5; 5; 5; 10; 10; 10; 10; 25
5; 5; 5; 5; 5; 5; 5; 5; 10; 25; 25
5; 5; 5; 5; 5; 5; 5; 5; 10; 10; 10; 10; 10; 10
5; 5; 5; 5; 5; 5; 5; 5; 5; 10; 10; 10; 25
5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 25; 25
5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 10; 10; 10; 10; 10
5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 10; 10; 25
5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 10; 10; 10; 10
5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 10; 25
5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 10; 10; 10
5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 25
5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 10; 10
5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 10
5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5
これは、請求書を受け取り、合計に達するまで小さい請求書を再帰的に取得し、同じ額面の別の請求書を取得して、再び再帰する単純な再帰アルゴリズムです。実例については、以下のサンプル出力を参照してください。
var bills = new int[] { 100, 50, 20, 10, 5, 1 };
void PrintAllWaysToMakeChange(int sumSoFar, int minBill, string changeSoFar)
{
for (int i = minBill; i < bills.Length; i++)
{
var change = changeSoFar;
var sum = sumSoFar;
while (sum > 0)
{
if (!string.IsNullOrEmpty(change)) change += " + ";
change += bills[i];
sum -= bills[i];
if (sum > 0)
{
PrintAllWaysToMakeChange(sum, i + 1, change);
}
}
if (sum == 0)
{
Console.WriteLine(change);
}
}
}
PrintAllWaysToMakeChange(15, 0, "");
次を印刷します。
10 + 5
10 + 1 + 1 + 1 + 1 + 1
5 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1
5 + 5 + 1 + 1 + 1 + 1 + 1
5 + 5 + 5
1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1
ああ、私は今ばかげている。以下に、非常に複雑な解決策がありますが、それはis解決策なので、私はこれを保存します。簡単な解決策は次のとおりです。
// Generate a pretty string
val coinNames = List(("quarter", "quarters"),
("dime", "dimes"),
("nickel", "nickels"),
("penny", "pennies"))
def coinsString =
Function.tupled((quarters: Int, dimes: Int, nickels:Int, pennies: Int) => (
List(quarters, dimes, nickels, pennies)
Zip coinNames // join with names
map (t => (if (t._1 != 1) (t._1, t._2._2) else (t._1, t._2._1))) // correct for number
map (t => t._1 + " " + t._2) // qty name
mkString " "
))
def allCombinations(amount: Int) =
(for{quarters <- 0 to (amount / 25)
dimes <- 0 to ((amount - 25*quarters) / 10)
nickels <- 0 to ((amount - 25*quarters - 10*dimes) / 5)
} yield (quarters, dimes, nickels, amount - 25*quarters - 10*dimes - 5*nickels)
) map coinsString mkString "\n"
他のソリューションがあります。この解決策は、各コインが他のコインの倍数であるという観察に基づいているため、コインを表すことができます。
// Just to make things a bit more readable, as these routines will access
// arrays a lot
val coinValues = List(25, 10, 5, 1)
val coinNames = List(("quarter", "quarters"),
("dime", "dimes"),
("nickel", "nickels"),
("penny", "pennies"))
val List(quarter, dime, nickel, penny) = coinValues.indices.toList
// Find the combination that uses the least amount of coins
def leastCoins(amount: Int): Array[Int] =
((List(amount) /: coinValues) {(list, coinValue) =>
val currentAmount = list.head
val numberOfCoins = currentAmount / coinValue
val remainingAmount = currentAmount % coinValue
remainingAmount :: numberOfCoins :: list.tail
}).tail.reverse.toArray
// Helper function. Adjust a certain amount of coins by
// adding or subtracting coins of each type; this could
// be made to receive a list of adjustments, but for so
// few types of coins, it's not worth it.
def adjust(base: Array[Int],
quarters: Int,
dimes: Int,
nickels: Int,
pennies: Int): Array[Int] =
Array(base(quarter) + quarters,
base(dime) + dimes,
base(nickel) + nickels,
base(penny) + pennies)
// We decrease the amount of quarters by one this way
def decreaseQuarter(base: Array[Int]): Array[Int] =
adjust(base, -1, +2, +1, 0)
// Dimes are decreased this way
def decreaseDime(base: Array[Int]): Array[Int] =
adjust(base, 0, -1, +2, 0)
// And here is how we decrease Nickels
def decreaseNickel(base: Array[Int]): Array[Int] =
adjust(base, 0, 0, -1, +5)
// This will help us find the proper decrease function
val decrease = Map(quarter -> decreaseQuarter _,
dime -> decreaseDime _,
nickel -> decreaseNickel _)
// Given a base amount of coins of each type, and the type of coin,
// we'll produce a list of coin amounts for each quantity of that particular
// coin type, up to the "base" amount
def coinSpan(base: Array[Int], whichCoin: Int) =
(List(base) /: (0 until base(whichCoin)).toList) { (list, _) =>
decrease(whichCoin)(list.head) :: list
}
// Generate a pretty string
def coinsString(base: Array[Int]) = (
base
Zip coinNames // join with names
map (t => (if (t._1 != 1) (t._1, t._2._2) else (t._1, t._2._1))) // correct for number
map (t => t._1 + " " + t._2)
mkString " "
)
// So, get a base amount, compute a list for all quarters variations of that base,
// then, for each combination, compute all variations of dimes, and then repeat
// for all variations of nickels.
def allCombinations(amount: Int) = {
val base = leastCoins(amount)
val allQuarters = coinSpan(base, quarter)
val allDimes = allQuarters flatMap (base => coinSpan(base, dime))
val allNickels = allDimes flatMap (base => coinSpan(base, nickel))
allNickels map coinsString mkString "\n"
}
したがって、37コインの場合、たとえば:
scala> println(allCombinations(37))
0 quarter 0 dimes 0 nickels 37 pennies
0 quarter 0 dimes 1 nickel 32 pennies
0 quarter 0 dimes 2 nickels 27 pennies
0 quarter 0 dimes 3 nickels 22 pennies
0 quarter 0 dimes 4 nickels 17 pennies
0 quarter 0 dimes 5 nickels 12 pennies
0 quarter 0 dimes 6 nickels 7 pennies
0 quarter 0 dimes 7 nickels 2 pennies
0 quarter 1 dime 0 nickels 27 pennies
0 quarter 1 dime 1 nickel 22 pennies
0 quarter 1 dime 2 nickels 17 pennies
0 quarter 1 dime 3 nickels 12 pennies
0 quarter 1 dime 4 nickels 7 pennies
0 quarter 1 dime 5 nickels 2 pennies
0 quarter 2 dimes 0 nickels 17 pennies
0 quarter 2 dimes 1 nickel 12 pennies
0 quarter 2 dimes 2 nickels 7 pennies
0 quarter 2 dimes 3 nickels 2 pennies
0 quarter 3 dimes 0 nickels 7 pennies
0 quarter 3 dimes 1 nickel 2 pennies
1 quarter 0 dimes 0 nickels 12 pennies
1 quarter 0 dimes 1 nickel 7 pennies
1 quarter 0 dimes 2 nickels 2 pennies
1 quarter 1 dime 0 nickels 2 pennies
これは、Zihanの答えの改善です。多くの不要なループは、金種が1セントである場合に発生します。
直感的で非再帰的です。
public static int Ways2PayNCents(int n)
{
int numberOfWays=0;
int cent, nickel, dime, quarter;
for (quarter = 0; quarter <= n/25; quarter++)
{
for (dime = 0; dime <= n/10; dime++)
{
for (nickel = 0; nickel <= n/5; nickel++)
{
cent = n - (quarter * 25 + dime * 10 + nickel * 5);
if (cent >= 0)
{
numberOfWays += 1;
Console.WriteLine("{0},{1},{2},{3}", quarter, dime, nickel, cent);
}
}
}
}
return numberOfWays;
}
/*
* make a list of all distinct sets of coins of from the set of coins to
* sum up to the given target amount.
* Here the input set of coins is assumed yo be {1, 2, 4}, this set MUST
* have the coins sorted in ascending order.
* Outline of the algorithm:
*
* Keep track of what the current coin is, say ccn; current number of coins
* in the partial solution, say k; current sum, say sum, obtained by adding
* ccn; sum sofar, say accsum:
* 1) Use ccn as long as it can be added without exceeding the target
* a) if current sum equals target, add cc to solution coin set, increase
* coin coin in the solution by 1, and print it and return
* b) if current sum exceeds target, ccn can't be in the solution, so
* return
* c) if neither of the above, add current coin to partial solution,
* increase k by 1 (number of coins in partial solution), and recuse
* 2) When current denomination can no longer be used, start using the
* next higher denomination coins, just like in (1)
* 3) When all denominations have been used, we are done
*/
#include <iostream>
#include <cstdlib>
using namespace std;
// int num_calls = 0;
// int num_ways = 0;
void print(const int coins[], int n);
void combine_coins(
const int denoms[], // coins sorted in ascending order
int n, // number of denominations
int target, // target sum
int accsum, // accumulated sum
int coins[], // solution set, MUST equal
// target / lowest denom coin
int k // number of coins in coins[]
)
{
int ccn; // current coin
int sum; // current sum
// ++num_calls;
for (int i = 0; i < n; ++i) {
/*
* skip coins of lesser denomination: This is to be efficient
* and also avoid generating duplicate sequences. What we need
* is combinations and without this check we will generate
* permutations.
*/
if (k > 0 && denoms[i] < coins[k - 1])
continue; // skip coins of lesser denomination
ccn = denoms[i];
if ((sum = accsum + ccn) > target)
return; // no point trying higher denominations now
if (sum == target) {
// found yet another solution
coins[k] = ccn;
print(coins, k + 1);
// ++num_ways;
return;
}
coins[k] = ccn;
combine_coins(denoms, n, target, sum, coins, k + 1);
}
}
void print(const int coins[], int n)
{
int s = 0;
for (int i = 0; i < n; ++i) {
cout << coins[i] << " ";
s += coins[i];
}
cout << "\t = \t" << s << "\n";
}
int main(int argc, const char *argv[])
{
int denoms[] = {1, 2, 4};
int dsize = sizeof(denoms) / sizeof(denoms[0]);
int target;
if (argv[1])
target = atoi(argv[1]);
else
target = 8;
int *coins = new int[target];
combine_coins(denoms, dsize, target, 0, coins, 0);
// cout << "num calls = " << num_calls << ", num ways = " << num_ways << "\n";
return 0;
}
public class Coins {
static int ac = 421;
static int bc = 311;
static int cc = 11;
static int target = 4000;
public static void main(String[] args) {
method2();
}
public static void method2(){
//running time n^2
int da = target/ac;
int db = target/bc;
for(int i=0;i<=da;i++){
for(int j=0;j<=db;j++){
int rem = target-(i*ac+j*bc);
if(rem < 0){
break;
}else{
if(rem%cc==0){
System.out.format("\n%d, %d, %d ---- %d + %d + %d = %d \n", i, j, rem/cc, i*ac, j*bc, (rem/cc)*cc, target);
}
}
}
}
}
}
私のブログエントリ は、このナップザックのような問題を XKCD comic からの数字の問題として解決します。 items
dictとexactcost
の値を簡単に変更すると、問題のすべての解決策も得られます。
問題が最小コストを使用した変更を見つけることである場合、最高値のコインの多くを使用した素朴な欲張りアルゴリズムは、コインと目標額のいくつかの組み合わせで失敗する可能性があります。たとえば、値が1、3、および4のコインがある場合。目標金額が6である場合、値3のコインを2枚使用できることがわかりやすい場合、貪欲アルゴリズムは値4、1、および1のコイン3枚を提案します。
簡単なJavaソリューション:
public static void main(String[] args)
{
int[] denoms = {4,2,3,1};
int[] vals = new int[denoms.length];
int target = 6;
printCombinations(0, denoms, target, vals);
}
public static void printCombinations(int index, int[] denom,int target, int[] vals)
{
if(target==0)
{
System.out.println(Arrays.toString(vals));
return;
}
if(index == denom.length) return;
int currDenom = denom[index];
for(int i = 0; i*currDenom <= target;i++)
{
vals[index] = i;
printCombinations(index+1, denom, target - i*currDenom, vals);
vals[index] = 0;
}
}
これは、O [mxn)の複雑さをもたらす、再帰とメモ化を使用するpythonベースのソリューションです。
def get_combinations_dynamic(self, amount, coins, memo): end_index = len(coins) - 1 memo_key = str(amount)+'->'+str(coins) if memo_key in memo: return memo[memo_key] remaining_amount = amount if amount < 0: return [] if amount == 0: return [[]] combinations = [] if len(coins) <= 1: if amount % coins[0] == 0: combination = [] for i in range(amount // coins[0]): combination.append(coins[0]) list.sort(combination) if combination not in combinations: combinations.append(combination) else: k = 0 while remaining_amount >= 0: sub_combinations = self.get_combinations_dynamic(remaining_amount, coins[:end_index], memo) for combination in sub_combinations: temp = combination[:] for i in range(k): temp.append(coins[end_index]) list.sort(temp) if temp not in combinations: combinations.append(temp) k += 1 remaining_amount -= coins[end_index] memo[memo_key] = combinations return combinations
ここには多くのバリエーションがありますが、組み合わせの数に対するPHPソリューションがどこにも見つからなかったため、1つ追加します。
/**
* @param int $money The total value
* @param array $coins The coin denominations
* @param int $sum The countable sum
* @return int
*/
function getTotalCombinations($money, $coins, &$sum = 0){
if ($money == 0){
return $sum++;
} else if (empty($coins) || $money < 0){
return $sum;
} else {
$firstCoin = array_pop(array_reverse($coins));
getTotalCombinations($money - $firstCoin, $coins, $sum) + getTotalCombinations($money, array_diff($coins, [$firstCoin]), $sum);
}
return $sum;
}
$totalCombinations = getTotalCombinations($money, $coins);
金額を額面金額に分類するPHPコード:
//Define the denominations
private $denominations = array(1000, 500, 200, 100, 50, 40, 20, 10, 5, 1);
/**
* S# countDenomination() function
*
* @author Edwin Mugendi <[email protected]>
*
* Count denomination
*
* @param float $original_amount Original amount
*
* @return array with denomination and count
*/
public function countDenomination($original_amount) {
$amount = $original_amount;
$denomination_count_array = array();
foreach ($this->denominations as $single_denomination) {
$count = floor($amount / $single_denomination);
$denomination_count_array[$single_denomination] = $count;
$amount = fmod($amount, $single_denomination);
}//E# foreach statement
var_dump($denomination_count_array);
return $denomination_count_array;
//return $denomination_count_array;
}
// E#countDenomination()関数
以下のJavaソリューションは、さまざまな組み合わせも出力します。わかりやすい。アイデアは
合計5
解決策は
5 - 5(i) times 1 = 0
if(sum = 0)
print i times 1
5 - 4(i) times 1 = 1
5 - 3 times 1 = 2
2 - 1(j) times 2 = 0
if(sum = 0)
print i times 1 and j times 2
and so on......
各ループの残りの合計が額面金額よりも小さい場合、つまり、残りの合計1が2よりも小さい場合、ループを中断します。
以下の完全なコード
間違いがあった場合は修正してください
public class CoinCombinbationSimple {
public static void main(String[] args) {
int sum = 100000;
printCombination(sum);
}
static void printCombination(int sum) {
for (int i = sum; i >= 0; i--) {
int sumCopy1 = sum - i * 1;
if (sumCopy1 == 0) {
System.out.println(i + " 1 coins");
}
for (int j = sumCopy1 / 2; j >= 0; j--) {
int sumCopy2 = sumCopy1;
if (sumCopy2 < 2) {
break;
}
sumCopy2 = sumCopy1 - 2 * j;
if (sumCopy2 == 0) {
System.out.println(i + " 1 coins " + j + " 2 coins ");
}
for (int k = sumCopy2 / 5; k >= 0; k--) {
int sumCopy3 = sumCopy2;
if (sumCopy2 < 5) {
break;
}
sumCopy3 = sumCopy2 - 5 * k;
if (sumCopy3 == 0) {
System.out.println(i + " 1 coins " + j + " 2 coins "
+ k + " 5 coins");
}
}
}
}
}
}
以下はpythonソリューションです。
x = []
dic = {}
def f(n,r):
[a,b,c,d] = r
if not dic.has_key((n,a,b,c,d)): dic[(n,a,b,c,d)] = 1
if n>=25:
if not dic.has_key((n-25,a+1,b,c,d)):f(n-25,[a+1,b,c,d])
if not dic.has_key((n-10,a,b+1,c,d)):f(n-10,[a,b+1,c,d])
if not dic.has_key((n-5,a,b,c+1,d)):f(n-5,[a,b,c+1,d])
if not dic.has_key((n-1,a,b,c,d+1)):f(n-1,[a,b,c,d+1])
Elif n>=10:
if not dic.has_key((n-10,a,b+1,c,d)):f(n-10,[a,b+1,c,d])
if not dic.has_key((n-5,a,b,c+1,d)):f(n-5,[a,b,c+1,d])
if not dic.has_key((n-1,a,b,c,d+1)):f(n-1,[a,b,c,d+1])
Elif n>=5:
if not dic.has_key((n-5,a,b,c+1,d)):f(n-5,[a,b,c+1,d])
if not dic.has_key((n-1,a,b,c,d+1)):f(n-1,[a,b,c,d+1])
Elif n>=1:
if not dic.has_key((n-1,a,b,c,d+1)):f(n-1,[a,b,c,d+1])
else:
if r not in x:
x.extend([r])
f(100, [0,0,0,0])
print x
Javaソリューション
import Java.util.Arrays;
import Java.util.Scanner;
public class nCents {
public static void main(String[] args) {
Scanner input=new Scanner(System.in);
int cents=input.nextInt();
int num_ways [][] =new int [5][cents+1];
//putting in zeroes to offset
int getCents[]={0 , 0 , 5 , 10 , 25};
Arrays.fill(num_ways[0], 0);
Arrays.fill(num_ways[1], 1);
int current_cent=0;
for(int i=2;i<num_ways.length;i++){
current_cent=getCents[i];
for(int j=1;j<num_ways[0].length;j++){
if(j-current_cent>=0){
if(j-current_cent==0){
num_ways[i][j]=num_ways[i-1][j]+1;
}else{
num_ways[i][j]=num_ways[i][j-current_cent]+num_ways[i-1][j];
}
}else{
num_ways[i][j]=num_ways[i-1][j];
}
}
}
System.out.println(num_ways[num_ways.length-1][num_ways[0].length-1]);
}
}