web-dev-qa-db-fra.com

Trouvez le plus petit nombre de pièces nécessaires pour effectuer un changement de 1 à 99 cents

Récemment, j'ai demandé à mon collègue d'écrire un algorithme pour résoudre ce problème:

Trouvez le plus petit nombre de pièces nécessaires pour effectuer un changement de 1 à 99 cents. Les pièces ne peuvent être que des sous (1), des nickels (5), des dimes (10) et des quarts (25), et vous devez pouvoir faire chaque valeur de 1 à 99 (par incréments de 1 cent) avec ces pièces.

Cependant, j'ai réalisé que je ne savais pas comment faire moi-même sans examiner toutes les combinaisons possibles de pièces. Il doit y avoir un meilleur moyen de résoudre ce problème, mais je ne sais pas comment s'appellerait le nom générique de ce type d'algorithme, et je ne peux pas trouver un moyen de le simplifier au-delà de la recherche de toutes les solutions.

Je me demandais si quelqu'un pourrait me diriger dans la bonne direction ou proposer un algorithme plus efficace.

60
Daniel T.

Ce que vous recherchez, c'est Programmation dynamique .

En réalité, vous n'avez pas à énumérer toutes les combinaisons possibles pour toutes les valeurs possibles, car vous pouvez le construire au-dessus des réponses précédentes.

Votre algorithme doit prendre 2 paramètres:

  • La liste des valeurs de pièces possibles, ici [1, 5, 10, 25]
  • La plage à couvrir, ici [1, 99]

Et le but est de calculer le jeu minimal de pièces requis pour cette gamme.

Le moyen le plus simple est de procéder de manière ascendante:

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

C'est assez facile, à chaque étape, nous pouvons procéder en ajoutant au plus une pièce de monnaie, nous avons juste besoin de savoir où. Cela revient au fait que la plage [x,y] est incluse dans [x,y+1] et que l'ensemble minimal pour [x,y+1] devrait inclure l'ensemble minimal pour [x,y].

Comme vous l'avez peut-être remarqué cependant, il y a parfois des indécisions, c'est-à-dire que plusieurs jeux ont le même nombre de pièces. Dans ce cas, il ne sera possible de décider plus tard lequel des deux devrait être jeté.

Il devrait être possible d’améliorer son temps de jeu en remarquant que l’ajout d’une pièce de monnaie vous permet généralement de couvrir une gamme beaucoup plus large que celle pour laquelle vous l’avez ajoutée, je pense.

Par exemple, notez que:

 [1,5]    4*1  1*5
 [1,9]    4*1  1*5

nous ajoutons un nickel pour couvrir [1,5] mais cela nous donne gratuitement jusqu'à [1,9]!

Cependant, lorsque je traite des ensembles d’entrée scandaleux [2,3,5,10,25] pour couvrir [2,99], je ne sais pas comment vérifier rapidement la plage couverte par le nouvel ensemble, sinon ce serait plus efficace.

23
Matthieu M.

Vous pouvez très rapidement trouver une limite supérieure.

Dis, tu prends les trois quarts. Ensuite, il vous suffira de combler les «lacunes» 1-24, 26-49, 51-74, 76-99 avec d'autres pièces.

Trivialement, cela fonctionnerait avec 2 dimes, 1 nickel et 4 centimes.

Donc, 3 + 4 + 2 + 1 devrait être la limite supérieure de votre nombre de pièces. Chaque fois que votre algorithme de force brute dépasse cette valeur, vous pouvez instantanément arrêter de chercher plus profondément.

Le reste de la recherche doit être assez rapide pour une utilisation avec une programmation dynamique.

(edit: réponse fixe selon l'observation de Gabe)

9
Thomas

J'ai appris sur la programmation dynamique aujourd'hui, et voici le résultat:

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]

La programmation dynamique est vraiment magique.

7
sumzup

Vous avez besoin d'au moins 4 centimes, car vous voulez en obtenir 4 comme un changement, et vous ne pouvez le faire qu'avec des centimes.

Ce n'est pas optimal d'avoir plus de 4 centimes. Au lieu de 4 + x sous, vous pouvez en avoir 4 et x nickels - ils couvrent au moins la même plage.

Donc, vous avez exactement 4 centimes.

Vous avez besoin d'au moins 1 nickel, car vous voulez obtenir 5 comme un changement.

