Algoritmo para encontrar qué número en una lista de resumen a un cierto número

votos
20

Tengo una lista de números. También tengo una cierta suma. La suma se hace de algunos números de mi lista (que puede / no puede saber la cantidad de números que está hecho de). ¿Existe un algoritmo rápido para obtener una lista de posibles números? Escrito en Python sería grande, pero de pseudo-código bueno también. (Que todavía no puedo leer nada que no sea Python: P)

Ejemplo

list = [1,2,3,10]
sum = 12
result = [2,10]

NOTA: Yo sé de algoritmo para encontrar los números de una lista de tamaño n suma a otro número . (Pero no puedo leer C # y estoy no puede comprobar si funciona para mis necesidades que estoy en Linux y he intentado usar mono pero me da errores y no puedo encontrar la manera de trabajar de C # :(
y sí sé de algoritmo para resumir una lista de números para todas las combinaciones (pero parece ser bastante ineficiente. no necesito todas las combinaciones .)

Publicado el 06/08/2010 a las 05:09
fuente por usuario
En otros idiomas...                            


4 respuestas

votos
33

Este problema se reduce al 0-1 Problema de la mochila , en la que está tratando de encontrar un conjunto con una suma exacta. La solución depende de las limitaciones, en el caso general este problema es NP-completo.

Sin embargo, si la suma máxima de búsqueda (vamos a llamarlo S) no es demasiado alto, entonces se puede resolver el problema con la programación dinámica. Voy a explicarlo mediante una función recursiva y memoization , que es fácil de entender que un enfoque de abajo hacia arriba.

Vamos a escribir una función de f(v, i, S), de manera que vuelvan el número de subconjuntos en el v[i:]que resume exactamente a S. Para resolverlo de forma recursiva, primero tenemos que analizar la base (es decir: v[i:]está vacío):

  • S == 0: El único subconjunto de []tiene suma 0, por lo que es un subconjunto válido. Debido a esto, la función debería devolver 1.

  • S = 0: Como el único subconjunto de []tiene suma 0, no es un subconjunto válido. Debido a esto, la función debe devolver 0.

A continuación, vamos a analizar el caso recursivo (es decir: v[i:]no está vacío). Hay dos opciones: incluyen el número v[i]en el subconjunto actual, o no lo incluyen. Si incluimos v[i], entonces estamos buscando subconjuntos que tienen suma S - v[i], de lo contrario, todavía estamos buscando subconjuntos con suma S. La función fse puede implementar de la siguiente manera:

def f(v, i, S):
  if i >= len(v): return 1 if S == 0 else 0
  count = f(v, i + 1, S)
  count += f(v, i + 1, S - v[i])
  return count

v = [1, 2, 3, 10]
sum = 12
print(f(v, 0, sum))

Al marcar f(v, 0, S) > 0, se puede saber si hay una solución a su problema. Sin embargo, este código es demasiado lento, cada llamada recursiva genera dos nuevas llamadas, lo que conduce a una O (2 ^ n) algoritmo. Ahora, podemos aplicar memoization a hacer que se ejecute en tiempo O (n * S), que es más rápido si Sno es demasiado grande:

def f(v, i, S, memo):
  if i >= len(v): return 1 if S == 0 else 0
  if (i, S) not in memo:  # <-- Check if value has not been calculated.
    count = f(v, i + 1, S, memo)
    count += f(v, i + 1, S - v[i], memo)
    memo[(i, S)] = count  # <-- Memoize calculated result.
  return memo[(i, S)]     # <-- Return memoized value.

v = [1, 2, 3, 10]
sum = 12
memo = dict()
print(f(v, 0, sum, memo))

Ahora bien, es posible codificar una función gque devuelve un subconjunto que resume S. Para ello, basta con añadir elementos sólo si existe al menos una solución incluirlos:

def f(v, i, S, memo):
  # ... same as before ...

def g(v, S, memo):
  subset = []
  for i, x in enumerate(v):
    # Check if there is still a solution if we include v[i]
    if f(v, i + 1, S - x, memo) > 0:
      subset.append(x)
      S -= x
  return subset

v = [1, 2, 3, 10]
sum = 12
memo = dict()
if f(v, 0, sum, memo) == 0: print("There are no valid subsets.")
else: print(g(v, sum, memo))

Exención de responsabilidad: Esta solución dice que hay dos subconjuntos de [10, 10] que resume 10. Esto es porque se supone que los diez primeros es diferente a la segunda decena. El algoritmo se puede fijar a asumir que ambos decenas son iguales (y por tanto responder uno), pero que es un poco más complicada.

Respondida el 06/08/2010 a las 06:16
fuente por usuario

votos
1

Por lo tanto, la lógica es inversa a ordenar los números, y supongamos que la lista de números está l y la suma de formarse es s .

   for i in b:
            if(a(round(n-i,2),b[b.index(i)+1:])):
                r.append(i)    
                return True
        return False

a continuación, vamos a través de este bucle y un número se selecciona entre l en orden y dejar que dicen que es i . hay 2 casos posibles o bien i es la parte de la suma o no. Por lo tanto, suponemos que i es parte de la solución y entonces el problema se reduce a l bienestar l[l.index(i+1):]y s ser si es así, si nuestra función es un (l, s) entonces llamamos a(l[l.index(i+1):] ,s-i). y si i no es una parte de s entonces tenemos que formar s de la l[l.index(i+1):]lista. Por lo tanto, es similar en los dos casos, único cambio es que si i es parte de s, entonces s = Si y de otro tipo s = s solamente.

ahora para reducir el problema de manera que en el número de casos en l son mayores que s les quitamos a reducir la complejidad hasta l está vacía y en ese caso los números que se seleccionan no son una parte de nuestra solución y nos devuelven false.

if(len(b)==0):
    return False    
while(b[0]>n):
    b.remove(b[0])
    if(len(b)==0):
        return False    

y en el caso l tiene solamente 1 elemento dejó entonces o bien puede ser parte de s entonces volvemos verdadero o no lo es entonces volvemos falsa y bucle a pasar por otro número.

if(b[0]==n):
    r.append(b[0])
    return True
if(len(b)==1):
    return False

en cuenta en el bucle si han utilizado b..but b es nuestra lista only.and i han redondeado siempre que sea posible, por lo que no hay que obtener una respuesta equivocada debido a cálculos de punto flotante en Python.

r=[]
list_of_numbers=[61.12,13.11,100.12,12.32,200,60.00,145.34,14.22,100.21,14.77,214.35,200.32,65.43,0.49,132.13,143.21,156.34,11.32,12.34,15.67,17.89,21.23,14.21,12,122,134]
list_of_numbers=sorted(list_of_numbers)
list_of_numbers.reverse()
sum_to_be_formed=401.54
def a(n,b):
    global r
    if(len(b)==0):
        return False    
    while(b[0]>n):
        b.remove(b[0])
        if(len(b)==0):
            return False    
    if(b[0]==n):
        r.append(b[0])
        return True
    if(len(b)==1):
        return False
    for i in b:
        if(a(round(n-i,2),b[b.index(i)+1:])):
            r.append(i)    
            return True
    return False
if(a(sum_to_be_formed,list_of_numbers)):
    print(r)

esta solución funciona fast.more rápida de una explicado anteriormente. Sin embargo, esto sólo funciona para los números positivos. Sin embargo también funciona bien si hay una solución única de lo contrario se necesita mucho tiempo para salir de bucles.

un ejemplo de ejecución es como esto permite decir

    l=[1,6,7,8,10]

and s=22 i.e. s=1+6+7+8
so it goes through like this 

1.) [10, 8, 7, 6, 1] 22
i.e. 10  is selected to be part of 22..so s=22-10=12 and l=l.remove(10)
2.) [8, 7, 6, 1] 12
i.e. 8  is selected to be part of 12..so s=12-8=4 and l=l.remove(8)
3.) [7, 6, 1] 4  
now 7,6 are removed and 1!=4 so it will return false for this execution where 8 is selected.
4.)[6, 1] 5
i.e. 7  is selected to be part of 12..so s=12-7=5 and l=l.remove(7)
now 6 are removed and 1!=5 so it will return false for this execution where 7 is selected.
5.)[1] 6
i.e. 6  is selected to be part of 12..so s=12-6=6 and l=l.remove(6)
now 1!=6 so it will return false for this execution where 6 is selected.
6.)[] 11
i.e. 1 is selected to be part of 12..so s=12-1=1 and l=l.remove(1)
now l is empty so all the cases for which 10 was a part of s are false and so 10 is not a part of s and we now start with 8 and same cases follow.
7.)[7, 6, 1] 14
8.)[6, 1] 7
9.)[1] 1

