引导deepseek实现一个高效算自然对数的底e的高精度算法 - l1t1/note GitHub Wiki

首个实现已经很高效,但后面理解我的改进有偏差,花了二个提示纠偏

#引导deepseek实现一个高效算自然对数的底e的高精度算法

from decimal import Decimal, getcontext
import sys
sys.set_int_max_str_digits(0)
import math
import time

#请用python写一个计算自然对数的底e的高精度算法

def calculate_e(precision, max_iterations=1000):
    """
    高精度计算自然对数的底e
    
    参数:
        precision: 所需的小数位数
        max_iterations: 最大迭代次数(防止无限循环)
    
    返回:
        Decimal类型的e值
    """
    # 设置Decimal的精度(比需要的多几位以避免舍入误差)
    getcontext().prec = precision + 2
    
    e = Decimal(0)
    factorial = Decimal(1)
    
    for n in range(max_iterations):
        term = Decimal(1) / factorial
        e += term
        
        # 检查收敛性 - 如果新项小于10^(-precision),可以停止
        if term < Decimal(10) ** (-precision-1):
            break
            
        factorial *= (n + 1)
    
    # 调整精度到所需的小数位数
    getcontext().prec = precision
    return +e  # +操作符应用当前精度

#按照你写的e的泰勒级数展开式,预计算从1到所需精度对应的n!,然后通分,计算分子,然后把分子乘以10的对应次方,然后divmod求整数解,然后点小数点,全过程都用整数运算会不会快一点?用python实现这个思路

def calculate_e_integer(digits):
    """
    使用纯整数运算计算e到指定小数位数
    
    参数:
        digits: 需要计算的小数位数
    
    返回:
        e的字符串表示,包含小数点
    """
    # 预计算足够的阶乘项
    max_terms = digits * 2  # 安全估计需要的项数
    factorials = [1] * (max_terms + 1)
    for i in range(1, max_terms + 1):
        factorials[i] = factorials[i-1] * i
    
    # 计算通分后的分子总和 (分母为max_factorial)
    max_factorial = factorials[max_terms]
    numerator = 0
    for n in range(max_terms + 1):
        numerator += max_factorial // factorials[n]
    
    # 计算整数部分和小数部分
    int_part = numerator // max_factorial
    remainder = numerator % max_factorial
    
    # 计算小数部分
    decimal_digits = []
    for _ in range(digits):
        remainder *= 10
        digit = remainder // max_factorial
        decimal_digits.append(str(digit))
        remainder = remainder % max_factorial
    
    # 组合结果
    return f"{int_part}.{''.join(decimal_digits)}"

#这个版本性能不太高,可能与计算分子用了除法numerator += max_factorial // factorials[n]有关,能否用1+n+n(n-1)+n(n-1)(n-2) +....+n! 的方法算分子?请用python实现

def calculate_e_optimized(digits):
    """
    使用优化后的整数运算计算e到指定小数位数
    
    参数:
        digits: 需要计算的小数位数
    
    返回:
        e的字符串表示,包含小数点
    """
    # 动态确定需要的项数 (n)
    # 根据泰勒级数余项估计: 1/(n+1)! < 10^(-digits)
    n = 1
    while True:
        # 使用斯特林公式近似估算阶乘
        # ln(n!) ≈ n*ln(n) - n + 0.5*ln(2πn)
        # 我们想要 (n+1)! > 10^digits
        log_fact = (n+1)*math.log(n+1) - (n+1) + 0.5*math.log(2*math.pi*(n+1))
        if log_fact > digits * math.log(10):
            break
        n += 1
    
    # 预计算系数: 1, n, n(n-1), n(n-1)(n-2), ..., n!
    coefficients = [1] * (n + 1)
    for i in range(1, n + 1):
        coefficients[i] = coefficients[i-1] * (n - i + 1)
    
    # 计算分子总和
    numerator = 0
    for coeff in coefficients:
        numerator += coeff
    
    # 计算整数部分和小数部分
    int_part = numerator // coefficients[-1]  # coefficients[-1] = n!
    remainder = numerator % coefficients[-1]
    
    # 计算小数部分
    decimal_digits = []
    for _ in range(digits):
        remainder *= 10
        digit = remainder // coefficients[-1]
        decimal_digits.append(str(digit))
        remainder = remainder % coefficients[-1]
    
    return f"{int_part}.{''.join(decimal_digits)}"