Ce n'est pas optimal d'avoir plus de 1 nickel. Au lieu de 1 + x nickels, vous pouvez avoir 1 nickel et x dimes - ils couvrent au moins la même plage.

Donc, vous avez exactement 1 nickel.

Vous avez besoin d'au moins 2 dimes, car vous voulez obtenir 20.

Cela signifie que vous avez 4 sous, 1 nickel et au moins 2 dimes.

Si vous aviez moins de 10 pièces, vous auriez moins de 3 quarts. Mais alors le changement maximum possible que vous pourriez obtenir en utilisant toutes les pièces est de 4 + 5 + 20 + 50 = 79, pas assez.

Cela signifie que vous avez au moins 10 pièces. La réponse de Thomas montre qu'en fait, si vous avez 4 sous, 1 nickel, 2 dimes et 3 quarts, tout va bien.

7
sdcvvc

Bonne question. C’est la logique que j’ai trouvée. Testé avec quelques scénarios dont 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);
    }
}

Quelques résultats: Lorsque maxCurrencyLevelForTest = 25

Min Number of coins needed: 7
Penny: 4 needed
Nickels: 1 needed
Dimes: 2 needed
Quarters: 0 needed

Lorsque 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

Je suppose que le code peut encore être remanié.

4
SKG

Edit: Comme les commentateurs l'ont noté, j'ai mal interprété la question. (La question est très similaire à un problème de base de CS, je vois des étudiants au collège devoir résoudre ...) waves hand Ce n'est pas la réponse que vous cherchez. Cela dit, bien que la réponse initiale soit fausse, nous pouvons l’utiliser comme un tremplin vers une solution O (n).

Prenez donc la mauvaise réponse ci-dessous, qui ne résout que pour une valeur unique (c’est-à-dire le minimum requis pour 68 cents) et exécutez-la simplement pour chaque valeur.

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)]

Cela vous donnera certainement une réponse valable, mais s'agit-il d'une réponse minimale? (C'est la partie la plus difficile. Actuellement, mon instinct me dit que oui, mais je pense toujours à celui-là ...)


( La mauvaise réponse )

Sensationnel. Boucles? Programmation dynamique? Vraiment les gars?

En Python:

amount = ( your_amount_in_cents )

quarters = amount // 25
dimes = amount % 25 // 10
nickels = amount % 25 % 10 // 5
pennies = amount % 25 % 10 % 5

Certaines de ces opérations modulo peuvent probablement être simplifiées ...

Ce n'est pas difficile, il vous suffit de réfléchir à la manière dont vous apportez des changements dans la vie réelle. Vous donnez des trimestres jusqu'à ce qu'un autre quart vous fasse dépasser le montant souhaité, vous distribuez des centimes jusqu'à ce que l'ajout d'un autre centime vous fasse dépasser le montant souhaité, etc. Puis convertissez-vous en opérations mathématiques: modulo et division. La même solution s'applique pour les dollars, convertissant les secondes en HH: MM: SS, etc.

3
Thanatos

En supposant que vous parlez de la devise américaine, vous voudriez un algorithme Greedy: http://en.wikipedia.org/wiki/Greedy_algorithm

Essentiellement, vous essayez toutes les dénominations, de la plus haute à la plus basse, en prenant autant de pièces que possible jusqu'à ce qu'il ne vous reste plus rien.

Pour le cas général, voir http://en.wikipedia.org/wiki/Change-making_problem , car vous voudriez utiliser la programmation dynamique ou linéaire pour trouver la réponse à des dénominations arbitraires dans lesquelles un algorithme glouton ne le ferait pas. travail.

2
Gabe

Après avoir échoué à trouver une bonne solution à ce type de problème en PHP, j'ai développé cette fonction. 

Il prend n’importe quelle somme d’argent (jusqu’à 999,99 $) et renvoie un tableau du nombre minimum de chaque facture/pièce requise pour atteindre cette valeur.

Il convertit d'abord la valeur en int en pennies (pour une raison quelconque, j'obtiendrais des erreurs à la fin lorsque j'utilisais des valeurs float standard). 

Les dénominations retournées sont également en centimes (c.-à-d.: 5000 = 50 $, 100 = 1 $, etc.).

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
2
user3537590

La tâche

Find the least number of coins required, that can make any change from 1 to 99 cent.

diffère de la tâche

For each single change from 1 to 99 cent, find the least number of coins required.

parce que la solution pourrait être un multiset complet de pièces complètement différent.

Supposons que vous n’ayez pas des pièces de monnaie (1), (5), (10) et (25), mais des pièces de monnaie (1), (3), (5) et (17) Centimes: effectuer le changement pour 5, vous n'avez besoin que d'une (5) pièce; mais pour tous les changements de 1 à 5, vous avez besoin de deux (1) pièces de monnaie et une (3) pièce de monnaie, pas n'importe quelle (5) pièce de monnaie.

L'algorithme glouton itère de la plus petite valeur à la plus grande, concernant les valeurs de changement et les valeurs de pièces:

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...

C'est à 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]

Et en 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;
}
1
comonad

