Problème de changement de pièce en Java
Imaginez que vous êtes dans un magasin et que vous devez rendre de la monnaie avec des pièces de différentes valeurs. Le défi ici est de trouver le nombre minimum de pièces dont vous avez besoin pour atteindre une somme donnée. À première vue, cela semble facile, mais plus la somme est élevée, plus la recherche de la solution devient complexe, surtout si vous avez un grand nombre de types de pièces disponibles. L’algorithme gourmand ne sera pas toujours le meilleur choix, et c’est là que la programmation dynamique entre en jeu pour trouver la solution optimale.
Introduction au problème :
Le problème du changement de pièce est défini comme suit : étant donné un ensemble de pièces de monnaie avec des valeurs distinctes et une somme cible, notre objectif est de déterminer le nombre minimum de pièces nécessaires pour former cette somme. Si ce n’est pas possible, nous renvoyons -1.
Approche naïve :
La première approche qui peut venir à l'esprit est de simplement essayer toutes les combinaisons possibles de pièces. Cependant, cette solution est extrêmement inefficace, surtout lorsque le nombre de pièces ou la somme cible est élevé. Cette méthode peut être décrite comme une approche de force brute, mais elle devient rapidement impraticable en raison de sa complexité exponentielle.
Optimisation avec la programmation dynamique :
La programmation dynamique offre une solution plus élégante et plus efficace. En stockant les solutions intermédiaires et en évitant les recalculs inutiles, nous pouvons résoudre ce problème en temps polynomial. Le principe est de construire un tableau où chaque position représente le nombre minimum de pièces nécessaires pour obtenir une somme donnée.
Voici un exemple de code Java qui résout le problème de changement de pièce en utilisant la programmation dynamique :
javaimport java.util.Arrays; public class CoinChange { public int coinChange(int[] coins, int amount) { int max = amount + 1; int[] dp = new int[amount + 1]; Arrays.fill(dp, max); dp[0] = 0; for (int i = 1; i <= amount; i++) { for (int coin : coins) { if (i - coin >= 0) { dp[i] = Math.min(dp[i], dp[i - coin] + 1); } } } return dp[amount] > amount ? -1 : dp[amount]; } public static void main(String[] args) { CoinChange cc = new CoinChange(); int[] coins = {1, 2, 5}; int amount = 11; int result = cc.coinChange(coins, amount); System.out.println("Nombre minimum de pièces nécessaires : " + result); } }
Explication du code :
- Initialisation : Nous commençons par créer un tableau
dp
où chaque élément représente le nombre minimum de pièces pour une somme spécifique. Chaque élément est initialisé à une valeur très élevée (iciamount + 1
), saufdp[0]
, qui est 0 (car 0 pièce est nécessaire pour une somme de 0). - Double boucle : La première boucle parcourt chaque somme de 1 à
amount
, et la seconde boucle vérifie chaque pièce. Si la pièce peut contribuer à la somme (c’est-à-dire si la somme actuelle moins la valeur de la pièce est non négative), nous mettons à jourdp[i]
avec le minimum entre la valeur actuelle dedp[i]
etdp[i - coin] + 1
. - Résultat final : Si à la fin,
dp[amount]
est resté à sa valeur initiale, cela signifie qu'il n'est pas possible de former la somme avec les pièces données, et nous retournons -1.
Complexité temporelle :
L'algorithme a une complexité en temps de O(n*m), où n est la somme cible, et m est le nombre de types de pièces disponibles. Cette solution est bien plus efficace que la recherche exhaustive.
Améliorations potentielles :
- Nous pourrions également envisager une optimisation mémoire en ne stockant que les informations nécessaires pour la somme en cours, ce qui réduirait la consommation de mémoire.
- De plus, une approche basée sur des algorithmes gourmands pourrait être tentée dans certains cas spécifiques où les pièces sont bien ordonnées, mais cette méthode n'est pas toujours fiable pour trouver la solution optimale.
Exemples de cas d'utilisation :
- Rendre la monnaie dans un distributeur automatique où les pièces sont limitées à des valeurs spécifiques.
- Calcul d’un budget précis pour un événement en utilisant différentes dénominations de billets.
- Optimisation de la répartition de ressources dans des systèmes à capacité limitée.
Conclusion :
Le problème de changement de pièce est un excellent exemple pour démontrer la puissance de la programmation dynamique dans la résolution de problèmes complexes. Avec Java, il est relativement simple d’implémenter une solution optimisée qui garantit un temps d'exécution raisonnable même pour de grandes entrées. Cette approche peut être étendue à d’autres problèmes similaires où le but est de minimiser ou maximiser une certaine quantité sous des contraintes données.
Avec cette solution, vous êtes bien armé pour résoudre des problèmes similaires, et la programmation dynamique devient un outil puissant dans votre boîte à outils de développeur.
Commentaires populaires
Pas de commentaires pour l'instant