Python优化:如何简化代码?

Python优化:如何简化代码?,python,python-3.x,discrete-mathematics,knapsack-problem,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):

我是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来修正一样) 我如何简化它


    谢谢

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

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

    如果你想区分日期而不是电子邮件(即,你只关心一天收到多少封电子邮件,而不关心哪封特定的电子邮件,你确实关心特定数量的电子邮件到达哪一天)这是一个常见的组合问题。您需要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)
    

    如果您想区分日期而不是电子邮件(即,您只关心一天收到多少封电子邮件,而不关心哪封特定的电子邮件,并且您确实关心特定数量的电子邮件在哪一天到达),那么这是一个常见的组合问题。您需要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)
    

    正如Rory Daulton所指出的,这是一个问题。我会尽量用一种简单的方式来解释它,所以不用麻烦去维基百科了

    现在,假设你在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)
    

    这个解决方案不是很有效,因为它可以计算非常大的阶乘。您可以通过寻找一种聪明的方法来计算该值来改进它,或者使用一个为您提供二项式系数的库,例如
    scipy
    sympy

    ,正如Rory Daulton所指出的,这是一个问题。我会尽量用一种简单的方式来解释它,所以不用麻烦去维基百科了

    现在,假设你在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)
    

    这个解决方案不是很有效,因为它可以计算非常大的阶乘。你可以通过寻找一种聪明的方法来计算这个值来改进它,或者使用一个为你提供二项式系数的库,比如
    scipy
    sympy

    答案不就是7吗?26978328?一个数学表达式足够了吗?还是您真的想让代码来计算数字?(一个简单的表达式确实存在。)对现有方法的微小优化:对于
    d7
    ,没有循环,因为
    d7=n-d1-d2-d6
    。没有必要对它进行实际评估,只需
    solcount+=1
    。答案不是只有42选择7吗?26978328?一个数学表达式足够了吗?还是您真的想让代码来计算数字?(一个简单的表达式确实存在。)对现有方法的微小优化:对于
    d7
    ,没有循环,因为
    d7=n-d1-d2-d6
    。无需实际计算,只需
    solcount+=1
    。谢谢!那么,如果我必须,另外,打印所有的组合,我还需要做什么?@A.K:请看我答案的补充。谢谢!那么,如果我必须,另外,打印所有的组合,我还需要做什么?@A.K:请看我答案的补充。谢谢!那么,如果我必须,另外,打印所有的组合,我还必须做什么?Rory的答案正是你需要的。这是一个发电机,如果你使用它,你会得到每一个“组合”。谢谢!那么,如果我必须,另外,打印所有的组合,我还必须做什么?Rory的答案正是你需要的。它是一个发电机,如果你消耗它,你会得到每一个“组合”。