Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/algorithm/11.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 - Fatal编程技术网

用于整数分区的优雅Python代码

用于整数分区的优雅Python代码,python,algorithm,Python,Algorithm,我试图编写代码来解决标准整数分区问题()。我写的代码乱七八糟。我需要一个优雅的解决方案来解决这个问题,因为我想改进我的编码风格。这不是一个家庭作业问题。我不知道我的代码是否是最优雅的,但为了研究的目的,我不得不多次解决这个问题。如果修改 sub_nums 变量可以限制分区中使用的数字 def make_partitions(number): out = [] tmp = [] sub_nums = range(1,number+1) for num in sub

我试图编写代码来解决标准整数分区问题()。我写的代码乱七八糟。我需要一个优雅的解决方案来解决这个问题,因为我想改进我的编码风格。这不是一个家庭作业问题。

我不知道我的代码是否是最优雅的,但为了研究的目的,我不得不多次解决这个问题。如果修改

sub_nums
变量可以限制分区中使用的数字

def make_partitions(number):
    out = []
    tmp = []
    sub_nums = range(1,number+1)
    for num in sub_nums:
        if num<=number:
            tmp.append([num])
        for elm in tmp:
            sum_elm = sum(elm)
            if sum_elm == number:
                out.append(elm)
            else:
                for num in sub_nums:
                    if sum_elm + num <= number:
                         L = [i for i in elm]
                         L.append(num)
                         tmp.append(L)
    return out
def make_分区(编号):
out=[]
tmp=[]
sub_nums=范围(1,数字+1)
对于sub_nums中的num:
如果num

只需实现这个递归。F(x,n)是所有总和为x且其元素大于或等于n的集合的集合。

虽然这个答案很好,但我建议skovorodkin在下面给出答案:

>>> def partition(number):
...     answer = set()
...     answer.add((number, ))
...     for x in range(1, number):
...         for y in partition(number - x):
...             answer.add(tuple(sorted((x, ) + y)))
...     return answer
... 
>>> partition(4)
set([(1, 3), (2, 2), (1, 1, 2), (1, 1, 1, 1), (4,)])
如果你想要所有的排列(即(1,3)和(3,1))改变
答案。添加(元组(排序((x,)+y))
答案。添加((x,)+y)

我认为这道菜可能算是优雅的。它精巧(20行),快速,基于凯勒和奥沙利文的作品,其中引用了:

def aP(n):
    """Generate partitions of n as ordered lists in ascending
    lexicographical order.

    This highly efficient routine is based on the delightful
    work of Kelleher and O'Sullivan.

    Examples
    ========

    >>> for i in aP(6): i
    ...
    [1, 1, 1, 1, 1, 1]
    [1, 1, 1, 1, 2]
    [1, 1, 1, 3]
    [1, 1, 2, 2]
    [1, 1, 4]
    [1, 2, 3]
    [1, 5]
    [2, 2, 2]
    [2, 4]
    [3, 3]
    [6]

    >>> for i in aP(0): i
    ...
    []

    References
    ==========

    .. [1] Generating Integer Partitions, [online],
        Available: http://jeromekelleher.net/generating-integer-partitions.html
    .. [2] Jerome Kelleher and Barry O'Sullivan, "Generating All
        Partitions: A Comparison Of Two Encodings", [online],
        Available: http://arxiv.org/pdf/0909.2331v2.pdf

    """
    # The list `a`'s leading elements contain the partition in which
    # y is the biggest element and x is either the same as y or the
    # 2nd largest element; v and w are adjacent element indices
    # to which x and y are being assigned, respectively.
    a = [1]*n
    y = -1
    v = n
    while v > 0:
        v -= 1
        x = a[v] + 1
        while y >= 2 * x:
            a[v] = x
            y -= x
            v += 1
        w = v + 1
        while x <= y:
            a[v] = x
            a[w] = y
            yield a[:w + 1]
            x += 1
            y -= 1
        a[v] = x + y
        y = a[v] - 1
        yield a[:w]
def aP(n):
“”“以升序方式将n个分区生成为有序列表
词典顺序。
这个高效的程序是基于令人愉快的
凯勒赫和奥沙利文的作品。
例子
========
>>>对于aP(6)中的i:i
...
[1, 1, 1, 1, 1, 1]
[1, 1, 1, 1, 2]
[1, 1, 1, 3]
[1, 1, 2, 2]
[1, 1, 4]
[1, 2, 3]
[1, 5]
[2, 2, 2]
[2, 4]
[3, 3]
[6]
>>>对于aP(0)中的i:i
...
[]
工具书类
==========
..[1]正在生成整数分区,[联机],
提供:http://jeromekelleher.net/generating-integer-partitions.html
[2]杰罗姆·凯勒赫和巴里·奥沙利文,“创造一切
分区:两种编码的比较“,[联机],
提供:http://arxiv.org/pdf/0909.2331v2.pdf
"""
#列表“a”的前导元素包含
#y是最大的元素,x要么与y相同,要么与
#第二大元素;v和w为相邻元素指数
#分别为其指定x和y。
a=[1]*n
y=-1
v=n
当v>0时:
v-=1
x=a[v]+1
当y>=2*x时:
a[v]=x
y-=x
v+=1
w=v+1

而x比接受的响应快得多,外观也不错。接受的响应多次执行许多相同的工作,因为它多次计算较低整数的分区。例如,当n=22时,差值为12.7秒,而不是0.0467秒

def partitions_dp(n):
    partitions_of = []
    partitions_of.append([()])
    partitions_of.append([(1,)])
    for num in range(2, n+1):
        ptitions = set()
        for i in range(num):
            for partition in partitions_of[i]:
                ptitions.add(tuple(sorted((num - i, ) + partition)))
        partitions_of.append(list(ptitions))
    return partitions_of[n]
代码基本上是相同的,只是我们保存了较小整数的分区,这样我们就不必反复计算它们了

# -*- coding: utf-8 -*-
import timeit

ncache = 0
cache = {}


def partition(number):
    global cache, ncache
    answer = {(number,), }
    if number in cache:
        ncache += 1
        return cache[number]
    if number == 1:
        cache[number] = answer
        return answer
    for x in range(1, number):
        for y in partition(number - x):
            answer.add(tuple(sorted((x, ) + y)))
    cache[number] = answer
    return answer


print('To 5:')
for r in sorted(partition(5))[::-1]:
    print('\t' + ' + '.join(str(i) for i in r))

print(
    'Time: {}\nCache used:{}'.format(
        timeit.timeit(
            "print('To 30: {} possibilities'.format(len(partition(30))))",
            setup="from __main__ import partition",
            number=1
        ), ncache
    )
)

或者

比诺伦的功能更小更快:

def partitions(n, I=1):
    yield (n,)
    for i in range(I, n//2 + 1):
        for p in partitions(n-i, i):
            yield (i,) + p
让我们比较一下:

In [10]: %timeit -n 10 r0 = nolen(20)
1.37 s ± 28.7 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

In [11]: %timeit -n 10 r1 = list(partitions(20))
979 µs ± 82.9 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

In [13]: sorted(map(sorted, r0)) == sorted(map(sorted, r1))
Out[14]: True
对于
n=20
,速度似乎快了1370倍

无论如何,这离:


您可以在ActiveState上找到其他版本:



我使用Python 3.6.1和IPython 6.0.0。

我将解决方案与(我的一个小项目)进行了比较,发现Nolen的得票最多的答案也是最慢的

由提供的两个答案都要快得多。(请注意对数刻度。)


要生成绘图,请执行以下操作:

导入perfplot
导入集合
def nolen(编号):
答案=集合()
回答。添加((编号,))
对于范围内的x(1,编号):
对于nolen中的y(数字-x):
添加(元组(已排序((x,)+y)))
回覆
def skovorodkin(n):
返回集(skovorodkin_产量(n))
def skovorodkin_产量(n,I=1):
产量(n,)
对于范围内的i(i,n//2+1):
对于skovorodkin_产量中的p(n-i,i):
收益率(i,)+p
def加速asc(n):
返回设置(加速asc产量(n))
def加速asc产量(n):
a=[0表示范围(n+1)内的i]
k=1
y=n-1
当k!=0时:
x=a[k-1]+1
k-=1
而2*x我需要解决一个类似的问题,即用排列将整数
n
划分为
d
非负部分。对此,有一个简单的递归解决方案(请参阅):

输出:

[
    [5, 0, 0], [4, 1, 0], [3, 2, 0], [2, 3, 0], [1, 4, 0],
    [0, 5, 0], [4, 0, 1], [3, 1, 1], [2, 2, 1], [1, 3, 1],
    [0, 4, 1], [3, 0, 2], [2, 1, 2], [1, 2, 2], [0, 3, 2],
    [2, 0, 3], [1, 1, 3], [0, 2, 3], [1, 0, 4], [0, 1, 4],
    [0, 0, 5]
]

我参加比赛有点晚了,但我可以提供一个在某些方面可能更优雅的贡献:

def partitions(n, m = None):
  """Partition n with a maximum part size of m. Yield non-increasing
  lists in decreasing lexicographic order. The default for m is
  effectively n, so the second argument is not needed to create the
  generator unless you do want to limit part sizes.
  """
  if m is None or m >= n: yield [n]
  for f in range(n-1 if (m is None or m >= n) else m, 0, -1):
    for p in partitions(n-f, f): yield [f] + p
只有3行代码。按字典顺序生成。可选择允许施加最大零件尺寸

对于具有给定数量部件的分区,我在上面还有一个变化:

def sized_partitions(n, k, m = None):
  """Partition n into k parts with a max part of m.
  Yield non-increasing lists.  m not needed to create generator.
  """
  if k == 1:
    yield [n]
    return
  for f in range(n-k+1 if (m is None or m > n-k+1) else m, (n-1)//k, -1): 
    for p in sized_partitions(n-f, k-1, f): yield [f] + p
撰写上述内容后,我遇到了一个解决方案,该解决方案是我近5年前创建的,但我已经忘记了。除了最大零件尺寸外,此解决方案还提供了一个附加功能,您可以施加最大长度(而不是特定长度)。FWIW:

def分区(总和,最大值=100000,最大长度=100000):
“”“具有值和长度限制的sum分区生成器”“”
#按字典顺序递减生成列表。
#要获取任何长度,请省略第3个参数。
#要获取所有分区,请省略第2个和第3个参数。
如果sum=sum;即第一个>=sum/max。
对于范围内的第一个(最小值(和-1,最大值),最大值(0,(和-1)//最大值),-1):
对于分区中的p(先求和、先求和、最大长度-1):
产量[第一]+p

这里是一个递归函数,它使用一个堆栈,我们在其中按递增顺序存储分区数。 它足够快而且非常直观

#获取整数的分区
堆栈=[]
def分区(余数,起始编号=1):
如果余数==0:
打印(“+”。连接(堆栈))
其他:
对于nb_至_外接程序范围(起始编号,余数+1):
Stack.append(str(nb_to_add))
分区(剩余部分-nb_至_添加,nb_至_添加)
Stack.pop()
当堆栈已满时(元素的总和o
def partition(n, d, depth=0):
    if d == depth:
        return [[]]
    return [
        item + [i]
        for i in range(n+1)
        for item in partition(n-i, d, depth=depth+1)
        ]


# extend with n-sum(entries)
n = 5
d = 3
lst = [[n-sum(p)] + p for p in partition(n, d-1)]

print(lst)
[
    [5, 0, 0], [4, 1, 0], [3, 2, 0], [2, 3, 0], [1, 4, 0],
    [0, 5, 0], [4, 0, 1], [3, 1, 1], [2, 2, 1], [1, 3, 1],
    [0, 4, 1], [3, 0, 2], [2, 1, 2], [1, 2, 2], [0, 3, 2],
    [2, 0, 3], [1, 1, 3], [0, 2, 3], [1, 0, 4], [0, 1, 4],
    [0, 0, 5]
]
def partitions(n, m = None):
  """Partition n with a maximum part size of m. Yield non-increasing
  lists in decreasing lexicographic order. The default for m is
  effectively n, so the second argument is not needed to create the
  generator unless you do want to limit part sizes.
  """
  if m is None or m >= n: yield [n]
  for f in range(n-1 if (m is None or m >= n) else m, 0, -1):
    for p in partitions(n-f, f): yield [f] + p
def sized_partitions(n, k, m = None):
  """Partition n into k parts with a max part of m.
  Yield non-increasing lists.  m not needed to create generator.
  """
  if k == 1:
    yield [n]
    return
  for f in range(n-k+1 if (m is None or m > n-k+1) else m, (n-1)//k, -1): 
    for p in sized_partitions(n-f, k-1, f): yield [f] + p
def partitions(sum, max_val=100000, max_len=100000):
    """ generator of partitions of sum with limits on values and length """
    # Yields lists in decreasing lexicographical order. 
    # To get any length, omit 3rd arg.
    # To get all partitions, omit 2nd and 3rd args. 

    if sum <= max_val:       # Can start with a singleton.
        yield [sum]

    # Must have first*max_len >= sum; i.e. first >= sum/max_len.
    for first in range(min(sum-1, max_val), max(0, (sum-1)//max_len), -1):
        for p in partitions(sum-first, first, max_len-1):
            yield [first]+p