最近、同僚にこの問題を解決するアルゴリズムを書くように依頼しました。
1セントから99セントへの変更が可能な、必要最小限のコインを見つけます。コインはペニー(1)、ニッケル(5)、ダイム(10)、クォーター(25)のみであり、これらのコインを使用して1〜99(1セント単位)のすべての値を作成できる必要があります。
しかし、コインの可能な組み合わせをすべて調べずに自分でこれを行う方法が実際にはわからないことに気付きました。この問題を解決するためのより良い方法が必要ですが、このタイプのアルゴリズムの一般名が何と呼ばれるかわかりません。すべてのソリューションを見る以上にそれを単純化する方法がわかりません。
誰かが私を正しい方向に向けることができるのか、より効率的なアルゴリズムを提供できるのかと思っていました。
あなたが探しているのは動的プログラミングです。
実際には、すべての可能な値のすべての可能な組み合わせを列挙する必要はありません。前の回答の上に構築できるからです。
アルゴリズムは2つのパラメーターを取る必要があります。
[1, 5, 10, 25]
_[1, 99]
_そして、目標は、この範囲に必要なコインの最小セットを計算することです。
最も簡単な方法は、ボトムアップ方式で進めることです。
_Range Number of coins (in the minimal set)
1 5 10 25
[1,1] 1
[1,2] 2
[1,3] 3
[1,4] 4
[1,5] 5
[1,5]* 4 1 * two solutions here
[1,6] 4 1
[1,9] 4 1
[1,10] 5 1 * experience tells us it's not the most viable one :p
[1,10] 4 2 * not so viable either
[1,10] 4 1 1
[1,11] 4 1 1
[1,19] 4 1 1
[1,20] 5 1 1 * not viable (in the long run)
[1,20] 4 2 1 * not viable (in the long run)
[1,20] 4 1 2
_
それはいくぶん簡単です。各ステップで最大1枚のコインを追加することで進めることができます。これは、_[x,y]
_の範囲が_[x,y+1]
_に含まれているという事実に要約されるため、_[x,y+1]
_の最小セットには_[x,y]
_の最小セットが含まれます。
あなたが気づいたかもしれませんが、時々優柔不断があります。つまり、複数のセットが同じ数のコインを持っています。この場合、どちらを破棄するかは後でのみ決定できます。
コインを追加すると、通常、追加した範囲よりもはるかに広い範囲をカバーできることに気づいたときに、実行時間を改善できるはずです。
たとえば、次のことに注意してください。
_ [1,5] 4*1 1*5
[1,9] 4*1 1*5
_
_[1,5]
_をカバーするためにニッケルを追加しますが、これは_[1,9]
_を無料で提供します!
ただし、_[2,3,5,10,25]
_をカバーするためにとんでもない入力セット_[2,99]
_を処理する場合、新しいセットでカバーされる範囲をすばやく確認する方法がわかりません。
すぐに上限を見つけることができます。
たとえば、4分の3かかります。次に、他のコインで「ギャップ」1-24、26-49、51-74、76-99を埋めるだけです。
些細なことに、2ダイム、1ニッケル、4ペニーで動作します。
したがって、3 + 4 + 2 + 1は、コインの数の上限である必要があります。ブルートフォースアルゴリズムがthtaを超えるたびに、より深い検索を即座に停止できます。
検索の残りの部分は、動的プログラミングのあらゆる目的に十分な速度で実行する必要があります。
(編集:Gabeの観察による固定答え)
動的プログラミング について今日学びましたが、結果は次のとおりです。
coins = [1,5,10,25]
d = {} # stores tuples of the form (# of coins, [coin list])
# finds the minimum # of coins needed to
# make change for some number of cents
def m(cents):
if cents in d.keys():
return d[cents]
Elif cents > 0:
choices = [(m(cents - x)[0] + 1, m(cents - x)[1] + [x]) for x in coins if cents >= x]
# given a list of tuples, python's min function
# uses the first element of each Tuple for comparison
d[cents] = min(choices)
return d[cents]
else:
d[0] = (0, [])
return d[0]
for x in range(1, 100):
val = m(x)
print x, "cents requires", val[0], "coins:", val[1]
動的プログラミングは本当に魔法です。
変更として4を取得したいので、少なくとも4ペニーが必要です。これはペニーでのみ実行できます。
4ペニー以上は最適ではありません。 4 + xペニーの代わりに、4ペニーとxニッケルを使用できます-それらは少なくとも同じ範囲に及びます。
したがって、正確に4ペニーがあります。
変更として5を取得したいので、少なくとも1つのニッケルが必要です。
1個以上のニッケルを使用するのは最適ではありません。 1 + x個のニッケルの代わりに、1個のニッケルとx個のダイムを持つことができます-それらは少なくとも同じ範囲に及びます。
したがって、ニッケルは1つだけです。
20を取得したいので、少なくとも2ダイムが必要です。
これは、4ペニー、1ニッケル、少なくとも2ダイムがあることを意味します。
コインが10個未満の場合、4分の3未満です。しかし、その後、すべてのコインを使用して得ることができる最大の変化は4 + 5 + 20 + 50 = 79であり、十分ではありません。
これは、少なくとも10枚のコインがあることを意味します。 トーマスの答え は、実際に4ペニー、1ニッケル、2ダイム、3クォーターがある場合、すべて順調であることを示しています。
いい質問です。これが私が思いついた論理です。 25を含むいくつかのシナリオでテスト済み。
class Program
{
//Allowable denominations
const int penny = 1;
const int nickel = 5;
const int dime = 10;
const int quarter = 25;
const int maxCurrencyLevelForTest =55; //1-n where n<=99
static void Main(string[] args)
{
int minPenniesNeeded = 0;
int minNickelsNeeded = 0;
int minDimesNeeded = 0;
int minQuartersNeeded = 0;
if (maxCurrencyLevelForTest == penny)
{
minPenniesNeeded = 1;
}
else if (maxCurrencyLevelForTest < nickel)
{
minPenniesNeeded = MinCountNeeded(penny, maxCurrencyLevelForTest);
}
else if (maxCurrencyLevelForTest < dime)
{
minPenniesNeeded = MinCountNeeded(penny, nickel - 1);
minNickelsNeeded = MinCountNeeded(nickel, maxCurrencyLevelForTest);
}
else if (maxCurrencyLevelForTest < quarter)
{
minPenniesNeeded = MinCountNeeded(penny, nickel - 1);
minNickelsNeeded = MinCountNeeded(nickel, dime - 1);
minDimesNeeded = MinCountNeeded(dime, maxCurrencyLevelForTest);
}
else
{
minPenniesNeeded = MinCountNeeded(penny, nickel - 1);
minNickelsNeeded = MinCountNeeded(nickel, dime - 1);
minDimesNeeded = MinCountNeeded(dime, quarter - 1);
var maxPossilbleValueWithoutQuarters = (minPenniesNeeded * penny + minNickelsNeeded * nickel + minDimesNeeded * dime);
if (maxCurrencyLevelForTest > maxPossilbleValueWithoutQuarters)
{
minQuartersNeeded = (((maxCurrencyLevelForTest - maxPossilbleValueWithoutQuarters)-1) / quarter) + 1;
}
}
var minCoinsNeeded = minPenniesNeeded + minNickelsNeeded+minDimesNeeded+minQuartersNeeded;
Console.WriteLine(String.Format("Min Number of coins needed: {0}", minCoinsNeeded));
Console.WriteLine(String.Format("Penny: {0} needed", minPenniesNeeded));
Console.WriteLine(String.Format("Nickels: {0} needed", minNickelsNeeded));
Console.WriteLine(String.Format("Dimes: {0} needed", minDimesNeeded));
Console.WriteLine(String.Format("Quarters: {0} needed", minQuartersNeeded));
Console.ReadLine();
}
private static int MinCountNeeded(int denomination, int upperRange)
{
int remainder;
return System.Math.DivRem(upperRange, denomination,out remainder);
}
}
一部の結果: maxCurrencyLevelForTest = 25の場合
Min Number of coins needed: 7
Penny: 4 needed
Nickels: 1 needed
Dimes: 2 needed
Quarters: 0 needed
MaxCurrencyLevelForTest = 99の場合
Min Number of coins needed: 10
Penny: 4 needed
Nickels: 1 needed
Dimes: 2 needed
Quarters: 3 needed
maxCurrencyLevelForTest:54
Min Number of coins needed: 8
Penny: 4 needed
Nickels: 1 needed
Dimes: 2 needed
Quarters: 1 needed
maxCurrencyLevelForTest:55
Min Number of coins needed: 9
Penny: 4 needed
Nickels: 1 needed
Dimes: 2 needed
Quarters: 2 needed
maxCurrencyLevelForTest:79
Min Number of coins needed: 9
Penny: 4 needed
Nickels: 1 needed
Dimes: 2 needed
Quarters: 2 needed
maxCurrencyLevelForTest:85
Min Number of coins needed: 10
Penny: 4 needed
Nickels: 1 needed
Dimes: 2 needed
Quarters: 3 needed
コードはさらにリファクタリングできると思います。
編集:コメント者が述べたように、私は質問を誤解しています。 (質問は大学の学生が解決しなければならない基本的なCS問題に非常に似ています...)waves handこれはあなたの答えではありません探しています。とはいえ、元の答えは間違っていますが、O(n)ソリューションへの足掛かりとして使用できます。
したがって、以下の間違った答えを選んでください。これは、単一の値(つまり、68セントに必要な最小コイン)のみを解き、すべての値に対して単純に実行します。
changes = []
for amount in xrange(1, 100): # [1, 99]
changes.append( run_the_algo_below( amount ) )
# Take the maximum for each coin type.
# ie, if run_the_algo_below returns (q, d, n, p):
change = [0, 0, 0, 0]
for c in changes:
change = [max(c[i], change[i] for i in xrange(0, 4)]
さて、これは確かにあなたに有効な答えを与えるでしょうが、それは最小限の答えですか? (これは難しい部分です。現在、私の腸はイエスと言っていますが、私はまだこれについて考えています...)
(間違った答え)
ワオ。ループ?動的プログラミング?ほんとに?
Pythonの場合:
amount = ( your_amount_in_cents )
quarters = amount // 25
dimes = amount % 25 // 10
nickels = amount % 25 % 10 // 5
pennies = amount % 25 % 10 % 5
おそらく、これらのモジュロ演算のいくつかは単純化できます...
これは難しくありません。実際の生活でどのように変化を起こすかを考える必要があります。別の四半期を追加することで希望の金額を超えるまで四半期を配り、さらに別の四半期を追加することで希望する金額を超えるまでダイムを配ります。次に、モジュロと除算の数学演算に変換します。同じソリューションがドルに適用され、秒をHH:MM:SSなどに変換します。
米国通貨について話していると仮定すると、貪欲なアルゴリズムが必要になります。 http://en.wikipedia.org/wiki/Greedy_algorithm
本質的に、最高から最低まですべての金種を試し、残りがなくなるまで、それぞれからできるだけ多くのコインを取ります。
一般的なケースについては、 http://en.wikipedia.org/wiki/Change-making_problem を参照してください。これは、動的プログラミングまたは線形プログラミングを使用して、欲張りなアルゴリズムの任意の宗派の答えを見つけるためです。動作しません。
PHPでこの種の問題に対する適切な解決策を見つけられなかった後、この関数を開発しました。
任意の金額(最大$ 999.99)を受け取り、その値に到達するために必要な各紙幣/硬貨の最小数の配列を返します。
最初に値をペニー単位のintに変換します(何らかの理由で、標準のfloat値を使用すると、最後にエラーが発生します)。
返される金額も小額です(例:5000 = 50ドル、100 = 1ドルなど)。
function makeChange($val)
{
$amountOfMoney = intval($val*100);
$cashInPennies = array(10000,5000,2000,1000,500,100,25,10,5,1);
$outputArray = array();
$currentSum = 0;
$currentDenom = 0;
while ($currentSum < $amountOfMoney) {
if( ( $currentSum + $cashInPennies[$currentDenom] ) <= $amountOfMoney ) {
$currentSum = $currentSum + $cashInPennies[$currentDenom];
$outputArray[$cashInPennies[$currentDenom]]++;
} else {
$currentDenom++;
}
}
return $outputArray;
}
$change = 56.93;
$output = makeChange($change);
print_r($output);
echo "<br>Total number of bills & coins: ".array_sum($output);
=== OUTPUT ===
Array ( [5000] => 1 [500] => 1 [100] => 1 [25] => 3 [10] => 1 [5] => 1 [1] => 3 )
Total number of bills & coins: 11
タスク
Find the least number of coins required, that can make any change from 1 to 99 cent.
タスクとは異なります
For each single change from 1 to 99 cent, find the least number of coins required.
なぜなら、ソリューションは完全に異なるコインのマルチセットかもしれないからです。
(1)、(5)、(10)、および(25)セント硬貨ではなく、(1)、(3)、(5)、および(17)セント硬貨があるとします:5の変更を行うには必要なコインは1つだけです。ただし、1から5までのすべての変更には、2枚のコインではなく、2枚のコインと1枚のコインが必要です。
欲張りアルゴリズムは、変化値とコイン値に関して、最小値から最大値まで反復します。
With 1x(1) you get all change values below 2.
To make a change of 2, you need an additional coin,
which could have any value up to 2;
choose greedy -> choose the largest -> (1).
With 2x(1) you get all change values below 3.
To make a change of 3, you need an additional coin,
which could have any value up to 3;
choose greedy -> choose the largest -> (3).
With 2x(1)+1x(3) you get all change values below 6.
To make a change of 6, you need an additional coin,
which could have any value up to 6;
choose greedy -> choose the largest -> (5).
and so on...
Haskellにあります:
coinsforchange [1,3,5,17] 99
where
coinsforchange coins change =
let f (coinssofar::[Int],sumsofar::Int) (largestcoin::Int,wanttogoto::Int) =
let coincount=(max 0 (wanttogoto-sumsofar+largestcoin-1))`div`largestcoin
in (replicate coincount largestcoin++coinssofar,sumsofar+coincount*largestcoin)
in foldl f ([],0) $ Zip coins $ tail [c-1|c<-coins] ++ [change]
C++の場合:
void f(std::map<unsigned,int> &coinssofar,int &sumsofar, unsigned largestcoin, int wanttogoto)
{
int x = wanttogoto - sumsofar + largestcoin - 1;
coinssofar[largestcoin] = (x>0) ? (x / largestcoin) : 0;
//returns coinssofar and sumsofar;
}
std::map<unsigned,int> coinsforchange(const std::list<unsigned> &coins, int change)
{
std::map<unsigned,int> coinssofar;
int sumsofar=0;
std::list<unsigned>::const_iterator coin = coins.begin();
unsigned largestcoin = *coin;
for( ++coin ; coin!=coins.end() ; largestcoin=*(coin++))
f(coinssofar,sumsofar,largestcoin,(*coin) - 1);
f(coinssofar,sumsofar,largestcoin,change);
return coinssofar;
}
このmightはC#の一般的なソリューションです
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace CoinProblem
{
class Program
{
static void Main(string[] args)
{
var coins = new int[] { 1, 5, 10, 25 }; // sorted lowest to highest
int upperBound = 99;
int numCoinsRequired = upperBound / coins.Last();
for (int i = coins.Length - 1; i > 0; --i)
{
numCoinsRequired += coins[i] / coins[i - 1] - (coins[i] % coins[i - 1] == 0 ? 1 : 0);
}
Console.WriteLine(numCoinsRequired);
Console.ReadLine();
}
}
}
私は完全にそれを考えていませんでした...それは夜に遅すぎます。この場合、答えは9であるべきだと思っていましたが、Gabeは10であるべきだと言いました...これが結果です。質問をどのように解釈するかによります... X未満の値を生成できる最小数のコイン、または最小数のコインを使用してX未満の値を生成できる最小数のコインを探していますか? ?たとえば...ニッケルを使わずに、わずか9枚のコインでどんな値でも作ることができると確信していますが、9を生産するには...必要です...ああ...なるほど。あなたが持っていない9ペニーが必要になるでしょう、なぜならそれは私たちが選んだものではないからです...thatの場合、Ithinkこの答えは正しい。トーマスのアイデアの再帰的な実装ですが、なぜ彼がそこで止まったのかわかりません..総当たり攻撃は必要ありません。
編集:これは間違っている。
Vbバージョン
Public Class Form1
Private Sub Button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Button1.Click
For saleAMT As Decimal = 0.01D To 0.99D Step 0.01D
Dim foo As New CashDrawer(0, 0, 0)
Dim chg As List(Of Money) = foo.MakeChange(saleAMT, 1D)
Dim t As Decimal = 1 - saleAMT
Debug.WriteLine(t.ToString("C2"))
For Each c As Money In chg
Debug.WriteLine(String.Format("{0} of {1}", c.Count.ToString("N0"), c.moneyValue.ToString("C2")))
Next
Next
End Sub
Class CashDrawer
Private _drawer As List(Of Money)
Public Sub New(Optional ByVal QTYtwoD As Integer = -1, _
Optional ByVal QTYoneD As Integer = -1, _
Optional ByVal QTYfifty As Integer = -1, _
Optional ByVal QTYquarters As Integer = -1, _
Optional ByVal QTYdimes As Integer = -1, _
Optional ByVal QTYnickels As Integer = -1, _
Optional ByVal QTYpennies As Integer = -1)
_drawer = New List(Of Money)
_drawer.Add(New Money(2D, QTYtwoD))
_drawer.Add(New Money(1D, QTYoneD))
_drawer.Add(New Money(0.5D, QTYfifty))
_drawer.Add(New Money(0.25D, QTYquarters))
_drawer.Add(New Money(0.1D, QTYdimes))
_drawer.Add(New Money(0.05D, QTYnickels))
_drawer.Add(New Money(0.01D, QTYpennies))
End Sub
Public Function MakeChange(ByVal SaleAmt As Decimal, _
ByVal amountTendered As Decimal) As List(Of Money)
Dim change As Decimal = amountTendered - SaleAmt
Dim rv As New List(Of Money)
For Each c As Money In Me._drawer
change -= (c.NumberOf(change) * c.moneyValue)
If c.Count > 0 Then
rv.Add(c)
End If
Next
If change <> 0D Then Throw New ArithmeticException
Return rv
End Function
End Class
Class Money
'-1 equals unlimited qty
Private _qty As Decimal 'quantity in drawer
Private _value As Decimal 'value money
Private _count As Decimal = 0D
Public Sub New(ByVal theValue As Decimal, _
ByVal theQTY As Decimal)
Me._value = theValue
Me._qty = theQTY
End Sub
ReadOnly Property moneyValue As Decimal
Get
Return Me._value
End Get
End Property
Public Function NumberOf(ByVal theAmount As Decimal) As Decimal
If (Me._qty > 0 OrElse Me._qty = -1) AndAlso Me._value <= theAmount Then
Dim ct As Decimal = Math.Floor(theAmount / Me._value)
If Me._qty <> -1D Then 'qty?
'limited qty
If ct > Me._qty Then 'enough
'no
Me._count = Me._qty
Me._qty = 0D
Else
'yes
Me._count = ct
Me._qty -= ct
End If
Else
'unlimited qty
Me._count = ct
End If
End If
Return Me._count
End Function
ReadOnly Property Count As Decimal
Get
Return Me._count
End Get
End Property
End Class
End Class
似たような回答が2つありますが、Javaを使用したソリューションは少し理解しやすいようです。これをチェックしてください。
public static int findMinimumNumberOfCoins(int inputCents) {
// Error Check, If the input is 0 or lower, return 0.
if(inputCents <= 0) return 0;
// Create the List of Coins that We need to loop through. Start from highest to lowewst.
// 25-10-5-1
int[] mCoinsArray = getCoinsArray();
// Number of Total Coins.
int totalNumberOfCoins = 0;
for(int i=0; i < mCoinsArray.length; i++) {
// Get the Coin from Array.
int coin = mCoinsArray[i];
// If there is no inputCoin Left, simply break the for-loop
if(inputCents == 0) break;
// Check If we have a smaller input than our coin
// If it's, we need to go the Next one in our Coins Array.
// e.g, if we have 8, but the current index of array is 10, we need to go to 5.
if(inputCents < coin) continue;
int quotient = inputCents/coin;
int remainder = inputCents%coin;
// Add qutient to number of total coins.
totalNumberOfCoins += quotient;
// Update the input with Remainder.
inputCents = remainder;
}
return totalNumberOfCoins;
}
// Create a Coins Array, from 25 to 1. Highest is first.
public static int[] getCoinsArray() {
int[] mCoinsArray = new int[4];
mCoinsArray[0] = 25;
mCoinsArray[1] = 10;
mCoinsArray[2] = 5;
mCoinsArray[3] = 1;
return mCoinsArray;
}
勉強しながら、今日これに出会いました https://www.coursera.org/course/bioinformatics
DPCHANGE(money, coins)
MinNumCoins(0) ← 0
for m ← 1 to money
MinNumCoins(m) ← ∞
for i ← 1 to |coins|
if m ≥ coini
if MinNumCoins(m - coini) + 1 < MinNumCoins(m)
MinNumCoins(m) ← MinNumCoins(m - coini) + 1
output MinNumCoins(money)
利用可能な金種のコンマ区切りの文字列と、目標額を受け取ります。
C#の実装:
public static void DPCHANGE(int val, string denoms)
{
int[] idenoms = Array.ConvertAll(denoms.Split(','), int.Parse);
Array.Sort(idenoms);
int[] minNumCoins = new int[val + 1];
minNumCoins[0] = 0;
for (int m = 1; m <= val; m++)
{
minNumCoins[m] = Int32.MaxValue - 1;
for (int i = 1; i <= idenoms.Count() - 1; i++)
{
if (m >= idenoms[i])
{
if (minNumCoins[m - idenoms[i]] + 1 < minNumCoins[m])
{
minNumCoins[m] = minNumCoins[m - idenoms[i]] + 1;
}
}
}
}
}
標準通貨システムの値を使用している場合は理解したように、単一のループで最小数のコインを数えるのは非常に簡単です。常に最大コイン値を消費し、それが不可能な場合は次のオプションを確認してください。しかし、1、2、3、4などのコインを持っているようなシステムがある場合は、機能しません。コインを1,2,5,10,25として持つという考えは、人間にとって計算を簡単にすることだと思います。
この問題に対して、GreedyアプローチはDPや他の方法よりも優れたソリューションを提供します。貪欲なアプローチ:必要な値よりも小さい最大額面を見つけ、それを配達されるコインのセットに追加します。追加した金額単位で必要なセントを引き下げ、必要なセントがゼロになるまで繰り返します。
Java solution:の私のソリューション(貪欲なアプローチ):
public class MinimumCoinDenomination {
private static final int[] coinsDenominations = {1, 5, 10, 25, 50, 100};
public static Map<Integer, Integer> giveCoins(int requiredCents) {
if(requiredCents <= 0) {
return null;
}
Map<Integer, Integer> denominations = new HashMap<Integer, Integer>();
int dollar = requiredCents/100;
if(dollar>0) {
denominations.put(100, dollar);
}
requiredCents = requiredCents - (dollar * 100);
//int sum = 0;
while(requiredCents > 0) {
for(int i = 1; i<coinsDenominations.length; i++) {
if(requiredCents < coinsDenominations[i]) {
//sum = sum +coinsDenominations[i-1];
if(denominations.containsKey(coinsDenominations[i-1])) {
int c = denominations.get(coinsDenominations[i-1]);
denominations.put(coinsDenominations[i-1], c+1);
} else {
denominations.put(coinsDenominations[i-1], 1);
}
requiredCents = requiredCents - coinsDenominations[i-1];
break;
}
}
}
return denominations;
}
public static void main(String[] args) {
System.out.println(giveCoins(199));
}
}
一般に、コインCOIN []と「範囲の変更」1..MAXがある場合、以下はコインの最大数を見つけるはずです。
Initialise array CHANGEVAL[MAX] to -1
For each element coin in COIN:
set CHANGEVAL[coin] to 1
Until there are no more -1 in CHANGEVAL:
For each index I over CHANGEVAL:
if CHANGEVAL[I] != -1:
let coincount = CHANGEVAL[I]
For each element coin in COIN:
let sum = coin + I
if (COINS[sum]=-1) OR ((coincount+1)<COINS[sum]):
COINS[sum]=coincount+1
厳密に言えば、内部条件のコイン最小性のチェックが必要かどうかはわかりません。コインの追加の最小限のチェーンは正しいものになると思いますが、申し訳ありませんが安全です。
Linqを使用した簡単なc#ソリューションを次に示します。
internal class Program
{
public static IEnumerable<Coin> Coins = new List<Coin>
{
new Coin {Name = "Dime", Value = 10},
new Coin {Name = "Penny", Value = 1},
new Coin {Name = "Nickel", Value = 5},
new Coin {Name = "Quarter", Value = 25}
};
private static void Main(string[] args)
{
PrintChange(34);
Console.ReadKey();
}
public static void PrintChange(int amount)
{
decimal remaining = amount;
//Order coins by value in descending order
var coinsDescending = Coins.OrderByDescending(v => v.Value);
foreach (var coin in coinsDescending)
{
//Continue to smaller coin when current is larger than remainder
if (remaining < coin.Value) continue;
// Get # of coins that fit in remaining amount
var quotient = (int)(remaining / coin.Value);
Console.WriteLine(new string('-',28));
Console.WriteLine("{0,10}{1,15}", coin.Name, quotient);
//Subtract fitting coins from remaining amount
remaining -= quotient * coin.Value;
if (remaining <= 0) break; //Exit when no remainder left
}
Console.WriteLine(new string('-', 28));
}
public class Coin
{
public string Name { get; set; }
public int Value { get; set; }
}
}
これが私の見解です。 1つの興味深い点は、coin_with_max_value(この場合は25)-1のみを形成するために必要な最小コインをチェックする必要があることです。その後、これらの最小コインの合計を計算します。その時点から、特定の金額のcoin_with_max_valueを追加するだけで、合計コストと見つかった合計の差に応じて、合計コストまでの任意の数を形成できます。それでおしまい。
したがって、取得した値については、24分の最小コインが見つかると、[1、2、2、5、10、10]になります。 30(最小コインの合計)を超える25値ごとに25コインを追加し続けるだけです。 99の最終回答は次のとおりです。
[1、2、2、5、10、10、25、25、25]
9
import itertools
import math
def ByCurrentCoins(val, coins):
for i in range(1, len(coins) + 1):
combinations = itertools.combinations(coins, i)
for combination in combinations:
if sum(combination) == val:
return True
return False
def ExtraCoin(val, all_coins, curr_coins):
for c in all_coins:
if ByCurrentCoins(val, curr_coins + [c]):
return c
def main():
cost = 99
coins = sorted([1, 2, 5, 10, 25], reverse=True)
max_coin = coins[0]
curr_coins = []
for c in range(1, min(max_coin, cost+1)):
if ByCurrentCoins(c, curr_coins):
continue
extra_coin = ExtraCoin(c, coins, curr_coins)
if not extra_coin:
print -1
return
curr_coins.append(extra_coin)
curr_sum = sum(curr_coins)
if cost > curr_sum:
extra_max_coins = int(math.ceil((cost - curr_sum)/float(max_coin)))
curr_coins.extend([max_coin for _ in range(extra_max_coins)])
print curr_coins
print len(curr_coins)
Pythonの簡単なバージョンを次に示します。
#!/usr/bin/env python
required = []
coins = [25, 10, 5, 1]
t = []
for i in range(1, 100):
while sum(t) != i:
for c in coins:
if sum(t) + c <= i:
t.append(c)
break
for c in coins:
while t.count(c) > required.count(c):
required.append(c)
del t[:]
print required
実行すると、次の内容が標準出力に出力されます。
[1, 1, 1, 1, 5, 10, 10, 25, 25, 25]
コードはかなり一目瞭然です(Pythonに感謝します!)が、基本的にアルゴリズムは、使用中の現在の合計を超えない利用可能な最大のコインを一時的なコインのリストに追加することです(この場合はt )。特定の合計に対して最も効率的なコインのセットを見つけたら、必要なリストに各コインの少なくともその数があることを確認してください。 1から99セントまでのすべての合計に対してこれを行うと、完了です。
私はDPで同様の種類の問題のためにこのアルゴリズムを書きました、それが役立つかもしれません
public class MinimumCoinProblem {
private static void calculateMinumCoins(int[] array_Coins, int sum) {
int[] array_best = new int[sum];
for (int i = 0; i < sum; i++) {
for (int j = 0; j < array_Coins.length; j++) {
if (array_Coins[j] <= i && (array_best[i] == 0 || (array_best[i - array_Coins[j]] + 1) <= array_best[i])) {
array_best[i] = array_best[i - array_Coins[j]] + 1;
}
}
}
System.err.println("The Value is" + array_best[14]);
}
public static void main(String[] args) {
int[] sequence1 = {11, 9,1, 3, 5,2 ,20};
int sum = 30;
calculateMinumCoins(sequence1, sum);
}
}
Javaの貪欲なアプローチによる解決策は以下のとおりです。
public class CoinChange {
public static void main(String args[]) {
int denominations[] = {1, 5, 10, 25};
System.out.println("Total required coins are " + greeadApproach(53, denominations));
}
public static int greeadApproach(int amount, int denominations[]) {
int cnt[] = new int[denominations.length];
for (int i = denominations.length-1; amount > 0 && i >= 0; i--) {
cnt[i] = (amount/denominations[i]);
amount -= cnt[i] * denominations[i];
}
int noOfCoins = 0;
for (int cntVal : cnt) {
noOfCoins+= cntVal;
}
return noOfCoins;
}
}
しかし、これは単一の金額で機能します。範囲ごとに実行する場合は、範囲の量ごとに呼び出す必要があります。
ここで私のソリューションに行きます、再びPythonおよび動的プログラミングを使用します。最初に、範囲1..99の各金額を変更するために必要なコインの最小シーケンスを生成し、その結果から各宗派から必要なコインの最大数を見つける:
_def min_any_change():
V, C = [1, 5, 10, 25], 99
mxP, mxN, mxD, mxQ = 0, 0, 0, 0
solution = min_change_table(V, C)
for i in xrange(1, C+1):
cP, cN, cD, cQ = 0, 0, 0, 0
while i:
coin = V[solution[i]]
if coin == 1:
cP += 1
Elif coin == 5:
cN += 1
Elif coin == 10:
cD += 1
else:
cQ += 1
i -= coin
if cP > mxP:
mxP = cP
if cN > mxN:
mxN = cN
if cD > mxD:
mxD = cD
if cQ > mxQ:
mxQ = cQ
return {'pennies':mxP, 'nickels':mxN, 'dimes':mxD, 'quarters':mxQ}
def min_change_table(V, C):
m, n, minIdx = C+1, len(V), 0
table, solution = [0] * m, [0] * m
for i in xrange(1, m):
minNum = float('inf')
for j in xrange(n):
if V[j] <= i and 1 + table[i - V[j]] < minNum:
minNum = 1 + table[i - V[j]]
minIdx = j
table[i] = minNum
solution[i] = minIdx
return solution
_
min_any_change()
を実行すると、求めていた答え_{'pennies': 4, 'nickels': 1, 'dimes': 2, 'quarters': 3}
_が得られます。テストとして、金種のコインを取り除き、1..99の範囲の金額を変更できるかどうかを確認できます。
_from itertools import combinations
def test(lst):
sums = all_sums(lst)
return all(i in sums for i in xrange(1, 100))
def all_sums(lst):
combs = []
for i in xrange(len(lst)+1):
combs += combinations(lst, i)
return set(sum(s) for s in combs)
_
上記で取得した結果をテストすると、True
が取得されます。
_test([1, 1, 1, 1, 5, 10, 10, 25, 25, 25])
_
しかし、コインの種類を問わず、1つのコインを削除すると、False
が取得されます。
_test([1, 1, 1, 5, 10, 10, 25, 25, 25])
_
これに触発された https://www.youtube.com/watch?v=GafjS0FfAC 以下
1)最適なサブ問題2)ビデオに導入された重複サブ問題の原則
using System;
using System.Collections.Generic;
using System.Linq;
namespace UnitTests.moneyChange
{
public class MoneyChangeCalc
{
private static int[] _coinTypes;
private Dictionary<int, int> _solutions;
public MoneyChangeCalc(int[] coinTypes)
{
_coinTypes = coinTypes;
Reset();
}
public int Minimun(int amount)
{
for (int i = 2; i <= amount; i++)
{
IList<int> candidates = FulfillCandidates(i);
try
{
_solutions.Add(i, candidates.Any() ? (candidates.Min() + 1) : 0);
}
catch (ArgumentException)
{
Console.WriteLine("key [{0}] = {1} already added", i, _solutions[i]);
}
}
int minimun2;
_solutions.TryGetValue(amount, out minimun2);
return minimun2;
}
internal IList<int> FulfillCandidates(int amount)
{
IList<int> candidates = new List<int>(3);
foreach (int coinType in _coinTypes)
{
int sub = amount - coinType;
if (sub < 0) continue;
int candidate;
if (_solutions.TryGetValue(sub, out candidate))
candidates.Add(candidate);
}
return candidates;
}
private void Reset()
{
_solutions = new Dictionary<int, int>
{
{0,0}, {_coinTypes[0] ,1}
};
}
}
}
テストケース:
using NUnit.Framework;
using System.Collections;
namespace UnitTests.moneyChange
{
[TestFixture]
public class MoneyChangeTest
{
[TestCaseSource("TestCasesData")]
public int Test_minimun2(int amount, int[] coinTypes)
{
var moneyChangeCalc = new MoneyChangeCalc(coinTypes);
return moneyChangeCalc.Minimun(amount);
}
private static IEnumerable TestCasesData
{
get
{
yield return new TestCaseData(6, new[] { 1, 3, 4 }).Returns(2);
yield return new TestCaseData(3, new[] { 2, 4, 6 }).Returns(0);
yield return new TestCaseData(10, new[] { 1, 3, 4 }).Returns(3);
yield return new TestCaseData(100, new[] { 1, 5, 10, 20 }).Returns(5);
}
}
}
}
一方では、これは答えられました。一方、ほとんどの答えには多くのコード行が必要です。このPython答えはコードの多くの行を必要とせず、単に思考の多くの行^ _ ^:
_div_round_up = lambda a, b: a // b if a % b == 0 else a // b + 1
def optimum_change(*coins):
wallet = [0 for i in range(0, len(coins) - 1)]
for j in range(0, len(wallet)):
target = coins[j + 1] - 1
target -= sum(wallet[i] * coins[i] for i in range(0, j))
wallet[j] = max(0, div_round_up(target, coins[j]))
return wallet
optimum_change(1, 5, 10, 25, 100)
# [4, 1, 2, 3]
_
これは非常に単純な再スケーリングアルゴリズムであり、まだ考慮していない入力に対しておそらく壊れる可能性がありますが、堅牢である必要があると思います。基本的に、「新しいコインタイプをウォレットに追加するには、次のコインタイプNを覗いてから、_target = N - 1
_を作成するのに必要な量の新しいコインを追加します。」それには、少なくともceil((target - wallet_value)/coin_value)
が必要であると計算され、これが間にあるすべての数値を作成するかどうかはチェックされません。構文は、適切な最終target
を生成するため、最終番号「100」を追加することにより「0から99セント」をエンコードすることに注意してください。
チェックしない理由は、「できれば自動的に実行される」などです。直接入力すると、ペニー(値1)に対してこのステップを実行すると、アルゴリズムはニッケル(値5)を任意のサブインターバル0〜4に「分割」できます。ニッケルに対してこれを行うと、アルゴリズムは「分割」 "ダイム(値10)。等々。
もちろん、これらの特定の入力は必要ありません。奇妙な通貨も使用できます:
_>>> optimum_change(1, 4, 7, 8, 100)
[3, 1, 0, 12]
_
7コインを自動的に無視することに注意してください。これは、変更によって8を既に「破る」ことができるためです。
これを解決するC#のコード。
public struct CoinCount
{
public int coinValue;
public int noOfCoins;
}
/// <summary>
/// Find and returns the no of coins in each coins in coinSet
/// </summary>
/// <param name="coinSet">sorted coins value in assending order</param>
/// <returns></returns>
public CoinCount[] FindCoinsCountFor1to99Collection(int[] coinSet)
{
// Add extra coin value 100 in the coin set. Since it need to find the collection upto 99.
CoinCount[] result = new CoinCount[coinSet.Length];
List<int> coinValues = new List<int>();
coinValues.AddRange(coinSet);
coinValues.Add(100);
// Selected coin total values
int totalCount = 0;
for (int i = 0; i < coinValues.Count - 1; i++)
{
int count = 0;
if (totalCount <= coinValues[i])
{
// Find the coins count
int remainValue = coinValues[i + 1] - totalCount;
count = (int)Math.Ceiling((remainValue * 1.0) / coinValues[i]);
}
else
{
if (totalCount <= coinValues[i + 1])
count = 1;
else
count = 0;
}
result[i] = new CoinCount() { coinValue = coinValues[i], noOfCoins = count };
totalCount += coinValues[i] * count;
}
return result;
}
サンプルプログラム:
#include<stdio.h>
#define LEN 9 // array length
int main(){
int coins[LEN]={0,0,0,0,0,0,0,0,0}; // coin count
int cointypes[LEN]={1000,500,100,50,20,10,5,2,1}; // declare your coins and note here {ASC order}
int sum =0; //temp variable for sum
int inc=0; // for loop
int amount=0; // for total amount
printf("Enter Amount :");
scanf("%d",&amount);
while(sum<amount){
if((sum+cointypes[inc])<=amount){
sum = sum+ cointypes[inc];
//printf("%d[1] - %d\n",cointypes[inc],sum);
//switch case to count number of notes and coin
switch(cointypes[inc]){
case 1000:
coins[0]++;
break;
case 500:
coins[1]++;
break;
case 100:
coins[2]++;
break;
case 50:
coins[3]++;
break;
case 20:
coins[4]++;
break;
case 10:
coins[5]++;
break;
case 5:
coins[6]++;
break;
case 2:
coins[7]++;
break;
case 1:
coins[8]++;
break;
}
}else{
inc++;
}
}
printf("note for %d in\n note 1000 * %d\n note 500 * %d\n note 100 * %d\n note 50 * %d\n note 20 * %d\n note 10 * %d\n coin 5 * %d\n coin 2 * %d\n coin 1 * %d\n",amount,coins[0],coins[1],coins[2],coins[3],coins[4],coins[5],coins[6],coins[7],coins[8]);
}