Como você testaria todas as combinações possíveis de acréscimos de um determinado conjunto N
de números para que eles somassem um determinado número final?
Um breve exemplo:
- Conjunto de números a serem adicionados:
N = {1,5,22,15,0,...}
- Resultado desejado:
12345
Respostas:
Esse problema pode ser resolvido com uma combinação recursiva de todas as somas possíveis, filtrando as que atingem o alvo. Aqui está o algoritmo em Python:
Esse tipo de algoritmo é muito bem explicado nos seguintes aula de programação abstrata de Standford - este vídeo é muito recomendável para entender como a recursão funciona para gerar permutações de soluções.
Editar
O acima como uma função de gerador, tornando-o um pouco mais útil. Requer Python 3.3 ou superior por causa de
yield from
.Aqui está a versão Java do mesmo algoritmo:
É exatamente a mesma heurística. Meu Java está um pouco enferrujado, mas acho fácil de entender.
Conversão de C # da solução Java: (por @JeremyThompson)
Solução Ruby: (por @emaillenin)
Editar: discussão sobre complexidade
Como outros mencionam, este é um problema difícil de NP . Pode ser resolvido no tempo exponencial O (2 ^ n); por exemplo, para n = 10, haverá 1024 soluções possíveis. Se os alvos que você está tentando alcançar estiverem em um intervalo baixo, esse algoritmo funcionará. Então, por exemplo:
subset_sum([1,2,3,4,5,6,7,8,9,10],100000)
gera 1024 ramificações porque o destino nunca consegue filtrar possíveis soluções.Por outro lado,
subset_sum([1,2,3,4,5,6,7,8,9,10],10)
gera apenas 175 agências, porque a meta de atingir10
chega a filtrar muitas combinações.Se
N
eTarget
são grandes números, deve-se passar para uma versão aproximada da solução.fonte
[1, 2, 0, 6, -3, 3], 3
- ele apenas gera resultados[1,2], [0,3], [3]
ausentes, como[6, -3, 3]
[1, 2, 5], 5
apenas saídas[5]
, quando[1, 1, 1, 1, 1]
,[2, 2, 1]
e[2, 1, 1, 1]
são soluções.i+1
inremaining = numbers[i+1:]
. Parece que esse algoritmo não permite duplicatas.[1, 1, 3]
dê uma olhada em stackoverflow.com/a/34971783/3684296 (Python)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.
Quando executado, ele exibe:
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)
eList(10, 5)
. PoisList(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.
fonte
Em Haskell :
E J :
Como você pode notar, ambos adotam a mesma abordagem e dividem o problema em duas partes: gere cada membro do conjunto de poderes e verifique a soma de cada membro no alvo.
Existem outras soluções, mas essa é a mais direta.
Você precisa de ajuda com um deles ou encontrar uma abordagem diferente?
fonte
not in scope: 'subsequences'
qualquer ponteiro?import Data.List
Uma versão Javascript:
fonte
remaining = numbers.slice();
remaining.slice(i + 1);
de outra formanumbers.slice(i + 1);
muda a matriz númerosslice
retorna uma cópia (rasa), não modifica anumbers
matriz.Versão C ++ do mesmo algoritmo
fonte
Versão C # da resposta de código @msalvadores
fonte
Pensei em usar uma resposta desta pergunta, mas não consegui, então aqui está a minha resposta. Ele está usando uma versão modificada de uma resposta em Estrutura e interpretação de programas de computador . Penso que esta é uma solução recursiva melhor e deve agradar mais aos puristas.
Minha resposta está no Scala (e desculpas se o meu Scala for péssimo, eu apenas comecei a aprender). A loucura findSumCombinations é classificar e exclusivo a lista original da recursão para evitar enganos.
Para usá-lo:
fonte
Eu converti acima lógica de python para php ..
fonte
Outra solução python seria usar o
itertools.combinations
módulo da seguinte maneira:Resultado:
[(8, 7), (5, 10), (3, 8, 4), (3, 5, 7)]
fonte
Aqui está uma solução em R
fonte
subset_sum(numbers = c(1:2), target = 5)
retorna"sum(1+2+2)=5"
. Mas a combinação 1 + 1 + 1 + 1 + 1 está ausente. Definir metas para números mais altos (por exemplo, 20) está faltando ainda mais combinações.subset_sum(1:2, 4)
deve retornar nenhuma solução porque não há nenhuma combinação de 1 e 2 que adiciona a 4. O que precisa ser adicionado a minha função é uma fuga, sei
for maior do que o comprimentonumbers
Aqui está uma versão Java que é adequada para N pequeno e soma de destino muito grande, quando a complexidade
O(t*N)
(a solução dinâmica) é maior que o algoritmo exponencial. Minha versão usa um ataque no meio, junto com um pouco de mudança para reduzir a complexidade do clássico ingênuoO(n*2^n)
para o clássico.O(2^(n/2))
.Se você quiser usar isso para conjuntos com entre 32 e 64 elementos, altere o
int
que representa o subconjunto atual na função step para umlong
embora o desempenho obviamente diminua drasticamente à medida que o tamanho do conjunto aumenta. Se você quiser usar isso para um conjunto com número ímpar de elementos, adicione um 0 ao conjunto para torná-lo numerado.fonte
Isso é semelhante a um problema de troca de moedas
fonte
Algoritmo muito eficiente usando tabelas que escrevi em c ++ alguns anos atrás.
Se você definir PRINT 1, imprimirá todas as combinações (mas não usará o método eficiente).
É tão eficiente que calcula mais de 10 ^ 14 combinações em menos de 10ms.
fonte
Versão do Excel VBA abaixo. Eu precisava implementar isso no VBA (não é minha preferência, não me julgue!) E usei as respostas nesta página para a abordagem. Estou enviando caso outras pessoas também precisem de uma versão VBA.
A saída (gravada na janela Imediata) deve ser:
fonte
Aqui está uma versão melhor com melhor formatação de saída e recursos do C ++ 11:
fonte
Versão não recursiva Java que simplesmente continua adicionando elementos e redistribuindo-os entre os valores possíveis.
0
são ignorados e funcionam para listas fixas (o que você recebe é o que pode tocar) ou uma lista de números repetíveis.Entrada de amostra:
Saída de amostra:
fonte
Para encontrar as combinações usando o Excel - (é bastante fácil). (Seu computador não deve estar muito lento)
Faça o download do arquivo excel "Soma ao destino".
Siga as instruções na página do site.
espero que isto ajude.
fonte
Conversão Swift 3 da solução Java: (por @JeremyThompson)
uso:
fonte
Isso pode ser usado para imprimir todas as respostas também
A complexidade do tempo é exponencial. Ordem de 2 ^ n
fonte
Eu estava fazendo algo semelhante para uma tarefa de scala. Pensei em postar minha solução aqui:
fonte
Eu enviei a amostra de C # para o Objective-c e não a vi nas respostas:
fonte
Resposta do @ KeithBeller com nomes de variáveis ligeiramente alterados e alguns comentários.
fonte
Versão PHP , inspirada na versão C # de Keith Beller.
A versão PHP de bala não funcionou para mim, porque eu não precisava agrupar números. Eu queria uma implementação mais simples com um valor de destino e um conjunto de números. Esta função também remove quaisquer entradas duplicadas.
fonte
Recomendado como resposta:
Aqui está uma solução usando os geradores es2015 :
O uso de geradores pode realmente ser muito útil, pois permite interromper a execução do script imediatamente após encontrar um subconjunto válido. Isso contrasta com as soluções sem geradores (isto é, com falta de estado) que precisam percorrer todos os subconjuntos de
numbers
fonte
Deduzir 0 em primeiro lugar. Zero é uma identificação para adição, portanto é inútil pelas leis monóides neste caso particular. Deduza também números negativos também se você quiser subir para um número positivo. Caso contrário, você também precisaria de operação de subtração.
Então ... o algoritmo mais rápido que você pode obter nesse trabalho específico é o seguinte, fornecido em JS.
Este é um algoritmo muito rápido, mas se você classificar a
data
matriz por descida , será ainda mais rápido. O uso.sort()
é insignificante, pois o algoritmo terminará com invocações muito menos recursivas.fonte
Versão Perl (da resposta principal):
Resultado:
Versão Javascript:
Uma linha de Javascript que realmente retorna resultados (em vez de imprimi-lo):
E o meu favorito, uma linha com retorno de chamada:
fonte
Esta é uma solução dinâmica para o JS dizer quantas maneiras alguém pode obter a soma certa. Essa pode ser a solução certa se você pensar em complexidade de tempo e espaço.
fonte
fonte
Não gostei da solução Javascript. Vi que é por isso que construo uma para mim usando aplicação parcial, fechamentos e recursão:
Ok, eu estava preocupada principalmente se a matriz de combinações poderia atender ao objetivo do requisito, mas com isso abordado, você pode começar a encontrar o restante das combinações
Aqui, apenas defina o alvo e passe a matriz de combinações.
a implementação atualmente eu vim com
fonte