码迷,mamicode.com
首页 > 其他好文 > 详细

快速求素数和

时间:2015-04-20 01:44:52      阅读:122      评论:0      收藏:0      [点我收藏+]

标签:

在知乎上看到一个问题:求十亿内所有质数的和,怎么做最快?

记录一下第一回答

 

定义技术分享技术分享技术分享所有整数中,在普通筛法中外层循环筛完技术分享时仍然幸存的数的和。因此这些数要不本身是素数,要不其最小的素因子也大于技术分享。因此我们需要求的是技术分享,其中技术分享是十亿。

为了计算技术分享,先考虑几个特殊情况

  1. 技术分享。此时所有数都还没有被筛掉,所以技术分享
  2. 技术分享不是素数。因为筛法中技术分享早已被别的数筛掉,所以在这步什么都不会做,所以此时技术分享
  3. 技术分享是素数,但是技术分享。因为每个合数都一定有一个不超过其平方根的素因子,如果筛到技术分享时还没筛掉一个数,那么筛到技术分享时这个数也还在。所以此时也有技术分享


现在考虑最后一种稍微麻烦些的情况:技术分享是素数,且技术分享
此时,我们要用素数技术分享去筛掉剩下的那些数中技术分享的倍数。注意到现在还剩下的合数都没有小于技术分享的素因子。因此有:
技术分享

后面那项中提取公共因子技术分享,有:
技术分享

因为技术分享整除技术分享,稍微变形一下,令技术分享,有:
技术分享

注意到一开始提到的技术分享的定义(“这些数要不本身是素数,要不其最小的素因子也大于(注意!)技术分享”),此时技术分享后面这项可以用技术分享来表达:
技术分享

再用技术分享替换素数和得到最终表达式:
技术分享


我们最终的结果是技术分享。计算时可以使用记忆化,也可以直接自底向上动态规划。
至于算法的复杂度就留作练习,是低于以上任何一种暴力方法的。

 

技术分享
def P10(n):
    r = int(n ** 0.5)
    # assert r*r <= n and (r+1)**2 > n
    V = [n // i for i in range(1, r + 1)]
    #    print V
    V += list(range(V[-1] - 1, 0, -1))
    #    print V
    S = {i: i * (i + 1) // 2 - 1 for i in V}
    #    print S
    for p in range(2, r + 1):
        if S[p] > S[p - 1]:  # p is prime
            sp = S[p - 1]  # sum of primes smaller than p
            p2 = p * p
            for v in V:
                if v < p2: break
                S[v] -= p * (S[v // p] - sp)
    return S[n]


N = 1000000000
print P10(N)
View Code

1e9的数据能在1s能跑出来, 真是神一样的算法, 神一样的代码。

快速求素数和

标签:

原文地址:http://www.cnblogs.com/yeahpeng/p/4440481.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!