Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/278.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 带减法的子集求和算法_Python_Algorithm_Subset Sum - Fatal编程技术网

Python 带减法的子集求和算法

Python 带减法的子集求和算法,python,algorithm,subset-sum,Python,Algorithm,Subset Sum,我有一个子集和问题,你可以加上或减去这些项。例如,如果我有五个术语(1、2、3、4、5),我想知道有多少种方法可以加/减这些术语,从而得到7: 3+4 2+5 1+2+4 5-2+4 等等 我用Python编写了一些代码,但一旦出现了许多术语,它的速度就会非常慢: import itertools from collections import OrderedDict sum_answer = 1 terms = {"T1": 1, "T2": -2, "T3": 3, "T4": -4,

我有一个子集和问题,你可以加上或减去这些项。例如,如果我有五个术语(1、2、3、4、5),我想知道有多少种方法可以加/减这些术语,从而得到7:

  • 3+4
  • 2+5
  • 1+2+4
  • 5-2+4
  • 等等
我用Python编写了一些代码,但一旦出现了许多术语,它的速度就会非常慢:

import itertools
from collections import OrderedDict

sum_answer = 1
terms = {"T1": 1, "T2": -2, "T3": 3, "T4": -4, "T5": 5}
numlist = [v for v in terms.values()]
zerlist = [x for x in itertools.repeat(0, len(numlist))]
opslist = [item for item in itertools.product((1, -1), repeat=len(numlist))]


res_list = []
for i in range(1, len(numlist)):
    combos = itertools.combinations(numlist, i)

    for x in combos:
        prnlist = list(x) + zerlist[:len(numlist) - len(x)]

        for o in opslist:
            operators = list(o)
            result = []
            res_sum = 0

            for t in range(len(prnlist)):
                if operators[t] == 1:
                    ops = "+"
                else:
                    ops = "-"
                if prnlist[t] != 0:
                    result += [ops, list(terms.keys())[list(terms.values()).index(prnlist[t])]]
                res_sum += operators[t] * prnlist[t]

            if sum_answer == res_sum:
                res_list += [" ".join(result)]

for ans in OrderedDict.fromkeys(res_list).keys():
    print(ans)
我意识到一百万个嵌套循环的效率非常低,所以我可以用一个更好的算法来加快速度吗?

类似于“常规”子集求和问题-在这里,您可以使用它来解决问题,但还需要一种可能性-减少当前元素,而不是添加它

f(0,i) = 1               //successive subset
f(x,0) = 0    x>0        //failure subset
f(x,i) = f(x+element[i],i-1) + f(x-element[i],i-1) + f(x,i-1)
                                 ^^^
               This is the added option for substraction
当将其转换为自下而上的DP解决方案时,您需要创建一个大小为
(SUM+1)*(2n+1)
的矩阵,其中
SUM
是所有元素的总和,
n
是元素的数量。

类似于“常规”子集和问题-在您用来解决问题的地方,您也将在这里使用它,但是还需要一种可能性——减少当前元素而不是添加它

f(0,i) = 1               //successive subset
f(x,0) = 0    x>0        //failure subset
f(x,i) = f(x+element[i],i-1) + f(x-element[i],i-1) + f(x,i-1)
                                 ^^^
               This is the added option for substraction

当将其转换为自下而上的DP解决方案时,您需要创建一个大小为
(SUM+1)*(2n+1)
的矩阵,其中
SUM
是所有元素的总和,
n
是元素的数量。

我认为您的想法基本正确:生成每个术语的组合,进行求和,看看是否成功。不过,您可以优化代码

问题是,一旦生成
1+2
,您就会发现它与所需的总和不匹配,并将其丢弃。但是,如果您向它添加
4
,它就是一个解决方案。但是,在生成
1+2+4
之前,您无法获得该解决方案,此时您将从头开始计算总和。您还可以为每个组合从头开始添加操作符,这也会出于同样的原因进行大量冗余工作

您还使用了大量的列表操作,这可能会很慢

我会这样做:

def solve(terms_list, stack, current_s, desired_s):
    if len(terms_list) == 0:
        if current_s == desired_s:
            print(stack)
        return

    for w in [0, 1, -1]: # ignore term (0), add it (1), subtract it (-1)
        stack.append(w)
        solve(terms_list[1:], stack, current_s + w * terms_list[0], desired_s)
        stack.pop()
例如,初始调用是
solve([1,2,3,4,5],[0,7)

请注意,这具有复杂性,因为每个术语都可以加、减或忽略

我的实际实现的复杂性是
O(n*3^n)
,因为递归调用复制了
terms\u list
参数。不过,您可以避免这种情况,但我希望使代码更简单,并将其作为练习。也可以避免在打印前构造实际表达式,而是以增量方式构造,但可能需要更多参数


然而,
O(3^n)
仍然很多,无论你做什么,你都不应该期望它在大型
n
中表现得很好。

我认为你的想法基本上是正确的:生成每个术语的组合,求和,看看是否成功。不过,您可以优化代码

问题是,一旦生成
1+2
,您就会发现它与所需的总和不匹配,并将其丢弃。但是,如果您向它添加
4
,它就是一个解决方案。但是,在生成
1+2+4
之前,您无法获得该解决方案,此时您将从头开始计算总和。您还可以为每个组合从头开始添加操作符,这也会出于同样的原因进行大量冗余工作

您还使用了大量的列表操作,这可能会很慢

我会这样做:

def solve(terms_list, stack, current_s, desired_s):
    if len(terms_list) == 0:
        if current_s == desired_s:
            print(stack)
        return

    for w in [0, 1, -1]: # ignore term (0), add it (1), subtract it (-1)
        stack.append(w)
        solve(terms_list[1:], stack, current_s + w * terms_list[0], desired_s)
        stack.pop()
例如,初始调用是
solve([1,2,3,4,5],[0,7)

请注意,这具有复杂性,因为每个术语都可以加、减或忽略

我的实际实现的复杂性是
O(n*3^n)
,因为递归调用复制了
terms\u list
参数。不过,您可以避免这种情况,但我希望使代码更简单,并将其作为练习。也可以避免在打印前构造实际表达式,而是以增量方式构造,但可能需要更多参数


然而,
O(3^n)
仍然很多,无论你做什么,你都不应该期望它对大的
n
表现得很好。

现在你正试图从一行强制执行所有可能的字段值组合(然后针对其他行测试每个组合的有效性)

我想你有很多行的数据要处理;我建议你利用这一点,取一堆行(至少是你要求解的字段的数量),然后应用一个近似的矩阵解算器,比如

这有许多重要的优点:

  • 允许您合理地处理舍入错误问题(如果您的任何字段都是非整数,则必须这样做)

  • 允许您轻松处理系数不在
    {-1,0,1}
    中的字段,即系数可能类似
    0.12
    的税率

  • 使用不需要调试或维护的完全受支持的代码

  • 使用运行速度更快的高度优化的代码(**最有可能,取决于编译numpy时使用的选项)

  • 它具有更好的时间复杂度(类似于O(n**2.8)而不是O(3**n)),这意味着它应该扩展到更多的字段

因此,一些测试数据:

import numpy as np

# generate test data
def make_test_data(coeffs, mean=20.0, base=0.05):
    w      = len(coeffs)    # number of fields
    h      = int(1.5 * w)   # number of rows of data
    rows   = np.random.exponential(mean - base, (h, w)) + base
    totals = data.dot(coeffs)
    return rows.round(2), totals.round(2)
这让我们有点像

>>> rows, totals = make_test_data([0, 1, 1, 0, -1, 0.12])

>>> print(rows)
[[  1.45  17.63  22.54   5.54  37.06   1.47]
 [ 11.71  80.43  26.43  18.48  11.08   8.8 ]
 [ 16.09  11.34  63.74   3.31  13.2   13.35]
 [ 11.96  12.17  10.23   8.15  73.3    0.42]
 [  4.03   8.01  20.84  21.46   2.76  18.98]
 [  3.24   6.6   35.06  23.17   9.03   8.58]
 [ 25.05  33.72   6.82   0.49  46.76  12.21]
 [ 70.27   1.48  23.05   0.69  31.11  43.13]
 [  9.04  10.45  15.08   4.32  52.94  11.13]]

>>> print(totals)
[  3.29  96.84  63.48 -50.85  28.37  33.66  -4.75  -1.4  -26.07]
以及解算器代码

>>> sol = np.linalg.lstsq(rows, totals)    # one line!

>>> print(sol[0])       # note the solutions are not *exact*
[ -1.485730e-04  1.000072e+00  9.999334e-01 -7.992023e-05 -9.999552e-01  1.203379e-01]

>>> print(sol[0].round(3))      # but they are *very* close
[ 0.    1.    1.    0.   -1.    0.12]

现在,您正试图强制一行中所有可能的字段值组合(然后针对其他行测试每个组合的有效性)

我想你有很多行的数据要处理;我建议你利用这一点,取一堆行(至少是你要求解的字段的数量),然后应用一个近似的矩阵解算器,比如

这个h