valores valor una sumen sumar suman suma resultado qué que para obtener números muy macro llegar importe hasta encontrar combinaciones cantidad buscar buena averiguar algorithm language-agnostic search combinations

algorithm - una - macro para encontrar que valores suman un importe en excel



Encontrar todas las combinaciones posibles de números para alcanzar una suma dada (24)

¿Cómo haría para probar todas las posibles combinaciones de adiciones de un conjunto dado de números para que se sumen a un número final dado?

Ejemplo:

  • Conjunto de números para agregar: {1,5,22,15,0, ...}
  • Resultado deseado: 12345

Algoritmo muy eficiente usando tablas que escribí en pareja c ++ hace un año.

Si configura PRINT 1 imprimirá todas las combinaciones (pero no usará el método eficiente).

Es tan eficiente que calcula más de 10 ^ 14 combinaciones en menos de 10ms.

#include <stdio.h> #include <stdlib.h> //#include "CTime.h" #define SUM 300 #define MAXNUMsSIZE 30 #define PRINT 0 long long CountAddToSum(int,int[],int,const int[],int); void printr(const int[], int); long long table1[SUM][MAXNUMsSIZE]; int main() { int Nums[]={3,4,5,6,7,9,13,11,12,13,22,35,17,14,18,23,33,54}; int sum=SUM; int size=sizeof(Nums)/sizeof(int); int i,j,a[]={0}; long long N=0; //CTime timer1; for(i=0;i<SUM;++i) for(j=0;j<MAXNUMsSIZE;++j) table1[i][j]=-1; N = CountAddToSum(sum,Nums,size,a,0); //algorithm //timer1.Get_Passd(); //printf("/nN=%lld time=%.1f ms/n", N,timer1.Get_Passd()); printf("/nN=%lld /n", N); getchar(); return 1; } long long CountAddToSum(int s, int arr[],int arrsize, const int r[],int rsize) { static int totalmem=0, maxmem=0; int i,*rnew; long long result1=0,result2=0; if(s<0) return 0; if (table1[s][arrsize]>0 && PRINT==0) return table1[s][arrsize]; if(s==0) { if(PRINT) printr(r, rsize); return 1; } if(arrsize==0) return 0; //else rnew=(int*)malloc((rsize+1)*sizeof(int)); for(i=0;i<rsize;++i) rnew[i]=r[i]; rnew[rsize]=arr[arrsize-1]; result1 = CountAddToSum(s,arr,arrsize-1,rnew,rsize); result2 = CountAddToSum(s-arr[arrsize-1],arr,arrsize,rnew,rsize+1); table1[s][arrsize]=result1+result2; free(rnew); return result1+result2; } void printr(const int r[], int rsize) { int lastr=r[0],count=0,i; for(i=0; i<rsize;++i) { if(r[i]==lastr) count++; else { printf(" %d*%d ",count,lastr); lastr=r[i]; count=1; } } if(r[i-1]==lastr) printf(" %d*%d ",count,lastr); printf("/n"); }


Aquí hay una mejor versión con mejores características de formato de salida y C ++ 11:

void subset_sum_rec(std::vector<int> & nums, const int & target, std::vector<int> & partialNums) { int currentSum = std::accumulate(partialNums.begin(), partialNums.end(), 0); if (currentSum > target) return; if (currentSum == target) { std::cout << "sum(["; for (auto it = partialNums.begin(); it != std::prev(partialNums.end()); ++it) cout << *it << ","; cout << *std::prev(partialNums.end()); std::cout << "])=" << target << std::endl; } for (auto it = nums.begin(); it != nums.end(); ++it) { std::vector<int> remaining; for (auto it2 = std::next(it); it2 != nums.end(); ++it2) remaining.push_back(*it2); std::vector<int> partial = partialNums; partial.push_back(*it); subset_sum_rec(remaining, target, partial); } }


Aquí hay una solución en R

subset_sum = function(numbers,target,partial=0){ if(any(is.na(partial))) return() s = sum(partial) if(s == target) print(sprintf("sum(%s)=%s",paste(partial[-1],collapse="+"),target)) if(s > target) return() for( i in seq_along(numbers)){ n = numbers[i] remaining = numbers[(i+1):length(numbers)] subset_sum(remaining,target,c(partial,n)) } }


Aquí hay una versión de Java que es adecuada para pequeñas N y suma de objetivos muy grande, cuando la complejidad O(t*N) (la solución dinámica) es mayor que el algoritmo exponencial. Mi versión utiliza un ataque meet in the middle, junto con un pequeño cambio para reducir la complejidad del clásico ingenuo O(n*2^n) a O(2^(n/2)) .