#这个版本比原始Decimal版本快,但计算小数用了很多次除法,能否把分子一次性乘以10的所需位数次方,然后用商再除以这个数(实际上只需要把小数点放在第个2个数字左边)


def calculate_e_fast(digits):
    """
    使用优化后的整数运算计算e到指定小数位数
    通过一次性乘以10^digits来避免多次除法
    
    参数:
        digits: 需要计算的小数位数
    
    返回:
        e的字符串表示,包含小数点
    """
    # 动态确定需要的项数 (n)
    n = 1
    while True:
        log_fact = (n+1)*math.log(n+1) - (n+1) + 0.5*math.log(2*math.pi*(n+1))
        if log_fact > digits * math.log(10):
            break
        n += 1
    
    # 预计算系数: 1, n, n(n-1), n(n-1)(n-2), ..., n!
    coefficients = [1] * (n + 1)
    for i in range(1, n + 1):
        coefficients[i] = coefficients[i-1] * (n - i + 1)
    
    # 计算分子总和
    numerator = 0
    for coeff in coefficients:
        numerator += coeff
    
    # 一次性乘以10^digits
    power = 10 ** digits
    scaled_numerator = numerator * power
    
    # 计算整数部分和小数部分
    total = scaled_numerator // coefficients[-1]  # coefficients[-1] = n!
    int_part = total // power
    decimal_part = total % power
    
    # 格式化为字符串,确保小数部分有正确的位数(前导零)
    return f"{int_part}.{str(decimal_part).zfill(digits)}"

# 测试
if __name__ == "__main__":
    import time
    
    digits = 10000  # 计算100位小数
    start_time = time.time()
    e_value = calculate_e_fast(digits)
    elapsed = time.time() - start_time
    
    print(f"e (精确到{digits}位小数):")
    print(str(e_value)[:102])
    print(f"\n计算耗时: {elapsed:.4f}秒")
    

    
    digits = 10000  # 计算10000位小数
    start_time = time.time()
    e_value = calculate_e_optimized(digits)
    elapsed = time.time() - start_time
    
    print(f"e (精确到{digits}位小数):")
    print(str(e_value)[:102])
    print(f"\n计算耗时: {elapsed:.4f}秒")


    digits = 1000  # 计算1000位小数
    start_time = time.time()
    e_value = calculate_e_integer(digits)
    elapsed = time.time() - start_time
    
    print(f"e (精确到{digits}位小数):")
    print(str(e_value)[:102])
    print(f"\n计算耗时: {elapsed:.4f}秒")
    

    precision = 10000
    start_time = time.time()
    e_value = calculate_e(precision)
    elapsed = time.time() - start_time

    print(f"e (精确到{precision}位小数):")
    print(str(e_value)[:102])
    print(f"\n计算耗时: {elapsed:.4f}秒")
    
"""
e (精确到10000位小数):
2.7182818284590452353602874713526624977572470936999595749669676277240766303535475945713821785251664274

计算耗时: 0.0176秒
e (精确到10000位小数):
2.7182818284590452353602874713526624977572470936999595749669676277240766303535475945713821785251664274

计算耗时: 0.0680秒
e (精确到1000位小数):
2.7182818284590452353602874713526624977572470936999595749669676277240766303535475945713821785251664274

计算耗时: 0.1171秒
e (精确到10000位小数):
2.7182818284590452353602874713526624977572470936999595749669676277240766303535475945713821785251664274

计算耗时: 0.2586秒
"""