A solução desse problema foi dada um milhão de vezes na Internet. O problema é chamado O problema de troca de moedas . Pode-se encontrar soluções em http://rosettacode.org/wiki/Count_the_coins e modelo matemático em http://jaqm.ro/issues/volume-5,issue-2/pdfs/patterson_harmel.pdf (ou troca de moeda do Google problema ).
A propósito, a solução Scala de Tsagadai é interessante. Este exemplo produz 1 ou 0. Como efeito colateral, ele lista no console todas as soluções possíveis. Ele exibe a solução, mas falha ao torná-la utilizável de qualquer forma.
Para ser o mais útil possível, o código deve retornar um List[List[Int]]
para permitir obter o número de solução (comprimento da lista de listas), a solução "melhor" (a lista mais curta) ou todas as soluções possíveis.
Aqui está um exemplo. É muito ineficiente, mas é fácil de entender.
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")
}
Quando executado, ele exibe:
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)
A sumCombinations()
função pode ser usada por si só e o resultado pode ser analisado posteriormente para exibir a "melhor" solução (a lista mais curta) ou o número de soluções (o número de listas).
Observe que, mesmo assim, os requisitos podem não ser totalmente satisfeitos. Pode acontecer que a ordem de cada lista na solução seja significativa. Nesse caso, cada lista teria que ser duplicada quantas vezes houver combinação de seus elementos. Ou podemos estar interessados apenas nas combinações diferentes.
Por exemplo, podemos considerar que List(5, 10)
deve fornecer duas combinações: List(5, 10)
e List(10, 5)
. Pois List(5, 5, 5)
poderia dar três combinações ou apenas uma, dependendo dos requisitos. Para números inteiros, as três permutações são equivalentes, mas se estamos lidando com moedas, como no "problema de troca de moedas", elas não são.
Também não é declarado nos requisitos a questão de saber se cada número (ou moeda) pode ser usado apenas uma ou várias vezes. Poderíamos (e deveríamos!) Generalizar o problema para uma lista de listas de ocorrências de cada número. Isso se traduz na vida real em "quais são as maneiras possíveis de ganhar uma certa quantia de dinheiro com um conjunto de moedas (e não um conjunto de valores de moedas)". O problema original é apenas um caso particular deste, em que temos tantas ocorrências de cada moeda quanto necessárias para fazer o valor total com cada valor de uma única moeda.