簡體   English   中英

如何從兩個數組中選擇元素,使其總和最小?

[英]How do I pick elements from two arrays such that their sum is minimum?

我有兩個相等長度的數組填充整數(可能是正數或負數但從不為0)。 在每個索引處,我可以選擇array1的元素或者從array2中選擇元素,並且這些元素之和的absoulute值應該是最小的。

例如:

a1 = [2, 2, 1]
a2 = [-3, -3, -4]

正確的答案是選擇這樣的:

At index 0 : -3 from a2
At index 1 : 2 from a1
At index 2 : 1 from a1

因此,最終總和將為0。

首先,簡化問題:

  • 創建數組b ,其中b[i] = a1[i] - a2[i]
  • sumA1 = a1中每個元素的總和。

然后問題變成:

b找到一個子數組,標記為c ,將其總和標記為sumC ,它應該最接近sumA1

或者,您也可以說它應該具有最小的Math.abs(sumC - sumA1)

順便說一句,如果c為空,它也是有效的,這意味着從a1選擇所有索引。

然后這個問題類似於這個: 給定一個輸入數組找到給定總和K的所有子數組

或者,參考這篇文章:

並且,回到OP的問題:

  • b中選取的任何指數均為a2
  • 未在b中選取的任何指數均為a1

這是一個動態編程解決方案,它找到pos + abs(neg + pos)的最小值(根據OP的更新)並打印一個候選解決方案。 我們需要將總和和正整數之和保存為dp狀態以找到最小值。 我不確定如果沒有pos維度我們是否可以解決它。 時間復雜度為O(#elements * (sum of absolute values of elements)^2) 當然,如果個別數字非常大,這不是一個可行的解決方案。 在這種情況下,當元素數量為~20時,蠻力方法將起作用。

a1 = [2, 1, 1, -1] 
a2 = [-1, -2, -2, -4]
memo = {}   # to store dp state
nxt = {}    # for reconstructing path

def foo(a1, a2, index, total, pos):
    if index == len(a1):
        return pos + abs(total)
    if (index, total, pos) in memo:
        return memo[(index, total, pos)]

    # take from first array
    if a1[index] > 0:
        r1 = foo(a1, a2, index+1, total + a1[index], pos+a1[index])
    else:
        r1 = foo(a1, a2, index+1, total + a1[index], pos)

    # take from second array
    if a2[index] > 0:
        r2 = foo(a1, a2, index+1, total + a2[index], pos+a2[index])
    else:
        r2 = foo(a1, a2, index+1, total + a2[index], pos)

    # save path taken at this step
    if r1 < r2:
        nxt[index] = 0
    else:
        nxt[index] = 1

    memo[index, total, pos] = min(r1, r2)
    return min(r1, r2)

print('minimum sum:', foo(a1, a2, 0, 0, 0))   # minimum sum: 2
# path reconstruction
path = []
node = 0
while node < len(a1):
    path.append(nxt[node])
    node += 1
print('path:', path)   # path: [1, 0, 0, 0]
import itertools as iter
a = [a1, a2]
p = len(a1)
idx_to_pick = min(iter.product(*([[0, 1]]*p)), 
                  key=lambda b: abs(sum([a[i][j] for i, j in zip(b, range(p))])))

這段代碼建議選擇a1[0] + a1[1] + a2[2] = 2 + 2 + (-4) ,與OP的選擇不同,但也是正確的。

更新每個OP的后續問題,對此答案發表評論:

import itertools as iter
a1 = [2, 2, 1]
a2 = [-3, -3, -4]
a = [a1, a2]
p = len(a1)


def obj_func(b):
    arr = [a[i][j] for i, j in zip(b, range(p))]
    return sum([x for x in arr if x > 0]) + abs(sum(arr))


idx_to_pick = min(iter.product(*([[0, 1]]*p)), key=obj_func)

使用新的目標函數,仍然有多種解決方案。 它可以是(-3,2,1)或(2,-3,1)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM