Python 优化:如何简化此代码?

Posted

技术标签:

【中文标题】Python 优化:如何简化此代码?【英文标题】:Optimization on Python: How do I simplify this code? 【发布时间】:2017-06-17 07:35:31 【问题描述】:

我是 python 新手,我正在尝试解决这个优化问题:

    In How many possible ways can I receive 42 emails in 7 days?

我用 Python 编写了这个程序来计算所有解决方案:

n = 42
print(n, "emails can be received in the following ways:")
solcount = 0
for d1 in range (n+1):
    for d2 in range (n+1-d1):
        for d3 in range (n+1-d1-d2):
            for d4 in range (n+1-d1-d2-d3):
                for d5 in range (n+1-d1-d2-d3-d4):
                    for d6 in range (n+1-d1-d2-d3-d4-d5):
                        for d7 in range (n+1-d1-d2-d3-d4-d5-d6):
                            if d1+d2+d3+d4+d5+d6+d7 == n:
                                solcount +=1
print("There are", solcount, "possible solutions")

其中 d1 到 d7 分别是第 1 天到第 7 天收到的电子邮件数量。

现在,这有两个问题:

    运行时间高的离谱,我怀疑这个算法 远非最佳。 代码不允许我改变天数(就像我将天数固定为变量k)。

如何简化?

谢谢!

【问题讨论】:

codereview.stackexchange.com/tour 答案不就是42选7吗? 26978328? 数学表达式是否足够,或者您真的想要代码来计算数字? (确实存在一个简单的表达式。) ***.com/questions/14992411/… 现有方法的微小优化:d7 没有循环,因为d7 = n - d1 - d2 - ... - d6。无需实际评估,只需solcount += 1 【参考方案1】:

正如 Rory Daulton 所说,这是一个stars and bars 问题。我会尽量用简单的方式来解释它,所以甚至不要费心去***。

现在,假设您在 3 天内只收到 5 封电子邮件。解的总数与以下的字谜相同:

"eee|e|e" # represents 3 emails in day1, 1 in day2 and 1 in day3

字谜可以计算为符号数量的阶乘除以每个符号重复次数的阶乘乘积。在我们的简单案例中:

(5 + 3 - 1)!/(5!*(3-1)!)

请注意,我们三天只需要 2 个酒吧。

使用这个简单的参数,您可以轻松实现如下解决方案:

from math import factorial

def possibilities(emails, days):
    return factorial(emails + days - 1)//factorial(emails)//factorial(days - 1)

此解决方案效率不高,因为它可以计算非常大的阶乘。您可以通过寻找一种计算此值的巧妙方法来改进它,或者使用为您提供二项式系数的库,例如 scipysympy

【讨论】:

谢谢!那么,如果我必须另外打印所有组合,我还需要做什么? Rory 的答案正是您所需要的。它是一个生成器,如果你使用它,你会得到每个“组合”。【参考方案2】:

如果您想区分日期而不区分电子邮件(即您只关心一天收到多少封电子邮件而不关心哪些特定的电子邮件,并且您确实关心特定数量的电子邮件在哪一天到达),那么这是一个常见的stars and bars 组合问题。您需要 7 个非负整数,其总和为 42,并且数字的顺序很重要。如果nCr(n, k)是从一组大小n中取出的大小为k的子集的数量,即二项式系数,那么你想要的数字是

nCr(42 + 7 - 1, 7 - 1) = 12271512

这是实际计算出该数字需要 19.7 秒的代码。使用递归很容易改变天数。我的这个版本的代码使用了一个生成器,你在 Python 研究中可能还没有见过。

def partitions_nonnegative_fixed_length_ordered(n, r):
    """Generate the partitions of the nonnegative integer `n` as the
    sum of `r` nonnegative integers, where the order of the integers
    matters. The partitions are tuples and are generated in
    lexicographic order. The number of partitions generated is
    binomialcoefficient(n+r-1, r-1).
    """
    def partitions_prefixed(prefix, n, r):
        if r == 1:
            yield prefix + (n,)
        else:
            for i in range(n + 1):
                yield from partitions_prefixed(prefix + (i,), n - i, r - 1)

    if n >= 0 and r >= 1 and n == int(n) and r == int(r):
        yield from partitions_prefixed(tuple(), int(n), int(r))

print(sum(1 for v in partitions_nonnegative_fixed_length_ordered(42, 7)))

如果您想打印这些分区(全部 1200 万个)而不是只计算它们,每个分区位于单独的一行,请将最后一行代码替换为

for v in partitions_nonnegative_fixed_length_ordered(42, 7):
    print(v)

【讨论】:

谢谢!那么,如果我必须另外打印所有组合,我还需要做什么? @A.K.:请参阅我的答案的补充内容。【参考方案3】:

我相信您想要的确切代码可以在here 和here 找到。

【讨论】:

以上是关于Python 优化:如何简化此代码?的主要内容,如果未能解决你的问题,请参考以下文章

如何简化此更新函数数组代码?

如何使用 f"" Python 简化代码

AS3 求大神优化下这段代码,这个代码里的if else太多了,但需要去判断关数在返回指定的数组,求大神简化!

python代码耗时优化,你知道了吗?

如何简化此解析方法?

JDK1.8新特性Lambda表达式简化if-else里都有for循环的优化方式