Si desea utilizar esto para conjuntos con entre 32 y 64 elementos, debe cambiar la int que representa el subconjunto actual en la función de paso a una long aunque el rendimiento disminuirá drásticamente a medida que aumenta el tamaño del conjunto. Si desea usar esto para un conjunto con un número impar de elementos, debe agregar un 0 al conjunto para que esté incluso numerado.

import java.util.ArrayList; import java.util.List; public class SubsetSumMiddleAttack { static final int target = 100000000; static final int[] set = new int[]{ ... }; static List<Subset> evens = new ArrayList<>(); static List<Subset> odds = new ArrayList<>(); static int[][] split(int[] superSet) { int[][] ret = new int[2][superSet.length / 2]; for (int i = 0; i < superSet.length; i++) ret[i % 2][i / 2] = superSet[i]; return ret; } static void step(int[] superSet, List<Subset> accumulator, int subset, int sum, int counter) { accumulator.add(new Subset(subset, sum)); if (counter != superSet.length) { step(superSet, accumulator, subset + (1 << counter), sum + superSet[counter], counter + 1); step(superSet, accumulator, subset, sum, counter + 1); } } static void printSubset(Subset e, Subset o) { String ret = ""; for (int i = 0; i < 32; i++) { if (i % 2 == 0) { if ((1 & (e.subset >> (i / 2))) == 1) ret += " + " + set[i]; } else { if ((1 & (o.subset >> (i / 2))) == 1) ret += " + " + set[i]; } } if (ret.startsWith(" ")) ret = ret.substring(3) + " = " + (e.sum + o.sum); System.out.println(ret); } public static void main(String[] args) { int[][] superSets = split(set); step(superSets[0], evens, 0,0,0); step(superSets[1], odds, 0,0,0); for (Subset e : evens) { for (Subset o : odds) { if (e.sum + o.sum == target) printSubset(e, o); } } } } class Subset { int subset; int sum; Subset(int subset, int sum) { this.subset = subset; this.sum = sum; } }


En Haskell :

filter ((==) 12345 . sum) $ subsequences [1,5,22,15,0,..]

Y J :

(]#~12345=+/@>)(]<@#~[:#:@i.2^#)1 5 22 15 0 ...

Como puede observar, ambos toman el mismo enfoque y dividen el problema en dos partes: generar cada miembro del grupo de poder y verificar la suma de cada miembro con respecto al objetivo.

Hay otras soluciones, pero esta es la más directa.

¿Necesita ayuda con alguna de las dos opciones o encuentra un enfoque diferente?


Estaba haciendo algo similar para una tarea de scala. Pensé en publicar mi solución aquí:

def countChange(money: Int, coins: List[Int]): Int = { def getCount(money: Int, remainingCoins: List[Int]): Int = { if(money == 0 ) 1 else if(money < 0 || remainingCoins.isEmpty) 0 else getCount(money, remainingCoins.tail) + getCount(money - remainingCoins.head, remainingCoins) } if(money == 0 || coins.isEmpty) 0 else getCount(money, coins) }


Este problema se puede resolver con una combinación recursiva de todas las sumas posibles filtrando las que alcanzan el objetivo. Aquí está el algoritmo en Python:

def subset_sum(numbers, target, partial=[]): s = sum(partial) # check if the partial sum is equals to target if s == target: print "sum(%s)=%s" % (partial, target) if s >= target: return # if we reach the number why bother to continue for i in range(len(numbers)): n = numbers[i] remaining = numbers[i+1:] subset_sum(remaining, target, partial + [n]) if __name__ == "__main__": subset_sum([3,9,8,4,5,7,10],15) #Outputs: #sum([3, 8, 4])=15 #sum([3, 5, 7])=15 #sum([8, 7])=15 #sum([5, 10])=15

Este tipo de algoritmos están muy bien explicados en la siguiente conferencia de programación abstracta de Standford : este video es muy recomendable para entender cómo funciona la recursión para generar permutaciones de soluciones.

Editar

Lo anterior como una función del generador, haciéndolo un poco más útil. Requiere Python 3.3+ debido al yield from .

def subset_sum(numbers, target, partial=[], partial_sum=0): if partial_sum == target: yield partial if partial_sum >= target: return for i, n in enumerate(numbers): remaining = numbers[i + 1:] yield from subset_sum(remaining, target, partial + [n], partial_sum + n)

Aquí está la versión de Java del mismo algoritmo:

package tmp; import java.util.ArrayList; import java.util.Arrays; class SumSet { static void sum_up_recursive(ArrayList<Integer> numbers, int target, ArrayList<Integer> partial) { int s = 0; for (int x: partial) s += x; if (s == target) System.out.println("sum("+Arrays.toString(partial.toArray())+")="+target); if (s >= target) return; for(int i=0;i<numbers.size();i++) { ArrayList<Integer> remaining = new ArrayList<Integer>(); int n = numbers.get(i); for (int j=i+1; j<numbers.size();j++) remaining.add(numbers.get(j)); ArrayList<Integer> partial_rec = new ArrayList<Integer>(partial); partial_rec.add(n); sum_up_recursive(remaining,target,partial_rec); } } static void sum_up(ArrayList<Integer> numbers, int target) { sum_up_recursive(numbers,target,new ArrayList<Integer>()); } public static void main(String args[]) { Integer[] numbers = {3,9,8,4,5,7,10}; int target = 15; sum_up(new ArrayList<Integer>(Arrays.asList(numbers)),target); } }

Es exactamente la misma heurística. Mi Java está un poco oxidado, pero creo que es fácil de entender.

C # conversion of Java solution: (por @JeremyThompson)

public static void Main(string[] args) { List<int> numbers = new List<int>() { 3, 9, 8, 4, 5, 7, 10 }; int target = 15; sum_up(numbers, target); } private static void sum_up(List<int> numbers, int target) { sum_up_recursive(numbers, target, new List<int>()); } private static void sum_up_recursive(List<int> numbers, int target, List<int> partial) { int s = 0; foreach (int x in partial) s += x; if (s == target) Console.WriteLine("sum(" + string.Join(",", partial.ToArray()) + ")=" + target); if (s >= target) return; for (int i = 0; i < numbers.Count; i++) { List<int> remaining = new List<int>(); int n = numbers[i]; for (int j = i + 1; j < numbers.Count; j++) remaining.Add(numbers[j]); List<int> partial_rec = new List<int>(partial); partial_rec.Add(n); sum_up_recursive(remaining, target, partial_rec); } }

Solución Ruby: (por @emaillenin)

def subset_sum(numbers, target, partial=[]) s = partial.inject 0, :+ # check if the partial sum is equals to target puts "sum(#{partial})=#{target}" if s == target return if s >= target # if we reach the number why bother to continue (0..(numbers.length - 1)).each do |i| n = numbers[i] remaining = numbers.drop(i+1) subset_sum(remaining, target, partial + [n]) end end subset_sum([3,9,8,4,5,7,10],15)

Editar: discusión de complejidad

Como otros mencionan, este es un en.wikipedia.org/wiki/Subset_sum_problem . Se puede resolver en tiempo exponencial O (2 ^ n), por ejemplo para n = 10 habrá 1024 soluciones posibles. Si los objetivos que intentas alcanzar están en un rango bajo, entonces este algoritmo funciona. Entonces, por ejemplo:

subset_sum([1,2,3,4,5,6,7,8,9,10],100000) genera 1024 ramas porque el objetivo nunca filtra las posibles soluciones.

Por otro lado subset_sum([1,2,3,4,5,6,7,8,9,10],10) genera solo 175 ramas, porque el objetivo para llegar a 10 consigue filtrar muchas combinaciones.

Si N y Target son números grandes, uno debería pasar a una versión aproximada de la solución.


Esto es similar a un problema de cambio de monedas

public class CoinCount { public static void main(String[] args) { int[] coins={1,4,6,2,3,5}; int count=0; for (int i=0;i<coins.length;i++) { count=count+Count(9,coins,i,0); } System.out.println(count); } public static int Count(int Sum,int[] coins,int index,int curSum) { int count=0; if (index>=coins.length) return 0; int sumNow=curSum+coins[index]; if (sumNow>Sum) return 0; if (sumNow==Sum) return 1; for (int i= index+1;i<coins.length;i++) count+=Count(Sum,coins,i,sumNow); return count; } }


Esto se puede usar para imprimir todas las respuestas también

public void recur(int[] a, int n, int sum, int[] ans, int ind) { if (n < 0 && sum != 0) return; if (n < 0 && sum == 0) { print(ans, ind); return; } if (sum >= a[n]) { ans[ind] = a[n]; recur(a, n - 1, sum - a[n], ans, ind + 1); } recur(a, n - 1, sum, ans, ind); } public void print(int[] a, int n) { for (int i = 0; i < n; i++) System.out.print(a[i] + " "); System.out.println(); }

La complejidad del tiempo es exponencial. Orden de 2 ^ n


Excel VBA versión a continuación. Necesitaba implementar esto en VBA (no es mi preferencia, ¡no me juzguen!), Y usé las respuestas en esta página para el enfoque. Estoy cargando en caso de que otros también necesiten una versión de VBA.

