Алгоритм поиска, число которых в списке сумму до определенного числа

голоса
20

У меня есть список номеров. У меня тоже есть определенная сумма. Сумма производится из нескольких номеров из моего списка (я может / не может знать, сколько чисел это сделано из). Есть ли быстрый алгоритм, чтобы получить список возможных номеров? Написанная в Python было бы здорово, но псевдо-код тоже хорошо. (Я не могу пока ничего другого, чем Python чтения: P)

пример

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

Примечание: Я знаю алгоритм , чтобы найти какие номера из списка размера п суммы на другой номер . (Но я не могу читать C # и я не в состоянии проверить , работает ли он для моих нужд я на Linux , и я попытался с помощью Mono , но я получаю ошибки , и я не могу понять, как работать C # :(
и я не знаю алгоритма суммировать список номеров для всех комбинаций (но это , кажется, довольно неэффективным. Мне не нужны все комбинации .)

Задан 06/08/2010 в 05:09
источник пользователем
На других языках...                            


4 ответов

голоса
33

Эта задача сводится к 0-1 ранцу проблемы , где вы пытаетесь найти множество с точной суммой. Решение зависит от ограничений, в общем случае этой проблема является NP-полной.

Однако, если максимальная сумма поиска (назовем его S) не слишком высока, то вы можете решить эту проблему с помощью динамического программирования. Я объясню это с помощью рекурсивной функции и запоминания , который легче понять , чем подход снизу вверх.

Давайте закодировать функцию f(v, i, S), таким образом, что она возвращает число подмножеств в v[i:]том , что суммирует точно S. Для того, чтобы решить рекурсивно, сначала мы должны проанализировать базу (т.е. v[i:]пусто):

  • S == 0: только подмножество []имеет сумму 0, так что является допустимым подмножеством. Из - за этого, функция должна возвращать 1.

  • S = 0: Как только подмножество []имеет сумму 0, то не является допустимым подмножество. Из - за этого, функция должна возвращать 0.

Тогда давайте рассмотрим рекурсивный случай (т.е. v[i:]не является пустым). Есть два варианта: включить число v[i]в текущем подмножестве, или не включать его. Если мы включим v[i], то мы ищем подмножество , которые имеют сумму S - v[i], в противном случае, мы все еще ищем подмножество с суммой S. Функция fможет быть реализована следующим образом:

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))

Путем проверки f(v, 0, S) > 0, вы можете знать , если есть решение вашей проблемы. Тем не менее, этот код слишком медленно, каждый рекурсивный вызов порождает два новых вызовов, что приводит к O (N 2 ^) алгоритм. Теперь мы можем применить запоминание , чтобы запустить его во время O (N * S), которая быстрее , если Sне слишком велико:

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))

Теперь можно написать функцию , gкоторая возвращает одно подмножество, суммирующий S. Для этого достаточно добавить элементы , только если есть по крайней мере одно решение , в том числе их:

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))

Отказ от ответственности: Это решение говорит, что есть два подмножества [10, 10], который суммирует 10. Это потому, что он предполагает, что первые десять отличается от второй десятки. Алгоритм может быть закреплен предположить, что обе десятки равны (и, таким образом, ответить на один), но это является немного более сложным.

Ответил 06/08/2010 в 06:16
источник пользователем

голоса
1

Таким образом, логика для сортировки в обратном порядке числа, и предположим , что список чисел л и сумма будет сформирована в s .

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

Затем, мы проходим через эту петлю и число выбирается из л в порядке , и пусть говорят , что это я . есть 2 возможных случая либо я это часть суммы или нет. Таким образом, мы предполагаем , что я является частью решения , и тогда задача сводится к л существо l[l.index(i+1):]и ей существо си поэтому, если наша функция а (л, с) , то мы называем a(l[l.index(i+1):] ,s-i). и если я не являюсь частью с , то мы должны сформировать S из l[l.index(i+1):]списка. Так что похоже в обоих случаях, только изменение , если я является частью с, только тогда s = Si и в противном случае S = s.

