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

时间:2017-06-17 07:35:32

标签: python python-3.x discrete-mathematics knapsack-problem

我是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天收到的电子邮件数量。

现在,有两个问题:

  1. 运行时非常高,我怀疑这个算法 远非最佳状态。
  2. 该代码不允许我更改天数(例如,如果我将天数固定为变量 k )。
  3. 如何简化此操作?

    谢谢!

3 个答案:

答案 0 :(得分:2)

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

  

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

如果要打印这些分区(全部一百二十万个)而不是仅计算它们,将每个分区放在一个单独的行上,用

替换最后一行代码
for v in partitions_nonnegative_fixed_length_ordered(42, 7):
    print(v)

答案 1 :(得分:2)

如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

答案 2 :(得分:1)

我相信您可以找到所需的确切代码herehere