Option Explicit Public Sub SumTarget() Dim numbers(0 To 6) As Long Dim target As Long target = 15 numbers(0) = 3: numbers(1) = 9: numbers(2) = 8: numbers(3) = 4: numbers(4) = 5 numbers(5) = 7: numbers(6) = 10 Call SumUpTarget(numbers, target) End Sub Public Sub SumUpTarget(numbers() As Long, target As Long) Dim part() As Long Call SumUpRecursive(numbers, target, part) End Sub Private Sub SumUpRecursive(numbers() As Long, target As Long, part() As Long) Dim s As Long, i As Long, j As Long, num As Long Dim remaining() As Long, partRec() As Long s = SumArray(part) If s = target Then Debug.Print "SUM ( " & ArrayToString(part) & " ) = " & target If s >= target Then Exit Sub If (Not Not numbers) <> 0 Then For i = 0 To UBound(numbers) Erase remaining() num = numbers(i) For j = i + 1 To UBound(numbers) AddToArray remaining, numbers(j) Next j Erase partRec() CopyArray partRec, part AddToArray partRec, num SumUpRecursive remaining, target, partRec Next i End If End Sub Private Function ArrayToString(x() As Long) As String Dim n As Long, result As String result = "{" & x(n) For n = LBound(x) + 1 To UBound(x) result = result & "," & x(n) Next n result = result & "}" ArrayToString = result End Function Private Function SumArray(x() As Long) As Long Dim n As Long SumArray = 0 If (Not Not x) <> 0 Then For n = LBound(x) To UBound(x) SumArray = SumArray + x(n) Next n End If End Function Private Sub AddToArray(arr() As Long, x As Long) If (Not Not arr) <> 0 Then ReDim Preserve arr(0 To UBound(arr) + 1) Else ReDim Preserve arr(0 To 0) End If arr(UBound(arr)) = x End Sub Private Sub CopyArray(destination() As Long, source() As Long) Dim n As Long If (Not Not source) <> 0 Then For n = 0 To UBound(source) AddToArray destination, source(n) Next n End If End Sub

La salida (escrita en la ventana Inmediato) debe ser:

SUM ( {3,8,4} ) = 15 SUM ( {3,5,7} ) = 15 SUM ( {8,7} ) = 15 SUM ( {5,10} ) = 15


La solución de este problema se ha dado millones de veces en Internet. El problema se llama El problema del cambio de monedas . Se pueden encontrar soluciones en http://rosettacode.org/wiki/Count_the_coins y su modelo matemático en http://jaqm.ro/issues/volume-5,issue-2/pdfs/patterson_harmel.pdf (o cambio de moneda de Google). problema ).

Por cierto, la solución Scala de Tsagadai es interesante. Este ejemplo produce 1 o 0. Como efecto secundario, enumera en la consola todas las soluciones posibles. Muestra la solución, pero no puede usarla de ninguna manera.

Para que sea lo más útil posible, el código debe devolver una List[List[Int]] para permitir obtener el número de solución (la longitud de la lista de listas), la "mejor" solución (la lista más corta), o todas las posibles soluciones.

Aquí hay un ejemplo. Es muy ineficiente, pero es 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") }

Cuando se ejecuta, muestra:

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)

La función sumCombinations() puede usarse sola y el resultado puede analizarse más para mostrar la "mejor" solución (la lista más corta) o la cantidad de soluciones (la cantidad de listas).

Tenga en cuenta que incluso así, los requisitos pueden no estar completamente satisfechos. Puede suceder que el orden de cada lista en la solución sea significativo. En tal caso, cada lista debería duplicarse tantas veces como haya una combinación de sus elementos. O quizás solo nos interesen las combinaciones que son diferentes.

Por ejemplo, podríamos considerar que List(5, 10) debería dar dos combinaciones: List(5, 10) y List(10, 5) . Para List(5, 5, 5) podría dar tres combinaciones o solo una, dependiendo de los requisitos. Para los enteros, las tres permutaciones son equivalentes, pero si estamos tratando con monedas, como en el "problema de cambio de monedas", no lo son.

Tampoco se menciona en los requisitos la cuestión de si cada número (o moneda) puede usarse solo una o varias veces. Podríamos (¡y deberíamos!) Generalizar el problema a una lista de las ocurrencias de cada número. Esto se traduce en la vida real en "¿cuáles son las formas posibles de hacer una cierta cantidad de dinero con un conjunto de monedas (y no un conjunto de valores de monedas)". El problema original es solo un caso particular de este, donde tenemos tantas ocurrencias de cada moneda como sea necesario para hacer la cantidad total con cada valor de moneda individual.


Otra solución de python sería usar el módulo itertools.combinations siguiente manera:

#!/usr/local/bin/python from itertools import combinations def find_sum_in_list(numbers, target): results = [] for x in range(len(numbers)): results.extend( [ combo for combo in combinations(numbers ,x) if sum(combo) == target ] ) print results if __name__ == "__main__": find_sum_in_list([3,9,8,4,5,7,10], 15)

Salida: [(8, 7), (5, 10), (3, 8, 4), (3, 5, 7)]