Ceci pourrait être une solution générique en 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();
        }
    }
}

Je n'y ai pas bien réfléchi… il est trop tard le soir. Je pensais que la réponse devrait être 9 dans ce cas, mais Gabe a dit qu'il devrait être 10 ... ce que cela donne. Je suppose que cela dépend de la façon dont vous interprétez la question ... cherchons-nous le moins de pièces pouvant produire une valeur <= X, ou le moins de pièces pouvant produire une valeur <= X utilisant le moins de pièces ? Par exemple ... Je suis presque sûr que nous pouvons créer n'importe quelle valeur avec seulement 9 pièces, sans les nickels, mais alors pour en produire 9 ... vous avez besoin de ... oh ... je vois. Vous auriez besoin de 9 centimes, ce que vous n’avez pas, car ce n’est pas ce que nous avons choisi ... dans cela cas, je pense cette réponse est correcte. Juste une implémentation récursive de l'idée de Thomas, mais je ne sais pas pourquoi il s'est arrêté là… vous n'avez pas besoin de force brute.

Edit: Ceci soit faux.

1
mpen

Une version 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
0
dbasnett

Il existe quelques réponses similaires mais ma solution avec Java semble un peu plus facile à comprendre. Regarde ça.

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;
 }
0
osayilgan

Je suis tombé sur celui-ci aujourd'hui, alors qu'il étudiait 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)

Prend une chaîne de dénominations disponibles, séparées par des virgules, et le montant cible.

Implémentation 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;
                    }
                }
            }
        }
    }
0
Evgeny

Si j'ai utilisé les valeurs du système monétaire standard, il est très facile de compter le nombre minimum de pièces par une seule boucle. Consommez toujours la valeur maximale de la pièce et si cela n’est pas possible, vérifiez l’option suivante. Mais si vous avez un système comme celui que vous avez avec des pièces telles que 1,2,3,4, alors cela ne fonctionne pas. Je suppose que l’idée des pièces en tant que 1, 2, 5, 10, 25 est de faciliter le calcul pour les humains.

0
dinesh707

Pour ce problème, l’approche Greedy donne une meilleure solution que DP ou autres. Approche gourmande: trouvez la plus grande valeur nominale inférieure à la valeur requise et ajoutez-la à l'ensemble des pièces à livrer. Abaissez les cents requis par la dénomination que vous venez d'ajouter et répétez jusqu'à ce que les cents requis deviennent zéro.

Ma solution (approche gourmande) en solution Java:

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));
    }

}
0
Jeya

En général, si vous avez vos pièces COIN [] et votre "plage de variation" 1..MAX, les éléments suivants doivent trouver le nombre maximal de pièces.

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

Je ne sais pas si le chèque pour la minimalité des pièces dans le conditionnel intérieur est, à proprement parler, nécessaire. Je penserais que la chaîne minimale d’additions de pièces finira par être correcte, mais mieux vaut prévenir que guérir.

0
Vatine

Voici une solution simple c # utilisant Linq.

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; }
    }
}    
0
Vlad

Voici ce que je pense ... Une chose intéressante est que nous devons vérifier le nombre de pièces minimum nécessaire pour former jusqu'à coin_with_max_value (25 dans notre cas) - 1 seulement . Après cela, il suffit de calculer la somme de ces pièces min. À partir de là, il suffit d’ajouter un certain nombre de coin_with_max_value pour former un nombre quelconque jusqu’au coût total, en fonction de la différence entre le coût total et la somme obtenue. C'est tout.

Donc, pour les valeurs que nous avons prises, une fois que les pièces min pour 24 sont trouvées: [1, 2, 2, 5, 10, 10] . Nous devons simplement continuer à ajouter une pièce de 25 pour chaque valeur supérieure à 30 (somme de pièces)) .. La réponse finale pour 99 est:
[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)
0
i_am_saurabh