sólo para dar una comparación que me encontré en mi equipo que no es tan bueno. utilizando

l=[61.12,13.11,100.12,12.32,200,60.00,145.34,14.22,100.21,14.77,214.35,145.21,123.56,11.90,200.32,65.43,0.49,132.13,143.21,156.34,11.32,12.34,15.67,17.89,21.23,14.21,12,122,134]

y

s = 2000

mi bucle funcionó 1018 veces y 31 ms.

y el bucle de código anterior corrió 3415587 veces y tuvo algún lugar cerca de 16 segundos.

Sin embargo, en caso no existe una solución mi código corrió más de unos minutos, así que se detuvo y corrió código anterior sólo cerca alrededor de 17 ms y el código anterior funciona con números negativos también.

por lo que me algunas mejoras puede hacerse.

Respondida el 24/12/2015 a las 16:29
fuente por usuario

votos
0
#!/usr/bin/python2

ylist = [1, 2, 3, 4, 5, 6, 7, 9, 2, 5, 3, -1]
print ylist 
target = int(raw_input("enter the target number")) 
for i in xrange(len(ylist)):
    sno = target-ylist[i]
    for j in xrange(i+1, len(ylist)):
        if ylist[j] == sno:
            print ylist[i], ylist[j]

Este código Python hace lo que le pide, se imprimirá el único par de números cuya suma es igual a la variable de destino.