Теперь, чтобы уменьшить эту проблему таким образом, что в случае чисел в л больше, чем с мы удаляем их, чтобы уменьшить сложность до тех пор, пока л пусто, и в этом случае числа, которые выбраны не являются частью нашего решения, и мы возвращаемся ложным.

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

обратите внимание, в цикле, если использовали b..but б наш список only.and я закругленный везде, где это возможно, так что мы не должны получить неправильный ответ из-за вычисления с плавающей запятой в 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)

это решение работает fast.more быстрее, чем один пояснено выше. Однако это работает только для положительных чисел. Однако и это хорошо работает, если есть решение только в противном случае потребуется много времени, чтобы выйти из петель.

пример запуска, как это позволяет сказать,

    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

просто чтобы дать сравнение, которое я выбежала на моем компьютере, который не так хорошо. с помощью

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]

а также

s = 2000

мой цикл побежал 1018 раз и 31 мс.

и предыдущий цикл кода побежал 3415587 раз и взял где-то около 16 секунд.

Однако в случае, если решение не существует мой код побежал более чем несколько минут, поэтому я остановил его и предыдущий код побежал только около около 17 мсов и предыдущий код работает с отрицательными числами также.

поэтому я, что некоторые улучшения могут быть сделаны.

Ответил 24/12/2015 в 19:29
источник пользователем

голоса
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]

Этот код питона делать то, что вы просили, он будет печатать уникальную пару чисел, сумма которых равна целевой переменной.

если цель номер 8, он будет печатать: 
1 7
2 6
3 5
3 5
5 3
6 2
9 -1
5 3
Ответил 08/02/2017 в 04:38
источник пользователем

голоса
0

Я нашел ответ, который имеет время выполнения сложность O (п) и пространство сложности около O (2n), где п есть длина списка.

Ответ удовлетворяет следующие условия:

  1. Список может содержать дубликаты, например, [1,1,1,2,3], и вы хотите, чтобы найти пары просуммировать по 2

  2. Список может содержать как положительные, так и отрицательные числа

Код, как показано ниже, и затем объяснения:

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. Создание пустого словаря, итерацию по списку и положить все возможные ключи в Словаре с начальным значением 0. Заметим, что ключ (к-Iter1) необходимо указать, например, если список содержит 1, но не содержит 4, а сумма равна 5. Тогда, когда мы смотрим на 1, мы хотели бы найти, сколько 4 мы имеем, но если 4 не в Словаре, то это вызовет ошибку.
  2. Перебрать список снова, и подсчитать, сколько раз, что происходит, каждое целое и сохранять результаты в Словаре.
  3. Перебрать через Словарь, это время, чтобы найти, сколько пара у нас есть. Нам необходимо рассмотреть 3 условия:

    3.1 Ключ только половина суммы, и этот ключ встречается более чем один раз в списке, например, список [1,1,1], сумма 2. Мы рассматриваем это особое состояние, как то, что делает код.

    3.2 Ключ только половина суммы, и этот ключ происходит только один раз в списке, мы пропускаем это условие.

    3.3 Для других случаев, что ключ не половина суммы, просто умножить его значение со значением другого ключа, где эти две клавиши подводить к заданному значению. Например, если сумма равна 6, мы умножаем темп [1] и температуры [5], температура [2] и температуры [4], и т.д. ... (Я не перечислить случаи, когда числа отрицательны, но идея та же. )

Самый сложный этап является этапом 3, который включает в себя поиск в словаре, но как поиск в словаре, как правило, быстро, почти постоянная сложность. (Хотя худшем случае O (п), но не должен быть для целочисленных ключей.) Таким образом, предполагая, что поиск является постоянной сложностью, общая сложность O (п), как мы только просматривать список много раз отдельно.

Советы для лучшего решения приветствуются :)

Ответил 08/10/2017 в 09:30
источник пользователем

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