Voici une version simple en 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

Lorsqu'il est exécuté, il imprime ce qui suit sur stdout.

[1, 1, 1, 1, 5, 10, 10, 25, 25, 25]

Le code est assez explicite (merci Python!), Mais l’algorithme consiste essentiellement à ajouter la plus grosse pièce disponible qui ne vous place pas au-dessus du total actuel que vous visez dans votre liste temporaire de pièces (t dans ce cas ). Une fois que vous avez trouvé le jeu de pièces le plus efficace pour un total particulier, assurez-vous qu'il y a au moins autant de pièces dans la liste requise. Faites cela pour chaque total de 1 à 99 cents, et vous avez terminé.

0
Zeke

J'ai écrit cet algorithme pour un type de problème similaire avec DP, puis-je vous aider?

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);
    }

}
0
Abhishek Choudhary

La solution avec approche gourmande en Java est la suivante: 

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;
    }
}

Mais cela fonctionne pour un montant unique. Si vous voulez l'exécuter pour une plage, nous devons l'appeler pour chaque quantité de plage.

0
Ashok

Voici ma solution, toujours en Python et utilisant la programmation dynamique. Tout d'abord, je génère la séquence minimale de pièces requise pour effectuer une modification pour chaque montant compris dans l'intervalle 1..99, et à partir de ce résultat, je recherche le nombre maximal de pièces nécessaire à chaque dénomination:

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

Exécuter min_any_change() donne la réponse que nous recherchions: {'pennies': 4, 'nickels': 1, 'dimes': 2, 'quarters': 3}. En guise de test, nous pouvons essayer de supprimer une pièce de n'importe quelle dénomination et de vérifier s'il est encore possible d'effectuer de la monnaie pour un montant compris dans l'intervalle 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)

Si nous testons le résultat obtenu ci-dessus, nous obtenons une True:

test([1, 1, 1, 1, 5, 10, 10, 25, 25, 25])

Mais si nous retirons une seule pièce de monnaie, quelle que soit sa dénomination, nous aurons une False:

test([1, 1, 1, 5, 10, 10, 25, 25, 25])
0
Óscar López

Inspiré de ceci https://www.youtube.com/watch?v=GafjS0FfAC0 Suivant
1) optimal sous-problème 2) Principes de superposition de sous-problèmes Introduits dans la vidéo

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}
                };
        }
    }
}

Cas de test:

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);
            }
        }
    }
}
0
yi.han

D'une part, cela a été répondu. En revanche, la plupart des réponses nécessitent plusieurs lignes de code. Cette réponse en Python ne nécessite pas beaucoup de lignes de code, mais simplement de nombreuses lignes de pensée ^ _ ^:

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]

C’est un algorithme de redimensionnement très simple qui peut s’arrêter pour des entrées que je n’ai pas encore prises en compte, mais je pense qu’il devrait être robuste. Il dit essentiellement "pour ajouter un nouveau type de pièce au portefeuille, jetez un coup d'œil au type de pièce suivant N, puis ajoutez le nombre de nouvelles pièces nécessaires pour créer target = N - 1." Il calcule que vous avez besoin d'au moins ceil((target - wallet_value)/coin_value) pour le faire, et ne vérifie pas si cela fera également chaque nombre entre les deux. Notez que la syntaxe code le "de 0 à 99 cents" en ajoutant le nombre final "100" car cela donne le target final approprié. 

La raison pour laquelle il ne vérifie pas est quelque chose comme, "s'il le peut, il le fera automatiquement." En d'autres termes, une fois que vous avez effectué cette étape pour un sou (valeur 1), l'algorithme peut "décomposer" un nickel (valeur 5) en n'importe quel sous-intervalle 0 - 4. Une fois que vous l'avez effectué pour un sou, l'algorithme peut maintenant "rompre". "un centime (valeur 10). Etc.

Bien sûr, cela n’exige pas ces intrants particuliers; vous pouvez aussi utiliser des devises étranges:

>>> optimum_change(1, 4, 7, 8, 100)
[3, 1, 0, 12]

Remarquez comment il ignore automatiquement la pièce de 7 parce qu’il sait qu’il peut déjà "casser" un 8 avec le changement qu’il a.

0
CR Drost

C'est le code en c # pour trouver la solution.

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;
}
0

Exemple de programme:

#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]); 

    }
0
Elshan