这是有条件限制的正整数有序拆分问题,没有直接的计算公式。
如果规模小,可以枚举。如果大到上百位,可以用动态规划算法。如果大到上千位,动态规划法也得几十分钟,甚至几个小时。规模更大,大到上万位的话,受时间和空间限制,PC 已无力完成了。
“插板法+容斥原理” 是个大杀器。
参考了 CSDN 网友 “西域狂猪” 的文章,核心算法见如下公式:
需要减去首位为 0 的情形,即:
total = sum( i = 0~n ) - sum( i = 0~n-1 )
Python 有内置的大整数和组合计算函数,测试了一下,效率胜过 Fortran。
Python 代码如:
# 1000位正整数,各位数字之和为5000,有多少个?
# szw_sh@163.com
# 2024-07-14
from math import comb
import time
def sum_expression(n, s, m):
"""
计算满足特定条件的项的累加和。
参数:
n -- 正整数的位数
s -- 各位数字之和
m -- 各数字小于该值
返回: 满足条件的项的累加和
"""
total_sum = 0
for i in range(n + 1):
if i * m > s:
break
term = (-1)**i * comb(n-1+s-i*m, n-1) * comb(n, i)
total_sum += term
return total_sum
n = 1000
s = 5000
m = 10
start_time = time.time()
total = sum_expression(n, s, m) - sum_expression(n-1, s, m)
end_time = time.time()
time = (end_time - start_time) * 1000
print(f"total = {total}")
print(f"time = {time:.0f} ms")
n = 1000,s = 5000,m = 10 时,运行耗时 507 毫秒。
计算结果:
total = 10285365067195108279225142980141878600436775717079668098763107749084681963865663398549438382871357086343262593006829915422766021250213523036365720794008181853252571208894977792325007703100745052309162270431546632904405801249072129315390740993369189182722741184529959028657805877226409256723754071554845143402710377458469861704855510858930204077579300369143561806187085658687038624639772365001271352767972620393008430988939906039570647457525714932032736164841522324617879072246419422236156405366464153445733145564034044182972376371702400606659207050480816922601281434846851694581686209528756902423796651122212073205284566821880292636599595714274027420413877993217075979458903453123944101597589100389223674589656462816635288040086976653861613595937991672823392045555064175060116269398996825041632535493492592636186432758707578488603262994149040157870336864388850537821127957936706260836057978829775191634098139364843182377363956436912804227224405620901587681832910238503135057103703350309161710
time = 507 ms
附:在线编译运行的截图