080. 平方根的小数表示(Square root digital expansion)

众所周知如果一个整数的平方根不是一个整数,那么这个平方根就是一个无理数,这种平方根的小数表示是无限不循环的。二的平方根是\(1.41421356237309504880...\),它的前一百位数字的和是475。对于前一百个自然数,如果它的平方根是无理数,求这些无理数的小数表示的前一百之和的和。

分析:计算平方根的方法有很多,如经典的牛顿迭代法,但这些方法都会使用浮点数,从而容易出现近似误差的问题,在求小数表示的数位之和时容易出现错误。经过查阅资料,在这篇论文中我发现一个非常简单易懂的计算任意精度的平方根的方法,这种方法虽然收敛速度要慢于牛顿迭代法,但最大的优点是只需要通过整数的加减就可以得到答案,从而完全避免浮点误差问题。对算法的具体介绍和背后的原理大家可以参见论文,我这里只简单描述以下算法的实现步骤:

对于前一百个自然数中的非完全平方数,依次求其小数表示的前一百个有效数字之和,然后把这些和加总起来,即为题目所求,代码如下:

# time cost = 42.1 ms ± 549 µs

def jarvis_sqrt_sum(n,prec=100):
    a,b = 5*n,5
    while len(str(b)) <= prec+3:
        if a >= b:
            a,b = a-b,b+10
        else:
            a,b = a*100,(b-b%10)*10+b%10
    return sum([int(x) for x in str(b)[:prec]])

def main():
    numbers = set(range(2,100))-{x**2 for x in range(2,10)}
    ans = 0
    for i in numbers:
        ans += jarvis_sqrt_sum(i)
    return ans