【问题标题】:Partition a number into a given set of numbers将一个数字划分为一组给定的数字
【发布时间】:2018-03-18 14:03:44
【问题描述】:

这就是我想要做的。给定一个数字和一组数字,我想将该数字划分为集合中给定的数字(重复)。 例如 : 取数字 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 个循环这一事实不满意,我想它可以以更优雅的方式进行改进。也许递归更适合这种情况。任何建议或更正将不胜感激。提前致谢。

【问题讨论】:

  • 16 in S 正确吗?
  • 是的,因为它会忽略它,因为它大于要分区的数字 9。无论有没有它,该功能都可以正常工作。较大的数字不能成为分区的一部分。

标签: python numbers set partition


【解决方案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]]

当然你也可以不使用内部函数,方法是改变函数的参数并假设列表已经以相反的顺序排序。

如果您想限制大数字的部分数量,您可以添加一个附加参数,指示剩余允许的元素数量,并且仅当该数量仍然大于零时才产生结果。

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))

【讨论】:

  • 嗨 Tobias_k。如果我想对列表中的元素数量设置上限怎么办?例如:我不能使用函数: nr =1000 和一组 S ={1, 4, 9, 16,25, 36,49, 64,81} 。显然是太多了,代码冻结了。但是我想将结果分区的元素数量限制为 10 个。这是可以实现的。那么如何做到这一点呢?非常感谢。
  • @youth4ever 查看我的编辑。但是请注意,对于您的示例输入,似乎没有只有 10 个术语的解决方案。
【解决方案2】:

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

通过删除大于所需最大总和的任何元素,对 reshape 列表进行一点记忆。

假设您将 sum 设为集合中的最大值(在本例中为 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]]

编辑

您可以通过在产品总和为&gt; 9 后停止查找组合来进一步优化速度(如果需要) 例如

if sum(j) > n + 2:
            break

【讨论】:

    猜你喜欢
    • 2022-01-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-09-17
    • 1970-01-01
    • 2022-10-01
    • 1970-01-01
    相关资源
    最近更新 更多