Para encontrar las combinaciones usando excel - (es bastante fácil). (Tu computadora no debe ser demasiado lenta)

  1. Ir a este sitio
  2. Ir a la página "Suma al objetivo"
  3. Descargue el archivo de Excel "Suma al objetivo".

    Siga las instrucciones en la página del sitio web.

espero que esto ayude.


Pensé que usaría una respuesta de esta pregunta, pero no pude, así que esta es mi respuesta. Está utilizando una versión modificada de una respuesta en Estructura e Interpretación de Programas de Computadora . Creo que esta es una solución recursiva mejor y debería agradar a los puristas más.

Mi respuesta está en Scala (y me disculpo si mi Scala apesta, acabo de empezar a aprenderlo). La locura de findSumCombinations es ordenar y hacer única la lista original de la recursión para evitar a los incautos.

def findSumCombinations(target: Int, numbers: List[Int]): Int = { cc(target, numbers.distinct.sortWith(_ < _), List()) } def cc(target: Int, numbers: List[Int], solution: List[Int]): Int = { if (target == 0) {println(solution); 1 } else if (target < 0 || numbers.length == 0) 0 else cc(target, numbers.tail, solution) + cc(target - numbers.head, numbers, numbers.head :: solution) }

Para usarlo:

> findSumCombinations(12345, List(1,5,22,15,0,..)) * Prints a whole heap of lists that will sum to the target *


Una versión de Javascript:

function subsetSum(numbers, target, partial) { var s, n, remaining; partial = partial || []; // sum partial s = partial.reduce(function (a, b) { return a + b; }, 0); // check if the partial sum is equals to target if (s === target) { console.log("%s=%s", partial.join("+"), target) } if (s >= target) { return; // if we reach the number why bother to continue } for (var i = 0; i < numbers.length; i++) { n = numbers[i]; remaining = numbers.slice(i + 1); subsetSum(remaining, target, partial.concat([n])); } } subsetSum([3,9,8,4,5,7,10],15); // output: // 3+8+4=15 // 3+5+7=15 // 8+7=15 // 5+10=15


Versión C # de la respuesta del código @msalvadores

void Main() { int[] numbers = {3,9,8,4,5,7,10}; int target = 15; sum_up(new List<int>(numbers.ToList()),target); } static void sum_up_recursive(List<int> numbers, int target, List<int> part) { int s = 0; foreach (int x in part) { s += x; } if (s == target) { Console.WriteLine("sum(" + string.Join(",", part.Select(n => n.ToString()).ToArray()) + ")=" + target); } if (s >= target) { return; } for (int i = 0;i < numbers.Count;i++) { var remaining = new List<int>(); int n = numbers[i]; for (int j = i + 1; j < numbers.Count;j++) { remaining.Add(numbers[j]); } var part_rec = new List<int>(part); part_rec.Add(n); sum_up_recursive(remaining,target,part_rec); } } static void sum_up(List<int> numbers, int target) { sum_up_recursive(numbers,target,new List<int>()); }


Versión C ++ del mismo algoritmo

#include <iostream> #include <list> void subset_sum_recursive(std::list<int> numbers, int target, std::list<int> partial) { int s = 0; for (std::list<int>::const_iterator cit = partial.begin(); cit != partial.end(); cit++) { s += *cit; } if(s == target) { std::cout << "sum(["; for (std::list<int>::const_iterator cit = partial.begin(); cit != partial.end(); cit++) { std::cout << *cit << ","; } std::cout << "])=" << target << std::endl; } if(s >= target) return; int n; for (std::list<int>::const_iterator ai = numbers.begin(); ai != numbers.end(); ai++) { n = *ai; std::list<int> remaining; for(std::list<int>::const_iterator aj = ai; aj != numbers.end(); aj++) { if(aj == ai)continue; remaining.push_back(*aj); } std::list<int> partial_rec=partial; partial_rec.push_back(n); subset_sum_recursive(remaining,target,partial_rec); } } void subset_sum(std::list<int> numbers,int target) { subset_sum_recursive(numbers,target,std::list<int>()); } int main() { std::list<int> a; a.push_back (3); a.push_back (9); a.push_back (8); a.push_back (4); a.push_back (5); a.push_back (7); a.push_back (10); int n = 15; //std::cin >> n; subset_sum(a, n); return 0; }


Versión no recursiva de Java que simplemente sigue agregando elementos y redistribuyéndolos entre los posibles valores. 0 son ignorados y funcionan para listas fijas (lo que le dan es con lo que puede jugar) o una lista de números repetibles.

import java.util.*; public class TestCombinations { public static void main(String[] args) { ArrayList<Integer> numbers = new ArrayList<>(Arrays.asList(0, 1, 2, 2, 5, 10, 20)); LinkedHashSet<Integer> targets = new LinkedHashSet<Integer>() {{ add(4); add(10); add(25); }}; System.out.println("## each element can appear as many times as needed"); for (Integer target: targets) { Combinations combinations = new Combinations(numbers, target, true); combinations.calculateCombinations(); for (String solution: combinations.getCombinations()) { System.out.println(solution); } } System.out.println("## each element can appear only once"); for (Integer target: targets) { Combinations combinations = new Combinations(numbers, target, false); combinations.calculateCombinations(); for (String solution: combinations.getCombinations()) { System.out.println(solution); } } } public static class Combinations { private boolean allowRepetitions; private int[] repetitions; private ArrayList<Integer> numbers; private Integer target; private Integer sum; private boolean hasNext; private Set<String> combinations; /** * Constructor. * * @param numbers Numbers that can be used to calculate the sum. * @param target Target value for sum. */ public Combinations(ArrayList<Integer> numbers, Integer target) { this(numbers, target, true); } /** * Constructor. * * @param numbers Numbers that can be used to calculate the sum. * @param target Target value for sum. */ public Combinations(ArrayList<Integer> numbers, Integer target, boolean allowRepetitions) { this.allowRepetitions = allowRepetitions; if (this.allowRepetitions) { Set<Integer> numbersSet = new HashSet<>(numbers); this.numbers = new ArrayList<>(numbersSet); } else { this.numbers = numbers; } this.numbers.removeAll(Arrays.asList(0)); Collections.sort(this.numbers); this.target = target; this.repetitions = new int[this.numbers.size()]; this.combinations = new LinkedHashSet<>(); this.sum = 0; if (this.repetitions.length > 0) this.hasNext = true; else this.hasNext = false; } /** * Calculate and return the sum of the current combination. * * @return The sum. */ private Integer calculateSum() { this.sum = 0; for (int i = 0; i < repetitions.length; ++i) { this.sum += repetitions[i] * numbers.get(i); } return this.sum; } /** * Redistribute picks when only one of each number is allowed in the sum. */ private void redistribute() { for (int i = 1; i < this.repetitions.length; ++i) { if (this.repetitions[i - 1] > 1) { this.repetitions[i - 1] = 0; this.repetitions[i] += 1; } } if (this.repetitions[this.repetitions.length - 1] > 1) this.repetitions[this.repetitions.length - 1] = 0; } /** * Get the sum of the next combination. When 0 is returned, there''s no other combinations to check. * * @return The sum. */ private Integer next() { if (this.hasNext && this.repetitions.length > 0) { this.repetitions[0] += 1; if (!this.allowRepetitions) this.redistribute(); this.calculateSum(); for (int i = 0; i < this.repetitions.length && this.sum != 0; ++i) { if (this.sum > this.target) { this.repetitions[i] = 0; if (i + 1 < this.repetitions.length) { this.repetitions[i + 1] += 1; if (!this.allowRepetitions) this.redistribute(); } this.calculateSum(); } } if (this.sum.compareTo(0) == 0) this.hasNext = false; } return this.sum; } /** * Calculate all combinations whose sum equals target. */ public void calculateCombinations() { while (this.hasNext) { if (this.next().compareTo(target) == 0) this.combinations.add(this.toString()); } } /** * Return all combinations whose sum equals target. * * @return Combinations as a set of strings. */ public Set<String> getCombinations() { return this.combinations; } @Override public String toString() { StringBuilder stringBuilder = new StringBuilder("" + sum + ": "); for (int i = 0; i < repetitions.length; ++i) { for (int j = 0; j < repetitions[i]; ++j) { stringBuilder.append(numbers.get(i) + " "); } } return stringBuilder.toString(); } } }

Muestra de entrada:

numbers: 0, 1, 2, 2, 5, 10, 20 targets: 4, 10, 25

Muestra de salida:

## each element can appear as many times as needed 4: 1 1 1 1 4: 1 1 2 4: 2 2 10: 1 1 1 1 1 1 1 1 1 1 10: 1 1 1 1 1 1 1 1 2 10: 1 1 1 1 1 1 2 2 10: 1 1 1 1 2 2 2 10: 1 1 2 2 2 2 10: 2 2 2 2 2 10: 1 1 1 1 1 5 10: 1 1 1 2 5 10: 1 2 2 5 10: 5 5 10: 10 25: 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 25: 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 25: 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 25: 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 25: 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 25: 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 25: 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 25: 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 25: 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 25: 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 25: 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 25: 1 1 1 2 2 2 2 2 2 2 2 2 2 2 25: 1 2 2 2 2 2 2 2 2 2 2 2 2 25: 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 5 25: 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 5 25: 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 5 25: 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 5 25: 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 5 25: 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 5 25: 1 1 1 1 1 1 1 1 2 2 2 2 2 2 5 25: 1 1 1 1 1 1 2 2 2 2 2 2 2 5 25: 1 1 1 1 2 2 2 2 2 2 2 2 5 25: 1 1 2 2 2 2 2 2 2 2 2 5 25: 2 2 2 2 2 2 2 2 2 2 5 25: 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 5 5 25: 1 1 1 1 1 1 1 1 1 1 1 1 1 2 5 5 25: 1 1 1 1 1 1 1 1 1 1 1 2 2 5 5 25: 1 1 1 1 1 1 1 1 1 2 2 2 5 5 25: 1 1 1 1 1 1 1 2 2 2 2 5 5 25: 1 1 1 1 1 2 2 2 2 2 5 5 25: 1 1 1 2 2 2 2 2 2 5 5 25: 1 2 2 2 2 2 2 2 5 5 25: 1 1 1 1 1 1 1 1 1 1 5 5 5 25: 1 1 1 1 1 1 1 1 2 5 5 5 25: 1 1 1 1 1 1 2 2 5 5 5 25: 1 1 1 1 2 2 2 5 5 5 25: 1 1 2 2 2 2 5 5 5 25: 2 2 2 2 2 5 5 5 25: 1 1 1 1 1 5 5 5 5 25: 1 1 1 2 5 5 5 5 25: 1 2 2 5 5 5 5 25: 5 5 5 5 5 25: 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 10 25: 1 1 1 1 1 1 1 1 1 1 1 1 1 2 10 25: 1 1 1 1 1 1 1 1 1 1 1 2 2 10 25: 1 1 1 1 1 1 1 1 1 2 2 2 10 25: 1 1 1 1 1 1 1 2 2 2 2 10 25: 1 1 1 1 1 2 2 2 2 2 10 25: 1 1 1 2 2 2 2 2 2 10 25: 1 2 2 2 2 2 2 2 10 25: 1 1 1 1 1 1 1 1 1 1 5 10 25: 1 1 1 1 1 1 1 1 2 5 10 25: 1 1 1 1 1 1 2 2 5 10 25: 1 1 1 1 2 2 2 5 10 25: 1 1 2 2 2 2 5 10 25: 2 2 2 2 2 5 10 25: 1 1 1 1 1 5 5 10 25: 1 1 1 2 5 5 10 25: 1 2 2 5 5 10 25: 5 5 5 10 25: 1 1 1 1 1 10 10 25: 1 1 1 2 10 10 25: 1 2 2 10 10 25: 5 10 10 25: 1 1 1 1 1 20 25: 1 1 1 2 20 25: 1 2 2 20 25: 5 20 ## each element can appear only once 4: 2 2 10: 1 2 2 5 10: 10 25: 1 2 2 20 25: 5 20


PHP Version , as inspired by Keith Beller''s C# version.

bala''s PHP version did not work for me, because I did not need to group numbers. I wanted a simpler implementation with one target value, and a pool of numbers. This function will also prune any duplicate entries.

/** * Calculates a subset sum: finds out which combinations of numbers * from the numbers array can be added together to come to the target * number. * * Returns an indexed array with arrays of number combinations. * * Example: * * <pre> * $matches = subset_sum(array(5,10,7,3,20), 25); * </pre> * * Returns: * * <pre> * Array * ( * [0] => Array * ( * [0] => 3 * [1] => 5 * [2] => 7 * [3] => 10 * ) * [1] => Array * ( * [0] => 5 * [1] => 20 * ) * ) * </pre> * * @param number[] $numbers * @param number $target * @param array $part * @return array[number[]] */ function subset_sum($numbers, $target, $part=null) { // we assume that an empty $part variable means this // is the top level call. $toplevel = false; if($part === null) { $toplevel = true; $part = array(); } $s = 0; foreach($part as $x) { $s = $s + $x; } // we have found a match! if($s == $target) { sort($part); // ensure the numbers are always sorted return array(implode(''|'', $part)); } // gone too far, break off if($s >= $target) { return null; } $matches = array(); $totalNumbers = count($numbers); for($i=0; $i < $totalNumbers; $i++) { $remaining = array(); $n = $numbers[$i]; for($j = $i+1; $j < $totalNumbers; $j++) { $remaining[] = $numbers[$j]; } $part_rec = $part; $part_rec[] = $n; $result = subset_sum($remaining, $target, $part_rec); if($result) { $matches = array_merge($matches, $result); } } if(!$toplevel) { return $matches; } // this is the top level function call: we have to // prepare the final result value by stripping any // duplicate results. $matches = array_unique($matches); $result = array(); foreach($matches as $entry) { $result[] = explode(''|'', $entry); } return $result; }


Swift 3 conversión de la solución Java: (por @JeremyThompson)

protocol _IntType { } extension Int: _IntType {} extension Array where Element: _IntType { func subsets(to: Int) -> [[Element]]? { func sum_up_recursive(_ numbers: [Element], _ target: Int, _ partial: [Element], _ solution: inout [[Element]]) { var sum: Int = 0 for x in partial { sum += x as! Int } if sum == target { solution.append(partial) } guard sum < target else { return } for i in stride(from: 0, to: numbers.count, by: 1) { var remaining = [Element]() for j in stride(from: i + 1, to: numbers.count, by: 1) { remaining.append(numbers[j]) } var partial_rec = [Element](partial) partial_rec.append(numbers[i]) sum_up_recursive(remaining, target, partial_rec, &solution) } } var solutions = [[Element]]() sum_up_recursive(self, to, [Element](), &solutions) return solutions.count > 0 ? solutions : nil } }

uso:

let numbers = [3, 9, 8, 4, 5, 7, 10] if let solution = numbers.subsets(to: 15) { print(solution) // output: [[3, 8, 4], [3, 5, 7], [8, 7], [5, 10]] } else { print("not possible") }


@KeithBeller''s answer with slightly changed variable names and some comments.

public static void Main(string[] args) { List<int> input = new List<int>() { 3, 9, 8, 4, 5, 7, 10 }; int targetSum = 15; SumUp(input, targetSum); } public static void SumUp(List<int> input, int targetSum) { SumUpRecursive(input, targetSum, new List<int>()); } private static void SumUpRecursive(List<int> remaining, int targetSum, List<int> listToSum) { // Sum up partial int sum = 0; foreach (int x in listToSum) sum += x; //Check sum matched if (sum == targetSum) Console.WriteLine("sum(" + string.Join(",", listToSum.ToArray()) + ")=" + targetSum); //Check sum passed if (sum >= targetSum) return; //Iterate each input character for (int i = 0; i < remaining.Count; i++) { //Build list of remaining items to iterate List<int> newRemaining = new List<int>(); for (int j = i + 1; j < remaining.Count; j++) newRemaining.Add(remaining[j]); //Update partial list List<int> newListToSum = new List<int>(listToSum); int currentItem = remaining[i]; newListToSum.Add(currentItem); SumUpRecursive(newRemaining, targetSum, newListToSum); } }''


I ported the C# sample to Objective-c and didn''t see it in the responses:

//Usage NSMutableArray* numberList = [[NSMutableArray alloc] init]; NSMutableArray* partial = [[NSMutableArray alloc] init]; int target = 16; for( int i = 1; i<target; i++ ) { [numberList addObject:@(i)]; } [self findSums:numberList target:target part:partial]; //******************************************************************* // Finds combinations of numbers that add up to target recursively //******************************************************************* -(void)findSums:(NSMutableArray*)numbers target:(int)target part:(NSMutableArray*)partial { int s = 0; for (NSNumber* x in partial) { s += [x intValue]; } if (s == target) { NSLog(@"Sum[%@]", partial); } if (s >= target) { return; } for (int i = 0;i < [numbers count];i++ ) { int n = [numbers[i] intValue]; NSMutableArray* remaining = [[NSMutableArray alloc] init]; for (int j = i + 1; j < [numbers count];j++) { [remaining addObject:@([numbers[j] intValue])]; } NSMutableArray* partRec = [[NSMutableArray alloc] initWithArray:partial]; [partRec addObject:@(n)]; [self findSums:remaining target:target part:partRec]; } }


Recomendado como una respuesta:

Aquí hay una solución que usa generators es2015 :

function* subsetSum(numbers, target, partial = [], partialSum = 0) { if(partialSum === target) yield partial if(partialSum >= target) return for(let i = 0; i < numbers.length; i++){ const remaining = numbers.slice(i + 1) , n = numbers[i] yield* subsetSum(remaining, target, [...partial, n], partialSum + n) } }

Usar generadores en realidad puede ser muy útil porque le permite pausar la ejecución del script inmediatamente después de encontrar un subconjunto válido. Esto está en contraste con las soluciones sin generadores (es decir, que carecen de estado) que tienen que iterar a través de cada subconjunto denumbers


Thank you.. ephemient

He convertido la lógica anterior de python a php ..

<?php $data = array(array(2,3,5,10,15),array(4,6,23,15,12),array(23,34,12,1,5)); $maxsum = 25; print_r(bestsum($data,$maxsum)); //function call function bestsum($data,$maxsum) { $res = array_fill(0, $maxsum + 1, ''0''); $res[0] = array(); //base case foreach($data as $group) { $new_res = $res; //copy res foreach($group as $ele) { for($i=0;$i<($maxsum-$ele+1);$i++) { if($res[$i] != 0) { $ele_index = $i+$ele; $new_res[$ele_index] = $res[$i]; $new_res[$ele_index][] = $ele; } } } $res = $new_res; } for($i=$maxsum;$i>0;$i--) { if($res[$i]!=0) { return $res[$i]; break; } } return array(); } ?>