La solution de ce problème a été donnée un million de fois sur Internet. Le problème s'appelle Le problème du changement de monnaie . On peut trouver des solutions à http://rosettacode.org/wiki/Count_the_coins et son modèle mathématique à http://jaqm.ro/issues/volume-5,issue-2/pdfs/patterson_harmel.pdf (ou Google problème de changement de pièces ).
Au fait, la solution Scala de Tsagadai est intéressante. Cet exemple produit soit 1 soit 0. Comme effet secondaire, il liste sur la console toutes les solutions possibles. Il affiche la solution, mais ne parvient pas à la rendre utilisable de quelque manière que ce soit.
Pour être aussi utile que possible, le code doit retourner un List[List[Int]]
afin de permettre d'obtenir le nombre de solutions (longueur de la liste des listes), la "meilleure" solution (la liste la plus courte), ou toutes les solutions possibles.
Voici un exemple. Il est très inefficace, mais il est facile à comprendre.
object Sum extends App {
def sumCombinations(total: Int, numbers: List[Int]): List[List[Int]] = {
def add(x: (Int, List[List[Int]]), y: (Int, List[List[Int]])): (Int, List[List[Int]]) = {
(x._1 + y._1, x._2 ::: y._2)
}
def sumCombinations(resultAcc: List[List[Int]], sumAcc: List[Int], total: Int, numbers: List[Int]): (Int, List[List[Int]]) = {
if (numbers.isEmpty || total < 0) {
(0, resultAcc)
} else if (total == 0) {
(1, sumAcc :: resultAcc)
} else {
add(sumCombinations(resultAcc, sumAcc, total, numbers.tail), sumCombinations(resultAcc, numbers.head :: sumAcc, total - numbers.head, numbers))
}
}
sumCombinations(Nil, Nil, total, numbers.sortWith(_ > _))._2
}
println(sumCombinations(15, List(1, 2, 5, 10)) mkString "\n")
}
Lorsqu'il est exécuté, il affiche :
List(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1)
List(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2)
List(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2)
List(1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2)
List(1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2)
List(1, 1, 1, 1, 1, 2, 2, 2, 2, 2)
List(1, 1, 1, 2, 2, 2, 2, 2, 2)
List(1, 2, 2, 2, 2, 2, 2, 2)
List(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5)
List(1, 1, 1, 1, 1, 1, 1, 1, 2, 5)
List(1, 1, 1, 1, 1, 1, 2, 2, 5)
List(1, 1, 1, 1, 2, 2, 2, 5)
List(1, 1, 2, 2, 2, 2, 5)
List(2, 2, 2, 2, 2, 5)
List(1, 1, 1, 1, 1, 5, 5)
List(1, 1, 1, 2, 5, 5)
List(1, 2, 2, 5, 5)
List(5, 5, 5)
List(1, 1, 1, 1, 1, 10)
List(1, 1, 1, 2, 10)
List(1, 2, 2, 10)
List(5, 10)
El sumCombinations()
peut être utilisée seule, et le résultat peut être analysé plus avant pour afficher la "meilleure" solution (la liste la plus courte), ou le nombre de solutions (le nombre de listes).
Notez que même ainsi, les exigences peuvent ne pas être entièrement satisfaites. Il se peut que l'ordre de chaque liste dans la solution soit important. Dans un tel cas, chaque liste devrait être dupliquée autant de fois qu'il y a de combinaisons de ses éléments. Ou bien nous pourrions être intéressés uniquement par les combinaisons qui sont différentes.
Par exemple, on peut considérer que List(5, 10)
devrait donner deux combinaisons : List(5, 10)
y List(10, 5)
. Pour List(5, 5, 5)
il peut donner trois combinaisons ou une seule, selon les besoins. Pour les nombres entiers, les trois permutations sont équivalentes, mais si nous avons affaire à des pièces de monnaie, comme dans le "problème du changement de pièce", elles ne le sont pas.
Les exigences ne précisent pas non plus si chaque numéro (ou pièce) peut être utilisé une seule fois ou plusieurs fois. On pourrait (et on devrait !) généraliser le problème à une liste de listes d'occurrences de chaque nombre. Dans la vie réelle, cela se traduit par "quelles sont les manières possibles de gagner une certaine somme d'argent avec un ensemble de pièces (et non un ensemble de valeurs de pièces)". Le problème original n'est qu'un cas particulier de ce problème, où nous avons autant d'occurrences de chaque pièce que nécessaire pour obtenir le montant total avec chaque valeur de pièce.
9 votes
L'article de wikipedia ( fr.wikipedia.org/wiki/Sous-ensemble_somme_problème ) mentionne même que ce problème est une bonne introduction à la classe des problèmes NP-complets.
6 votes
Peut-on utiliser plus d'une fois le même élément de l'ensemble original ? Par exemple, si l'entrée est {1,2,3,5} et la cible 10, est-ce que 5 + 5 = 10 est une solution acceptable ?
0 votes
Juste une fois. Si un nombre entier doit être répété, il apparaît comme un nouvel élément.
1 votes
stackoverflow.com/a/64380474/585411 montre comment utiliser la programmation dynamique pour éviter tout travail inutile dans la production des réponses.