最近,我在YouTube上遇到了一个有趣的3Blue1Brown-OlympiadLevelCounting提出的计数问题.问题是找出集合{1,2,…,2000}的子集的个数,这些子集的元素之和可以被5整除.格兰特·桑德森提出了一个漂亮的解决方案,但他也讨论了如果在计算机上实施该算法以有效解决问题可能会变得多么繁琐.

然而,由于Euler的工作,使用整数分区来解决这个问题是有好处的.解决方案是小于或等于2000的所有5的倍数的distinct partitions之和,类似于我的posted on MathstackExchange years ago,其中使用"dxiv"的注释是该代码的关键.

毫无疑问,格兰特·桑德森的解决方案是优雅的,因为它不需要计算机,而且不需要冗长的计算就能找到.

我的代码是:

import numpy as np

def p(k):
    if k == 1:
        return np.array([1, 1])
    else:
        q = p(k - 1)
    return add(q, np.append(np.zeros(k, int), q))


def add(u, v):
    u = np.append(u, np.zeros(len(v) - len(u), int))
    return np.add(u, v)


def test(n):
    a = 1 << n
    b = 1 << (n//5)
    b *= 4
    return (a+b)//5


n = 1000      
generating_function = sum(p(n)[0:-1:5])+1
grant_sanderson = test(n)
print(generating_function)
print(grant_sanderson)
print(generating_function == grant_sanderson)

我的困难是,当n是一个大数字时.另外,这些数字太大了,所以我必须使用gmpy2.这是一个显示数组关于n//2对称的图:

a graph

Traceback (most recent call last):
  File "c:\Users\patil\OneDrive\Documents\GitHub\Theorie-der-Zahlen\Distinct partitions\distinctParitions-gmp2\main.py", line 15, in <module>
    generating_function = sum(p(n)[0:-1:5])+1
                              ^^^^
  File "c:\Users\patil\OneDrive\Documents\GitHub\Theorie-der-Zahlen\Distinct partitions\distinctParitions-gmp2\generatingfunction.py", line 8, in p
    q = p(k - 1)
        ^^^^^^^^
  File "c:\Users\patil\OneDrive\Documents\GitHub\Theorie-der-Zahlen\Distinct partitions\distinctParitions-gmp2\generatingfunction.py", line 8, in p
    q = p(k - 1)
        ^^^^^^^^
  File "c:\Users\patil\OneDrive\Documents\GitHub\Theorie-der-Zahlen\Distinct partitions\distinctParitions-gmp2\generatingfunction.py", line 8, in p
    q = p(k - 1)
        ^^^^^^^^
  [Previous line repeated 996 more times]
RecursionError: maximum recursion depth exceeded

下面的代码可以显示事情是如何迅速失控的:

import gmpy2

def q(n):
    Q = [gmpy2.mpz(0)] * (n+1)
    Q[0] = gmpy2.mpz(1)
    for k in range(1, n):
        for i in range(n, k-1, -1):
            Q[i] += Q[i-k]
    return Q[n] + 1

推荐答案

另一种方法是计算达到5个模数值(0、1、2、3、4)中的每一个的和的组合.将这些计数保存在列表中,并逐步增加它们.最后,取零模的计数,以获得相加为5的倍数的集合的数目:

def sumDiv5Count(numbers):
    modCounts = [0]*5
    for n in numbers:
        modCounts = [c+modCounts[(i-n)%5] for i,c in enumerate(modCounts)]
        modCounts[n%5] += 1        
    return modCounts[0]

每个数字的模5与5个可能的模数相结合,以添加更多的方法来产生5个不同的结果模数值.

例如13%5-3,

  • 产生0的方式被添加到产生0+3的方式-->;槽3

  • 生产1的方法添加到生产1+3的方法-->;槽4

  • 生成2的方法被添加到生成2+3的方法-->;插槽0(2+3)%5=0

  • 生成3的方法被添加到生成3+3的方法-->;槽1(3+3)%5=1

  • 生成4的方法被添加到生成4+3的方法-->;槽2(4+3)%5=2

  • 而且还有一种方法可以自己产生3

输出:

print(sumDiv5Count(range(1,10)))
# 103

print(sumDiv5Count(range(1,50)))
# 112589990684671

print(sumDiv5Count(range(1,100)))
# 126765060022822940149670739967

print(sumDiv5Count(range(1,500)))
# 327339060789614187001318969682759915221664204604306478948329136809613379640467455488327009232590415715088668412756007101428785894679831065931534041087

print(sumDiv5Count(range(1,1000)))
# 1071508607186267320948425049060001810561404811705533607443750388370351051124936122493198378815695858127594672917553146825187145285692314043598457757469857480393456777482423098542107460506237114187795418215304647498358194126739876755916554395250481509160715757985439053702508024174143636196837700927487

print(sumDiv5Count(range(1,2001)))
# 22962613905485090484656664023553639680446354041773904009552854736515325227847406277133189726330125398368919292779749255468942379217261106628518627123333063707825997829062456000137755829648008974285785398012697248956323092729277672789463405208093270794180999311632479761788925921124662329907232844394066536268833781796891701120475896961582811780186955300085800543341325166104401626447256258352253576663441319799079283625404355971680808431970636650308177886780418384110991556717934409897816293912852988275811422719154702569434391547265221166310540389294622648560061463880851178273858239474974548427800575

这种方法的一个优点是,它可以很容易地转换为一个生成器,在遍历数字时逐步产生计数.它也不依赖于数字是在一个序列中,甚至是不同的.考虑到这是一个O(N)进程,性能很好(几毫秒).

Python相关问答推荐

将列中的滚动值集转换为单元格中的单个值

使用unmanagedexports从Python调用的c#DLC

有没有办法清除气流中的僵尸

Python:根据创建时间合并两个收件箱

两极:滚动组,起始指数由不同列设置

Plotly:如何更改Heatmap中彩色条的勾选文本

如何在矩阵上并行化简单循环?

在matplotlib动画gif中更改配色方案

如何使用Google Gemini API为单个提示生成多个响应?

如何从具有多个嵌入选项卡的网页中Web抓取td类元素

返回nxon矩阵的diag元素,而不使用for循环

Python在tuple上操作不会通过整个单词匹配

Pandas 在最近的日期合并,考虑到破产

try 在树叶 map 上应用覆盖磁贴

如何将双框框列中的成对变成两个新列

如何避免Chained when/then分配中的Mypy不兼容类型警告?

Excel图表-使用openpyxl更改水平轴与Y轴相交的位置(Python)

发生异常:TclMessage命令名称无效.!listbox"

如何让这个星型模式在Python中只使用一个for循环?

如何启动下载并在不击中磁盘的情况下呈现响应?