si el número de destino es 8, se imprimirá: 
1 7
2 6
3 5
3 5
5 3
6 2
9 -1
5 3
Respondida el 08/02/2017 a las 01:38
fuente por usuario

votos
0

He encontrado una respuesta que tiene complejidad O-tiempo de ejecución (n) y la complejidad espacio sobre O (2n), donde n es la longitud de la lista.

La respuesta satisface las siguientes limitaciones:

  1. Lista puede contener duplicados, por ejemplo, [1,1,1,2,3] y que desea encontrar los pares suman 2

  2. Lista puede contener dos números enteros positivos y negativos

El código es como abajo, y seguido por la explicación:

def countPairs(k, a):
    # List a, sum is k
    temp = dict()
    count = 0
    for iter1 in a:
        temp[iter1] = 0
        temp[k-iter1] = 0
    for iter2 in a:
        temp[iter2] += 1
    for iter3 in list(temp.keys()):
        if iter3 == k / 2 and temp[iter3] > 1:
            count += temp[iter3] * (temp[k-iter3] - 1) / 2
        elif iter3 == k / 2 and temp[iter3] <= 1:
            continue
        else:
            count += temp[iter3] * temp[k-iter3] / 2
    return int(count)
  1. Crear un diccionario vacío, iterar a través de la lista y poner todas las claves posibles en el dict con valor inicial 0. Obsérvese que la clave (K-iter1) es necesario especificar, por ejemplo, si la lista contiene 1, pero no contiene 4 y el suma es 5. entonces cuando miramos a 1, nos gustaría saber cuántas 4 tenemos, pero si no es 4 en el dict, entonces se generará un error.
  2. Iterar a través de la lista de nuevo, y contar el número de veces que se produce cada entero y almacenar los resultados a la dict.
  3. Iterar a través de la dict, esta vez es encontrar cuántos pares tenemos. Debemos tener en cuenta 3 condiciones:

    3.1 La clave es sólo la mitad de la suma y esta clave se produce más de una vez en la lista, por ejemplo, la lista es [1,1,1], suma es 2. Tratamos a esta condición especial como lo hace el código.

    3.2 La clave es sólo la mitad de la suma y esta clave se produce sólo una vez en la lista, nos saltamos esta condición.

    3.3 Para otros casos esa clave no es la mitad de la suma, simplemente multiplique el valor de su valor con el de otra de las claves en las que estos dos teclas suma con el valor dado. Por ejemplo, si la suma es 6, multiplicamos temp [1] y la temperatura [5], temp [2] y la temperatura [4], etc ... (Yo no incluyó los casos en que los números son negativos, pero la idea es la misma. )

El paso más complejo es el paso 3, que consiste en buscar en el diccionario, pero como buscar el diccionario es generalmente rápido, la complejidad casi constante. (Aunque peor caso es O (n), pero no debe suceder para claves enteras.) Por lo tanto, con el supuesto de la búsqueda es la complejidad constante, la complejidad total es O (n), ya que sólo iterar la lista muchas veces por separado.

Consejos para una mejor solución es bienvenida :)

Respondida el 08/10/2017 a las 06:30
fuente por usuario

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more