You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何在线性时间内计算帕斯卡三角某一行的模10值(无需预计算所有前行)

如何在线性时间内计算帕斯卡三角某一行的模10值(无需预计算所有前行)

嘿,我太懂你这个困扰了!之前我也想直接算帕斯卡三角的某一行,结果刚算到n=30左右,数值就直接爆了64位整数,更别说要取模10的结果了。刚好踩过这个坑,给你分享个线性时间、不用预算所有前行的解法,完美解决溢出和模10的问题👇


核心思路:递推+约分+模运算,从根源避免溢出

你提到的直接计算行的递推公式是对的:C(n, k) = C(n, k-1) * (n - k + 1) / k,但问题是直接算的话数值会爆炸。我们的解决办法是每一步都做约分+模10处理,绝对不让数值变大到超出64位的范围,甚至连32位都用不上。

另外,帕斯卡三角是对称的——C(n, k) = C(n, n-k),所以我们只需要计算前半行,后半行直接镜像复制就行,效率直接翻倍!


具体步骤(附Python代码)

核心逻辑是:

  1. 初始化结果数组,第一个元素固定为1(C(n, 0)=1
  2. 只计算前半行的元素,每一步都先约分分子分母,再处理模10的特殊情况(因为10=2×5,这俩因子没法直接用逆元,得单独抽出来抵消)
  3. 对称位置直接复制结果,省一半时间
import math

def get_pascal_row_mod10(n):
    res = [1] * (n + 1)
    # 只算前半部分,后半部分直接镜像
    for k in range(1, (n // 2) + 1):
        prev_val = res[k-1]
        numerator = n - k + 1
        denominator = k
        
        # 第一步:先约分分子分母的最大公约数,把数值压到最小
        gcd_val = math.gcd(numerator, denominator)
        numerator //= gcd_val
        denominator //= gcd_val
        
        # 第二步:提取并抵消分子分母中的2、5因子(因为10的质因子是这俩,直接用逆元不行)
        cnt2_num, cnt5_num = 0, 0
        while numerator % 2 == 0:
            numerator //= 2
            cnt2_num += 1
        while numerator % 5 == 0:
            numerator //= 5
            cnt5_num += 1
            
        cnt2_den, cnt5_den = 0, 0
        while denominator % 2 == 0:
            denominator //= 2
            cnt2_den += 1
        while denominator % 5 == 0:
            denominator //= 5
            cnt5_den += 1
            
        # 抵消相同数量的2、5因子
        remaining_2 = cnt2_num - cnt2_den
        remaining_5 = cnt5_num - cnt5_den
        
        # 第三步:分母现在和10互质,直接用预存的逆元表(不用算,固定的)
        inv_denominator = {1:1, 3:7, 7:3, 9:9}[denominator]
        
        # 计算当前值的基础部分:前一个结果 × 约分后的分子 × 逆元 模10
        current_val = (prev_val * numerator) % 10
        current_val = (current_val * inv_denominator) % 10
        
        # 处理剩下的2、5因子
        for _ in range(remaining_2):
            current_val = (current_val * 2) % 10
        for _ in range(remaining_5):
            current_val = (current_val * 5) % 10
            
        # 赋值给当前位置和对称位置
        res[k] = current_val
        res[n - k] = current_val
    return res

关键细节解释

  • 为什么要单独处理2和5? 因为模运算里的除法需要逆元,但只有当分母和模数互质时逆元才存在。2和5都和10不互质,所以得先把这些因子抽出来抵消,剩下的分母就只能是1、3、7、9,这四个数的模10逆元是固定的,直接查表就行。
  • 为什么不会溢出? 每一步我们都做了约分,分子分母都是很小的数(最多是n的大小),乘完之后马上模10,数值永远不会超过10×10=100,别说64位,8位都够!
  • 时间复杂度? 因为只算前半行,实际是O(n/2),属于线性时间范畴,比预算所有行快多了。

举个栗子测试

比如算n=5的行,结果应该是[1,5,0,0,5,1],跑代码的话:

  • k=1:计算得5,对称位置4也赋值5
  • k=2:计算得0,对称位置3也赋值0
  • 直接返回结果,完美匹配预期!

要是你用C++、Java这类强类型语言,只需要把math.gcd换成语言自带的gcd函数,逆元表还是一样的,核心逻辑完全通用~

火山引擎 最新活动