Python 如何在分区算法中检索子集?

Python 如何在分区算法中检索子集?,python,algorithm,performance,python-3.x,partitioning,Python,Algorithm,Performance,Python 3.x,Partitioning,我有一个数组,我想把它分成两部分,使它们的和相等。例如,[10,30,20,50]可以分成[10,40],[20,30]。两者的总数都是50。这本质上是分区算法,但我希望检索子集,而不仅仅是确定它是否可分区。因此,我继续做了以下工作: 更新:更新脚本以处理重复项 from collections import Counter def is_partitionable(a): possible_sums = [a[0]] corresponding_subsets = [[a[0

我有一个数组,我想把它分成两部分,使它们的和相等。例如,
[10,30,20,50]
可以分成
[10,40],[20,30]
。两者的总数都是50。这本质上是分区算法,但我希望检索子集,而不仅仅是确定它是否可分区。因此,我继续做了以下工作:

更新:更新脚本以处理重复项

from collections import Counter

def is_partitionable(a):
    possible_sums = [a[0]]
    corresponding_subsets = [[a[0]]]
    target_value = sum(a)/2
    if a[0] == target_value:
        print("yes",[a[0]],a[1:])
        return
    for x in a[1:]:
        temp_possible_sums = []
        for (ind, t) in enumerate(possible_sums):
            cursum = t + x
            if cursum < target_value:
                corresponding_subsets.append(corresponding_subsets[ind] + [x])
                temp_possible_sums.append(cursum)
            if cursum == target_value:
                one_subset = corresponding_subsets[ind] + [x]
                another_subset = list((Counter(a) - Counter(one_subset)).elements())
                print("yes", one_subset,another_subset)
                return
        possible_sums.extend(temp_possible_sums)
    print("no")
    return

is_partitionable(list(map(int, input().split())))

我基本上是存储添加的相应值,以在
相应的\u子集中获得值。但是,随着
a
大小的增加,很明显
对应的_子集
会有太多的子列表(等于
可能的_和
中的元素数量)。有更好/更有效的方法吗?

尽管这仍然是一个难题,但您可以尝试以下方法。我假设有
n
个元素,它们存储在名为
arr
的数组中(我假设基于1的索引)。让我们组成两个小组
A
B
,这样我想在小组
A
B
之间划分
arr
的元素,这样两个小组中的元素总和相等。
arr
的每个元素都有一个选项,可以转到团队
A
或团队
B
。假设一个元素(比如第i个元素)进入团队
A
我们用
-A[i]
表示它,如果它进入团队
B
我们就让它成为
A[i]
。因此,在将每个元素分配给一个团队后,如果总和为
0
我们的工作就完成了。我们将创建
n
集(它们不存储重复项)。我将使用示例
arr={10,20,30,40}
。遵循以下步骤

set_1 = {10,-10} # -10 if it goes to Team A and 10 if goes to B

set_2 = {30,-10,10,-30} # four options as we add -20 and 20

set_3 = {60,0,20,-40,-20,-60} # note we don't need to store duplicates

set_4 = {100,20,40,-40,60,-20,-80,0,-60,-100} # see there is a zero means our task is possible
现在,您所要做的就是从上一组中的
0
回溯,以查看第i个元素
a[i]
是否添加为
a[i]
-a[i]
,即是否添加到团队
a
B

编辑

回溯程序。所以我们有
n
集合,从
set\u 1
set\u n
。让我们制作两个列表
list_A
来推送属于团队
A
的元素,同样地
list_B
。我们从
set\u n
开始,因此使用一个变量
current\u set
,该变量最初具有值
n
。此外,我们还将重点放在最后一个列表中的元素
0
,因此使用一个变量
current\u元素
,该变量最初具有值
0
。遵循下面代码中的方法(我假设所有集合1到
n
都已形成,为了方便起见,我将它们存储为列表列表,但您应该使用集合数据结构)。此外,下面的代码假设在最后一个列表中看到了
0
。我们的任务是可能的

sets = [ [0], #see this dummy set it is important, this is set_0
              #because initially we add -arr[0] or arr[0] to 0
         [10,-10],
         [30,-10,10,-30],
         [60,0,20,-40,-20,-60],
         [100,20,40,-40,60,-20,-80,0,-60,-100]]

# my array is 1 based so ignore the zero
arr = [0,10,20,30,40]

list_A = []
list_B = []

current_element = 0
current_set = 4 # Total number of sets in this case is n=4

while current_set >= 1:
   print current_set,current_element
   for element in sets[current_set-1]:
     if element + arr[current_set] == current_element:
       list_B.append(arr[current_set])
       current_element = element
       current_set -= 1
       break
     elif element - arr[current_set] == current_element:
       list_A.append(arr[current_set])
       current_element = element
       current_set -= 1
       break


print list_A,list_B

这是我实现@sasha算法的可行性

def my_part(my_list):
    item = my_list.pop()
    balance = []
    temp = [item, -item]
    while len(my_list) != 0:
        new_player = my_list.pop()
        for i, items in enumerate(temp):
            balance.append(items + new_player)
            balance.append(items - new_player)
        temp = balance[:]
    balance = set(balance)
    if 0 in balance:
        return 'YES'
    else:
        return 'NO'

我也在做回溯工作。

@Ev.Kounis,请等一分钟,一个小的回溯bug@Ev.Kounis,已更新@sasha,不,没有,我只是为自己做这件事,但我担心效率,我认为一定有比存储如此庞大的列表更好的方法,比如说我的数组大小顺序是
10^5
+@Ev.Kounis,不,没有这样的约束,我只想用相等的和检索子集!,不,我没有,我必须用某种自上而下的方式来存储列表,这样我们只能存储最上面的列表,然后可以检索最下面的列表,这些列表只是最上面列表的子集?@Ev.Kounis,一分钟,这是由于重复问题。你搜索可行性的方式很漂亮(+1),但我不确定回溯是否那么容易。但是对于最后一个集合的每个元素,都有一个这样的列表:
[1,-1,-1,1]
,其中的数字表示每个元素的位置。从那时起,重新创建分区就很简单。@Ev.Kounis将很快进行编辑,以包括回溯例程。很好的解决方案,等待回溯routine@PruthviRaj我增加了例行程序。希望能有帮助:)我希望你现在能整理好:)
def my_part(my_list):
    item = my_list.pop()
    balance = []
    temp = [item, -item]
    while len(my_list) != 0:
        new_player = my_list.pop()
        for i, items in enumerate(temp):
            balance.append(items + new_player)
            balance.append(items - new_player)
        temp = balance[:]
    balance = set(balance)
    if 0 in balance:
        return 'YES'
    else:
        return 'NO'