迭代而不是递归算法,找到将n分成m个部分的所有方法

时间:2015-09-02 03:44:11

标签: python algorithm recursion

我需要一个带两个整数的函数,比方说n和m,其中n> = 0且m> = 1,它返回一个列表列表,包含将n分成m正的所有可能方法整数件(订单事项,[4,7,2]与[7,4,2]不同。)
现在,我能够提出一个快速的小递归函数来完成这项工作,如下所示:

def split(number, elements):
    if elements == 1:
        return [[number]]
    ways = []
    for i in range(0, number + 1):
        for j in split_number(number - i, elements - 1):
            values.append([i] + j)
    return values

但是,我可能会使用大数字,因此需要将其转换为迭代方法。我不确定如何做到这一点,因为它每次'超级调用'会多次调用自己,这使得甚至很难转换为尾调用和累加器,更不用说它们转换为迭代形式了。

示例输出:

split(7, 2) -> [[0, 7], [1, 6], [2, 5], [3, 4], [4, 3], [5, 2], [6, 1], [7, 0]]
split(4, 3) -> [[0, 0, 4], [0, 1, 3], [0, 2, 2], [0, 3, 1], [0, 4, 0], [1, 0, 3], 
[1, 1, 2], [1, 2, 1], [1, 3, 0], [2, 0, 2], [2, 1, 1], [2, 2, 0], [3, 0, 1], [3, 1, 0],
[4, 0, 0]]

4 个答案:

答案 0 :(得分:0)

这是使用itertools的方法:

def chainsplit(n,p):
    return sorted(list(set(chain.from_iterable([list(permutations(i,len(i))) 
        for i in list(combinations_with_replacement(range(n+1),p)) if sum(i) == n]))))

以下是一些时间基准,显示了列表转换,排序和函数调用的开销:

%timeit set(chain.from_iterable([list(permutations(i,len(i))) for i in list(combinations_with_replacement(range(5),3)) if sum(i) == 4]))
10000 loops, best of 3: 20 µs per loop

%timeit list(set(chain.from_iterable([list(permutations(i,len(i))) for i in list(combinations_with_replacement(range(5),3)) if sum(i) == 4])))
10000 loops, best of 3: 20.4 µs per loop

%timeit sorted(list(set(chain.from_iterable([list(permutations(i,len(i))) for i in list(combinations_with_replacement(range(5),3)) if sum(i) == 4]))))
10000 loops, best of 3: 25 µs per loop

%timeit chainsplit(4,3)
10000 loops, best of 3: 26.4 µs per loop

以下是@ zyxue&n;基于numpy的函数f()的基准测试用于比较:

timeit f(4,3)
10000 loops, best of 3: 133 µs per loop

运行你的split()函数会冻结我的内核,所以我无法计时。请注意,在其中split_function应更改为拆分或将函数名称更改为split_function以使其起作用。 split_function可能是更好的选择,以避免与名为split的其他函数混淆。

答案 1 :(得分:0)

import numpy as np
from itertools import combinations_with_replacement

def f(n, m):
    r = combinations_with_replacement(xrange(n + 1), m - 1)
    return [np.diff([0] + list(x) + [n]).tolist() for x in r]


assert f(7, 2) == [[0, 7], [1, 6], [2, 5], [3, 4], [4, 3], [5, 2], [6, 1], [7, 0]]
assert f(4, 3) == [[0, 0, 4], [0, 1, 3], [0, 2, 2], [0, 3, 1], [0, 4, 0], [1, 0, 3],
                   [1, 1, 2], [1, 2, 1], [1, 3, 0], [2, 0, 2], [2, 1, 1], [2, 2, 0],
                   [3, 0, 1], [3, 1, 0], [4, 0, 0]]

答案 2 :(得分:0)

维护一系列未完成的工作。

未完成的作业是一个数字列表,其中第一个元素可能会进一步分割。从队列中提取作业,以可能的方式拆分第一个元素,然后将生成的作业添加回队列。

算法如下(折衷伪代码)

compositeId

答案 3 :(得分:0)

这是Python中的递归转向迭代方法:

def f(n,m):
  stack = [([n] + (m - 1)*[0],0)]
  result = []
  while len(stack) > 0:
    (p,i) = stack.pop()
    if i == m - 1:
      result.append(p)
    else:
      for k in xrange(0,p[i] + 1):
        _p = p[:]
        _p[i] = _p[i] - k
        _p[i + 1] = p[i + 1] + k
        stack.append((_p,i + 1))
  return result

print(f(4,3))
print(f(7,2))