将一个数字划分为一组给定的数字

时间:2017-10-06 17:44:50

标签: python numbers set partition

这是我想要做的。给定一个数字和一组数字,我想将该数字划分为集合中给出的数字(重复)。 例如 : 取数字9,数字组= {1,4,9}。 它将产生以下分区:

{(1,1,1,1,1,1,1,1,1),(1,1,1,1,1,4),(1,4,4),(9,) }

使用集合{1,4,9}的其他可能分区不能形成数字9的总和。

我在Python中编写了一个执行任务的函数:

S = [ 1, 4, 9, 16 ]
def partition_nr_into_given_set_of_nrs(nr , S):
   lst = set()
   # Build the base case :
   M = [1]*(nr%S[0]) + [S[0]] * (nr //S[0])
   if set(M).difference(S) == 0  :
      lst.add(M)
   else :
      for x in S :
         for j in range(1, len(M)+1):
            for k in range(1, nr//x +1 ) :
               if  k*x ==  sum(M[:j]) :
                  lst.add(  tuple(sorted([x]*k + M[j:])) )
   return lst

它工作正常,但我想看到一些意见。我不满意它使用3个循环的事实,我猜它可以以更优雅的方式进行改进。在这种情况下,也许递归更适合。任何建议或更正将不胜感激。提前谢谢。

2 个答案:

答案 0 :(得分:1)

我会使用递归函数来解决这个问题,从最大的数字开始,并使用越来越小的数字递归地找到剩余值的解。

def partition_nr_into_given_set_of_nrs(nr, S):
    nrs = sorted(S, reverse=True)
    def inner(n, i):
        if n == 0:
            yield []
        for k in range(i, len(nrs)):
            if nrs[k] <= n:
                for rest in inner(n - nrs[k], k):
                    yield [nrs[k]] + rest
    return list(inner(nr, 0))

S = [ 1, 4, 9, 16 ]
print(partition_nr_into_given_set_of_nrs(9, S))
# [[9], [4, 4, 1], [4, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1]]

当然你也可以通过改变函数的参数并假设列表已经按相反的顺序排序而没有内部函数。

如果要限制大数字的零件数量,可以添加一个aditional参数,指示剩余的允许元素数量,如果该数字仍然大于零,则只生成结果。

def partition_nr_into_given_set_of_nrs(nr, S, m=10):
    nrs = sorted(S, reverse=True)
    def inner(n, i, m):
        if m > 0:
            if n == 0:
                yield []
            for k in range(i, len(nrs)):
                if nrs[k] <= n:
                    for rest in inner(n - nrs[k], k, m - 1):
                        yield [nrs[k]] + rest
    return list(inner(nr, 0, m))

答案 1 :(得分:1)

这是一个使用itertools的解决方案,并且有两个for循环,因此时间复杂度约为O(n * n)(粗略)

通过删除任何大于所需最大总和的元素,对重塑列表应用了一点备忘录。

假设您将总和设为最大值(在这种情况下为9)。

<强>源码

import itertools

x = [ 1, 4, 9, 16 ]
s = []
n = 9
#Remove elements >9
x = [ i for i in x if i <= n]

for i in xrange(1,n + 1):
    for j in itertools.product(x,repeat = i):
        if sum(j) == n:
            s.append(list(j))

#Sort each combo
s =[sorted(i) for i in s]
#group by unique combo
print list(k for k,_ in itertools.groupby(s))

结果

>>> 
>>> 
[[9], [1, 4, 4], [1, 1, 1, 1, 1, 4], [1, 1, 1, 1, 1, 1, 1, 1, 1]]

编辑

您可以通过在产品总和为> 9后停止查找组合来进一步优化速度(如果需要) e.g。

if sum(j) > n + 2:
            break