求高效Python算法:统计1到n中数位和≤x的整数个数
高效统计数位和≤x的整数个数优化方案
你的原始方法通过遍历每个数字计算数位和,在n达到1012时会执行万亿次操作,完全无法在合理时间内完成。解决这类大范围数位统计问题,**数位动态规划(数位DP)**是最优方案,它通过按数位递归+记忆化的方式,把时间复杂度降到O(位数×x),对于1012这种最多12位的数字,计算量仅在千级以内。
实现思路
- 将数字n转换为字符串形式的数位列表,方便逐位处理;
- 用递归函数统计符合条件的数:
- 记录当前处理到第几位、已累计的数位和、是否受原数字n的限制(即前面的数位是否和n的对应数位完全相同);
- 记忆化已计算过的状态,避免重复计算;
- 直接返回递归结果,包含0到n的所有符合条件的数。
代码实现
from functools import lru_cache def count_digit_sum_le(x, n): s = str(n) length = len(s) @lru_cache(maxsize=None) def dp(pos, current_sum, tight): # pos: 当前处理的数位索引 # current_sum: 已累计的数位和 # tight: 布尔值,当前是否受原数字的数位限制(True表示前面数位和n完全相同,当前位最大只能取s[pos];False表示当前位可取值0-9) if pos == length: return 1 if current_sum <= x else 0 limit = int(s[pos]) if tight else 9 total = 0 for d in range(0, limit + 1): new_tight = tight and (d == limit) new_sum = current_sum + d if new_sum > x: continue # 数位和已超过x,无需继续递归 total += dp(pos + 1, new_sum, new_tight) return total return dp(0, 0, True)
验证示例
当n=112,x=5时,调用count_digit_sum_le(5, 112)会返回31(包含0到112中数位和≤5的所有数),和你原始函数的结果一致,但效率提升数个数量级。
性能说明
- 对于n=10^12(12位数),x最大为108(12×9),递归的状态数最多为12×109×2=2616,计算量极小;
- 记忆化缓存会自动复用已计算的状态,避免重复递归。
内容的提问来源于stack exchange,提问作者Raphael
相关产品推荐
相关产品推荐

