Чтобы получить общую сумму (не составляющие ее элементы), этот алгоритм очень быстро даст ответ, когда ваши числа выдают избыточные суммы:
def commonSum(A,B):
commonSet = set(A).intersection(B) # common values are common sums
if commonSet: return min(commonSet)
maxSumA = sum([a for a in A if a>0] or [max(A)])
maxSumB = sum([b for b in B if b>0] or [max(B)])
minSumA = sum([a for a in A if a<0] or [min(A)])
minSumB = sum([b for b in B if b<0] or [min(B)])
if maxSumA < minSumB: return None # no possible common sums
if maxSumB < minSumA: return None
sumsA,sumsB = set(),set() # sets of cumulative sums
diffA,diffB = set([sum(A)]),set([sum(B)]) # sets of cumulative differences from total
iterA,iterB = iter(sorted(A,key=abs)),iter(sorted(B,key=abs))
valueA,valueB = next(iterA,None), next(iterB,None)
while valueA is not None or valueB is not None: # traverse the two lists in parallel until a sum if found or both are exhausted
if valueA is not None:
newSums = [valueA+s for s in sumsA \
if valueA+s <= maxSumB and valueA+s not in sumsA] + [valueA]
sumsA.update(newSums) # new sums formed by combining element with all current ones
newDiffs = [d-valueA for d in diffA \
if d-valueA >= minSumB and d-valueA not in diffA]
diffA.update(newDiffs) # new differences formed by combining current element with existing ones
valueA = next(iterA,None)
if valueB is not None:
newSums = [valueB+s for s in sumsB \
if valueB+s <= maxSumA and valueB+s not in sumsB] + [valueB]
sumsB.update(newSums) # new sums formed by combining element with all current ones
newDiffs = [d-valueB for d in diffB \
if d-valueB >= minSumA and d-valueB not in diffB]
diffB.update(newDiffs) # new differences formed by combining current element with existing ones
valueB = next(iterB,None)
commonSet = (diffA & diffB) | (sumsA & sumsB) # detect common sums
if commonSet: return min(commonSet)
Результаты с использованием приведенного примера и случайныенаборы значений всегда хороши.
listA = [455, 698, 756, 3.56, -9]
listB = [526,55,943,156,531,304,618,911,598,498,268,926,899,898,131,966,303,936,509,67,976,639,74,935,23,226,422,280,64,975,583,596,583]
commonSum(listA,listB) # 446, 1.1 millisecond
listA = [ random.randint(-1000,1000)*2-1 for _ in range(50) ] # only odd numbers
listB = [ random.randint(-500,1500)*2 for _ in range(50) ] # only even numbers
commonSum(listA,listB) # 0.1 to 2 milliseconds
Функция оптимизирована для быстрого исключения исключительных списков, которые не могут иметь общую сумму, и для нахождения общей суммы как можно быстрее, если она есть.Это действительно тратит некоторое время на повторные проверки, если нет общих сумм (но это может быть дополнительно оптимизировано при необходимости)
Способ, которым это работает, заключается в постепенном построении наборов возможных сумм без дубликатов (в sumsA,sumsB
),Это минимизирует размер списков для сравнения.Это позволяет более прямо определять общие суммы (установить пересечение sumsA & sumsB
).Списки обрабатываются в порядке возрастания величины значения (абсолютного значения), так что меньшие дельты учитываются на ранних этапах и максимизируют шансы на достижение заданной суммы.Параллельно с кумулятивными суммами алгоритм проверяет кумулятивные различия, используя два других набора (diffA,diffB
).Это сумма всех элементов минус элементы, обработанные до сих пор.Пересечение этих двух наборов (diffA & diffB
) также способствует быстрому нахождению общей суммы.
Функция хорошо работает, когда значения относительно близки друг к другу (т. Е. Небольшая дисперсия), но существуют сценарии наихудшего случаяэто значительно замедлит его.Как правило, он подходит для приложений, в которых область значений соответствует нормальному распределению.
Чтобы получить действительные комбинации чисел, можно использовать функцию commonSum (), чтобы уменьшить проблему до поиска комбинации элементов, которыепроизводит известную сумму в списке (также известную как Subset sum).Поскольку мы знаем, что в списке есть решение для анализа, подмножество обычно находится очень быстро.
Вот функция, которая находит элементы списка, которые складываются в известную сумму:
from itertools import combinations,islice
def findSum(S,A,sumA=None,maxSumA=None,minSumA=None,lenA=None,pairs=None):
if sumA is None: # sort and prepare accumulators (once)
A,lenA = sorted(A),len(A)
pairs = dict()
for a,b in combinations(reversed(A),2): # a+b pairs with lowest a where a >= b
if a+b == S: return [b,a]
pairs[a+b] = [a,b]
sumA = sum(A)
maxSumA = sum([a for a in A if a>0] or [max(A)])
minSumA = sum([a for a in A if a<0] or [min(A)])
# exit conditions
if lenA == 0: return None # no more elements to sum
if S < minSumA or S > maxSumA: return None # sum unreachable
if sumA == S: return A[:lenA] # all values form the sum
if S in islice(A,lenA): return [S] # sum is one of the values (avoids creating temp list)
if lenA == 1: return None # if only one, it must be the one
if S in pairs \
and all(p<=a for p,a in zip(pairs[S],A[lenA-2:])): # quick pairs
return pairs[S]
if lenA == 2 : return None # if only two, pairs would catch it
for a in islice(A,lenA):
if S-a not in pairs: continue # quick triples
triple = sorted([a]+pairs[S-a])
if all(triple.count(n)==A[:lenA].count(n) for n in triple):
return triple
if lenA == 3: return None
# isolate maximum value, update accumulators
maxA = A[lenA-1]
sumA -= maxA
maxSumA -= maxA
minSumA -= min(0,maxA)
if maxSumA <= 0: maxSumA = maxA
# include max in result and recurse
usingMax = findSum(S-maxA,A,sumA,maxSumA,minSumA,lenA-1,pairs)
if usingMax : return usingMax + [maxA]
# exclude max from result and recurse
return findSum(S,A,sumA,maxSumA,minSumA,lenA-1,pairs)
Вы можете использовать эту функцию в исходных списках с результатом commonSum ():
s = commonSum(listA,listB) # 446
r1 = findSum(s,listA) # [-9, 455]
r2 = findSum(s,listB) # [23, 55, 64, 304]
Эта функция findSum () отвечает за миллисекунду в этих простых случаях, но она также может иметь некоторые худшиеслучаи, которые занимают значительно больше времени.
Вы можете объединить две функции в одну:
def commonSumItems(A,B):
s = commonSum(A,B)
if s is None: return None
sA = findSum(s,A)
sB = findSum(s,B)
return (s,sA,sB)
commonSumItems(listA,listB)
# (446, [-9, 455], [23, 55, 64, 304])
Два алгоритма все еще имеют много места для дальнейшей оптимизации, но я считаю, что они достаточно быстры для большинстваварианты использования.
Потенциальные оптимизации:
- В commonSum () прекратите проверять уменьшающиеся суммы (diffA, diffB), когда обрабатывается половина элементов в списке.
- В commonSum () вычислите наименьшую разницу с ближайшим соседом для элементов в каждом списке и выполните сортировку, используя ее в качестве ключа
- В commonSumItems () получитеподмножества listA и listB обработаны до сих пор и используют их вместо целых списков при вызове findSum ()
EDIT
Я пытался разгадать монстрасписок, который я нашел в другом сообщении, но findSum () делал это недостаточно быстро.Поэтому я создал функцию для упрощения задачи, исключив комбинации элементов, которые не могли бы вывести по модулю целевую сумму.Это действительно помогло и позволило мне решить этот список монстров:
Вот эта функция (после упрощения вызывает findSum):
from collections import defaultdict
def findSum2(S,A,modSize=None):
eligibleA = A.copy()
for modSize in range(2,len(A)*2): # modulo sum sizes
sumD = defaultdict(set) # eligible number per modulo of sum
for a in eligibleA:
newD = [(a%modSize,[a])] # new modulo sum candidates
for d,ad in sumD.items():
m = (a+d) % modSize
newD.append((m,ad | set([a])))
for m,ad in newD: # merge new candidates to target modulos
sumD[m].update(ad)
targetModList = sumD[S % modSize] # keep only eligibles
eligibleA = [ a for a in eligibleA if a in targetModList ]
return findSum(S,eligibleA) # call original algorithm with "cleaned" list
Вот список "монстров", который я пытался решитьwith findSum:
targetB = 262988806539946324131984661067039976436265064677212251086885351040000
B = [116883914017753921836437627140906656193895584300983222705282378240000,
65747201634986581032996165266759994109066266169303062771721337760000,
42078209046391411861117545770726396229802410348353960173901656166400,
29220978504438480459109406785226664048473896075245805676320594560000,
21468474003260924418937523352411426647858372626711204170357987840000,
16436800408746645258249041316689998527266566542325765692930334440000,
12987101557528213537381958571211850688210620477887024745031375360000,
10519552261597852965279386442681599057450602587088490043475414041600,
8693844844295746252297013588993057072273225278585528961549928960000,
7305244626109620114777351696306666012118474018811451419080148640000,
6224587137040149683597270084426981690799173128454727836375984640000,
5367118500815231104734380838102856661964593156677801042589496960000,
4675356560710156873457505085636266247755823372039328908211295129600,
4109200102186661314562260329172499631816641635581441423232583610000,
3639983481521748430892521260443459881470796742937193786669693440000,
3246775389382053384345489642802962672052655119471756186257843840000,
2914003396564502206448583502127866774917064428556368433095682560000,
2629888065399463241319846610670399764362650646772122510868853510400,
2385386000362324935437502594712380738650930291856800463373109760000,
2173461211073936563074253397248264268068306319646382240387482240000,
1988573206351200938616141104476672789688204647842814753019927040000,
1826311156527405028694337924076666503029618504702862854770037160000,
1683128361855656474444701830829055849192096413934158406956066246656,
1556146784260037420899317521106745422699793282113681959093996160000,
1443011284169801504153550952356872298690068941987447193892375040000,
1341779625203807776183595209525714165491148289169450260647374240000,
1250838556670374906691960338012080744048823137584838292922165760000,
1168839140177539218364376271409066561938955843009832227052823782400,
1094646437211014876720019400903392201607763016346356924399106560000,
1027300025546665328640565082293124907954160408895360355808145902500,
965982760477305139144112620999228563585913919842836551283325440000,
909995870380437107723130315110864970367699185734298446667423360000,
858738960130436976757500934096457065914334905068448166814319513600,
811693847345513346086372410700740668013163779867939046564460960000,
768411414287644482489363509326632509674989232073666182868912640000,
728500849141125551612145875531966693729266107139092108273920640000,
691620793004461075955252231602997965644352569828303092930664960000,
657472016349865810329961652667599941090662661693030627717213377600,
625791330255672395317036671188673352614551016483550865168079360000,
596346500090581233859375648678095184662732572964200115843277440000,
568931977371436071675467087219123799753953628290345594563299840000,
543365302768484140768563349312066067017076579911595560096870560000,
519484062301128541495278342848474027528424819115480989801255014400,
497143301587800234654035276119168197422051161960703688254981760000,
476213321032044045508347054897310957784092466595223632570186240000,
456577789131851257173584481019166625757404626175715713692509290000,
438132122515529069774235170457376054037925971973698044293020160000,
420782090463914118611175457707263962298024103483539601739016561664,
404442609057972047876946806715939986830088526993021531852188160000,
389036696065009355224829380276686355674948320528420489773499040000,
374494562534633427030238036407319297168052779889230688624970240000,
360752821042450376038387738089218074672517235496861798473093760000,
347753793771829850091880543559722282890929011143421158461997158400,
335444906300951944045898802381428541372787072292362565161843560000,
323778155173833578494287055791985197213007158728485381455075840000,
312709639167593726672990084503020186012205784396209573230541440000,
302199145693704480473409550206308504954053507241841138853071360000,
292209785044384804591094067852266640484738960752458056763205945600,
282707666261699891568916593460940582033071824431295083135592960000,
273661609302753719180004850225848050401940754086589231099776640000,
265042888929147215048611399412486748738992254650755607041456640000,
256825006386666332160141270573281226988540102223840088952036475625,
248983485481605987343890803377079267631966925138189113455039385600,
241495690119326284786028155249807140896478479960709137820831360000,
234340660761814501342824380545368657996226388663143017230461440000,
227498967595109276930782578777716242591924796433574611666855840000,
220952578483466770957349011608519198854244960871423861446658560000,
214684740032609244189375233524114266478583726267112041703579878400,
208679870295533683104133831435857945991878646837700655494453760000,
202923461836378336521593102675185167003290944966984761641115240000,
197401994025105141026072179446079922264038329650750423033879040000,
192102853571911120622340877331658127418747308018416545717228160000,
187014262428406274938300203425450649910232934881573156328451805184,
182125212285281387903036468882991673432316526784773027068480160000,
177425404985627474536673746714144021883127046501745489011223040000,
172905198251115268988813057900749491411088142457075773232666240000,
168555556186474170249629649778586749838977769381324948621621760000,
164368004087466452582490413166899985272665665423257656929303344400]
Он находит решение менее чем за секунду:
findSum2(targetB,B) # 0.3 second
[202923461836378336521593102675185167003290944966984761641115240000,
292209785044384804591094067852266640484738960752458056763205945600,
335444906300951944045898802381428541372787072292362565161843560000,
519484062301128541495278342848474027528424819115480989801255014400,
657472016349865810329961652667599941090662661693030627717213377600,
811693847345513346086372410700740668013163779867939046564460960000,
858738960130436976757500934096457065914334905068448166814319513600,
1168839140177539218364376271409066561938955843009832227052823782400,
1826311156527405028694337924076666503029618504702862854770037160000,
2385386000362324935437502594712380738650930291856800463373109760000,
29220978504438480459109406785226664048473896075245805676320594560000,
42078209046391411861117545770726396229802410348353960173901656166400,
65747201634986581032996165266759994109066266169303062771721337760000,
116883914017753921836437627140906656193895584300983222705282378240000]
EDIT2
Для некоторых данных полезно попробовать подмножества элементов списка, сгруппированных по общему gcd (наибольшему общему знаменателю), который они имеют с целевой суммой.Идея заключается в том, что элементы в списке, кратные некоторому значению K, всегда будут объединяться, чтобы сформировать кратное число K. Если целью является кратное K, все остальные элементы в списке (которые не являются кратными K) будет только мешать.Это позволяет реализовать стратегию, в которой мы сначала рассматриваем более простое решение, использующее меньше элементов и только два варианта, прежде чем переходить к большему числу комбинаций.Вот почему функция сначала пробует самые большие делители.
Мне удалось найти решение для вашего последнего списка, используя этот подход (в сочетании с предыдущей логикой): примечание: это заменяет findSum2 ()
from math import gcd
from itertools import combinations
def gcdSubSum(S,A):
divs = set( gcd(a,S) for a in A if isinstance(a,int)).difference([1])
prevDivs = set()
while prevDivs != divs:
prevDivs = divs
for a,b in combinations(divs,2):
g = gcd(a,b)
if g == 1 : continue
divs.discard(a)
divs.discard(b)
divs.add(g)
divs = sorted(divs,reverse=True)
combos = sorted(combinations(divs,2),key=lambda a:-min(a))
for divCombo in combos:
subA = [ a for a in A if any(a%d==0 for d in divCombo) ]
result = findSum(S,subA)
if result is not None:return result
return findSum(S,A)
C = [11000,11000,11000,1500,58272,76000,260669,2881,-3472,1591460,633959,
1298377,897946,1912536,35166,46888,46888,65190,16000,80000,-9175476,
51950,-51950,428546,1693196,-18378,-9800,-18820,-3087,3087,30000,
18378,18820,9800]
targetC = 290670
gcdSubSum(targetC,C) # 0.9 second
# [-51950, -9800, 11000, 11000, 11000, 16000, 18378, 18820, 51950, 58272, 76000, 80000]
Этот новый подход даже смог решить некоторые намеренно сложные контрольные примеры, которые я создал.
# Needs to combine 530 elements out of 1280 to reach the target:
targetD = 123456
D = [2**i for i in range(10)]*128
gcdSubSum(targetD,D) # 0.1 second
# must use several negatives to fill gap between a single
# larger positive number an insufficient other positive
# (52 out of 123).
targetE = 123456
E = [2**i for i in range(6,16)] + [1-2**i for i in range(1,15)]*8 + [2**18]
gcdSubSum(targetE,E) # 10 seconds !!!
# [ -16383, -16383, -16383, -16383, -16383, -16383, -16383, -16383,
# -4095, -4095, -4095, -4095, -4095, -4095, -4095, -4095, -1023,
# -1023, -1023, -1023, -1023, -1023, -1023, -1023, -255, -255,
# -255, -255, -255, -255, -255, -255, -63, -63, -63, -63, -63,
# -63, -63, -15, -3, -3, -3, -3, -3, -3, -3, -3,
# 1024, 2048, 32768, 262144]
EDIT3 итерации по суммам ...
Поиск всех наборов подсумок не будет работать с этими алгоритмами, поскольку они оптимизированы для поиска ОДНОГО решения.Чтобы сгенерировать все подмножества, мне пришлось по-другому подойти к проблеме.Эта новая функция является генератором и иногда дает первое решение быстрее, чем gcdSubSum ().У него есть ограничение: он работает только с целочисленными значениями.Но вы можете обойти эту проблему, умножив все элементы и сумму на большое число, чтобы сделать их целыми, и разделите результат после этого:
def iSubSum(S,A,maxSize=None,sumA=None, oddCount=None):
if maxSize is None: maxSize = len(A)
if maxSize==0 or not A : return
seen = set()
def newResult(result): # ensure distinct results
result = sorted(result)
return None if tuple(result) in seen else result
def addResult(result):
if result: seen.add(tuple(result))
if sumA is None: sumA = sum(A)
if oddCount is None: oddCount = sum(a&1 for a in A)
if len(A) > maxSize:
sA = sorted(A)
maxSumA = sum([a for a in sA[-maxSize:] if a>0] or [max(A)])
minSumA = sum([a for a in sA[:maxSize] if a<0] or [min(A)])
else:
maxSumA = sum([a for a in A if a>0] or [max(A)])
minSumA = sum([a for a in A if a<0] or [min(A)])
if S < minSumA or S > maxSumA: return # sum unreachable
if sumA == S and len(A)<=maxSize :
yield A # all values form the sum
addResult(sorted(A))
elif S in A :
yield [S] # one of the items is the sum
addResult([S])
if len(A) == 1 or maxSize==1: return # if only one, we're done
if S&1 and not oddCount: return # need at least one odd for odd sum
# remove elements that are beyond target range
mA = [a for a in A if (minSumA<=a or minSumA+a<=S) and (a>=0 or maxSumA+a>=S)]
if len(mA) < len(A):
for result in iSubSum(S,mA,maxSize): yield result
return
# even target: process even elements (divide problem by 2)
if S&1 == 0:
evens = [a//2 for a in A if a&1 == 0]
for result in iSubSum(S//2,evens,maxSize):
result = newResult([r*2 for r in result])
if result: yield result
addResult(result)
if oddCount < 2 : return # need 2+ odd elements for even sum
#process odd elements (recursing until only evens remains)
subA = A.copy()
for index,item in enumerate(reversed(A),1-len(A)):
if not item&1: continue
del subA[-index]
oddCount -= 1
sumA -= item
for result in iSubSum(S-item,subA,maxSize-1,sumA,oddCount):
result = newResult(result + [item])
if result: yield result
addResult(result)
Чтобы получить первое решение, вызовите iSubSum в пределах next ()функция:
next(iSubSum(targetA,A),None) # 1.869 sec. (SLOWER than gcdSubSum 0.042)
next(iSubSum(targetB,B),None) # too long (SLOWER than gcdSubSum 0.252)
next(iSubSum(targetC,C),None) # 0.003 sec. (FASTER than gcdSumSum 0.936)
next(iSubSum(targetD,D),None) # 0.006 sec. (FASTER than gcdSubSum 0.113)
next(iSubSum(targetE,E),None) # 0.003 sec. (FASTER than gcdSubSum 10.34)
К сожалению, iSubSum () не работает хорошо в списке монстров (targetB / B), поэтому он все еще может использовать больше работы.
Чтобы получить все решения, вы можетеиспользуйте цикл for или создайте список:
for solution in iSubSum(446,listB):
print(solution)
[64, 156, 226]
[23, 55, 64, 304]
solutions = list(iSubSum(targetC,C)) # 1.8 seconds
print(solutions)
[-9800, 11000, 11000, 11000, 16000, 18378, 18820, 58272, 76000, 80000]
[-51950, -9800, 11000, 11000, 11000, 16000, 18378, 18820, 51950, 58272, 76000, 80000]
[-51950, -9800, -3472, 1500, 11000, 11000, 16000, 18378, 18820, 35166, 46888, 51950, 65190, 80000]
[-51950, -18820, -9800, -3472, 1500, 9800, 11000, 11000, 11000, 18378, 18820, 30000, 35166, 46888, 46888, 58272, 76000]
[-51950, -18820, -3472, 1500, 11000, 11000, 11000, 18378, 18820, 30000, 35166, 46888, 46888, 58272, 76000]
[-51950, -9800, -3472, 1500, 9800, 11000, 11000, 11000, 18378, 30000, 35166, 46888, 46888, 58272, 76000]
[-51950, -3472, 1500, 11000, 11000, 11000, 18378, 30000, 35166, 46888, 46888, 58272, 76000]
[-9800, -3472, 1500, 11000, 11000, 16000, 18378, 18820, 35166, 46888, 65190, 80000]
[-18820, -18378, -9800, -3472, 11000, 11000, 16000, 30000, 51950, 65190, 76000, 80000]
[-9800, -3087, 3087, 11000, 11000, 11000, 16000, 18378, 18820, 58272, 76000, 80000]
[-51950, -9800, -3087, 3087, 11000, 11000, 11000, 16000, 18378, 18820, 51950, 58272, 76000, 80000]
[-51950, -9800, -3472, -3087, 1500, 3087, 11000, 11000, 16000, 18378, 18820, 35166, 46888, 51950, 65190, 80000]
[-51950, -18820, -9800, -3472, -3087, 1500, 3087, 9800, 11000, 11000, 11000, 18378, 18820, 30000, 35166, 46888, 46888, 58272, 76000]
[-51950, -18820, -3472, -3087, 1500, 3087, 11000, 11000, 11000, 18378, 18820, 30000, 35166, 46888, 46888, 58272, 76000]
[-51950, -9800, -3472, -3087, 1500, 3087, 9800, 11000, 11000, 11000, 18378, 30000, 35166, 46888, 46888, 58272, 76000]
[-51950, -3472, -3087, 1500, 3087, 11000, 11000, 11000, 18378, 30000, 35166, 46888, 46888, 58272, 76000]
[-9800, -3472, -3087, 1500, 3087, 11000, 11000, 16000, 18378, 18820, 35166, 46888, 65190, 80000]
[-18820, -18378, -9800, -3472, -3087, 3087, 11000, 11000, 16000, 30000, 51950, 65190, 76000, 80000]
[-51950, -18820, -18378, -9800, -3087, 1500, 2881, 3087, 11000, 18378, 30000, 65190, 260669]
[-51950, -18820, -9800, -3087, 1500, 2881, 3087, 11000, 30000, 65190, 260669]
[-9800, -3472, 2881, 3087, 11000, 18378, 18820, 46888, 46888, 76000, 80000]
[-51950, -18378, -9800, 1500, 2881, 3087, 11000, 11000, 18378, 18820, 30000, 35166, 46888, 46888, 65190, 80000]
[-18820, -18378, -3472, 2881, 3087, 9800, 11000, 11000, 18378, 35166, 46888, 51950, 65190, 76000]
[-51950, -9800, -3472, 2881, 3087, 11000, 18378, 18820, 46888, 46888, 51950, 76000, 80000]
[-51950, -18820, -18378, -9800, 1500, 2881, 3087, 9800, 16000, 30000, 46888, 58272, 65190, 76000, 80000]
[-51950, -18820, -18378, 1500, 2881, 3087, 16000, 30000, 46888, 58272, 65190, 76000, 80000]
[-51950, -9800, 1500, 2881, 3087, 11000, 11000, 18820, 30000, 35166, 46888, 46888, 65190, 80000]
[-18820, -3472, 2881, 3087, 9800, 11000, 11000, 35166, 46888, 51950, 65190, 76000]
[-18378, -9800, -3472, -3087, 1500, 2881, 9800, 18378, 18820, 30000, 46888, 51950, 65190, 80000]
[-18378, -3472, -3087, 1500, 2881, 18378, 18820, 30000, 46888, 51950, 65190, 80000]
[-18378, -9800, -3472, -3087, 2881, 9800, 11000, 46888, 46888, 51950, 76000, 80000]
[-18378, -3472, -3087, 2881, 11000, 46888, 46888, 51950, 76000, 80000]
[-18820, -18378, -9800, -3472, -3087, 2881, 9800, 11000, 18820, 46888, 46888, 51950, 76000, 80000]
[-18820, -18378, -3472, -3087, 2881, 11000, 18820, 46888, 46888, 51950, 76000, 80000]
[-9800, -3472, -3087, 1500, 2881, 9800, 18820, 30000, 46888, 51950, 65190, 80000]
[-3472, -3087, 1500, 2881, 18820, 30000, 46888, 51950, 65190, 80000]
[-51950, -18820, -18378, -9800, 1500, 2881, 11000, 18378, 30000, 65190, 260669]
[-51950, -18820, -9800, 1500, 2881, 11000, 30000, 65190, 260669]
EDIT4 дубликаты и индексы
Чтобы получить индексы значений, я бы предложил работать в обратном направлении отразличные решения, разработанные iSubSum.Вот функция, которая найдет различные комбинации индексов для данного решения:
from itertools import product
from collections import defaultdict
def comboIndexes(A,R): # return indexes conbinations in A matching values in R
idx = defaultdict(list)
for i,a in enumerate(A): idx[a].append(i)
return set(tuple(sorted(combo)) for combo in product(*[idx[r] for r in R]) if len(combo)==len(set(combo)))
Вы можете использовать ее на выходе iSubSum, чтобы не добавлялась сложность обработки к функции iSubSum:
Например:
listC = [10,10,40,40,40,50]
for solution in iSubSum(100,listC):
for indexes in comboIndexes(listC,solution):
print(indexes,solution)
(0, 4, 5) [10, 40, 50]
(0, 3, 5) [10, 40, 50]
(1, 4, 5) [10, 40, 50]
(1, 3, 5) [10, 40, 50]
(0, 2, 5) [10, 40, 50]
(1, 2, 5) [10, 40, 50]
(0, 1, 2, 4) [10, 10, 40, 40]
(0, 1, 3, 4) [10, 10, 40, 40]
(0, 1, 2, 3) [10, 10, 40, 40]
EDIT5 с использованием iSubSum для вычисления общей суммы между двумя списками
В большинстве ситуаций было бы возможно использовать iSubSum в качестве механизмаполучить общие суммы между двумя списками (оригинальный вопрос).Подход состоит в том, чтобы сделать один список из двух списков, но инвертировать один из двух списков.Если есть общие суммы, то объединенный список должен быть в состоянии сформировать сумму нуля.
Вот пример:
def iCommonSum(A,B):
if any( -a in B for a in A):
return None # value conflict (algorithm TBD)
for solution in iSubSum(0,[-a for a in A]+B):
sA = [-a for a in solution if -a in A]
sB = [b for b in solution if b in B]
yield sA,sB
listA = [455, 698, 756, 3,56, -9]
listB = [526,55,943,156,531,304,618,911,598,498,268,926,899,898,131,966,303,936,509,67,976,639,74,935,23,226,422,280,64,975,583,596,583]
for sA,sB in iCommonSum(listA,listB):
print(sum(sA),sA,sB)
1454 [756, 698] [226, 280, 422, 526]
1454 [756, 698] [74, 156, 304, 422, 498]
1510 [756, 698, 56] [64, 74, 156, 268, 422, 526]
1510 [756, 698, 56] [64, 422, 498, 526]
1454 [756, 698] [156, 280, 422, 596]
754 [698, 56] [64, 268, 422]
1510 [756, 698, 56] [64, 74, 226, 268, 280, 598]
...
Единственное ограничение состоит в том, что не должно быть элементаодин список, который является обратным значением элемента в другом списке.Обратите внимание, что эта проблема существует, только если списки допускают отрицательные числа.
Создание версии iSubSum, которая возвращает индексы вместо значений, позволит устранить неоднозначность этой ситуации (продолжение следует в новом ответе).