质数查找:暴力 埃氏筛 欧拉筛

什么是质数

质数是大于1的自然数,除了1和它本身之外,没有其它正因数的数。换句话说,一个大于1的自然数,只能被1和它本身整除,没有其他正整数可以整除它。质数是数论中重要且基础的概念,它在数论和密码学等领域有着广泛的应用。

什么是合数

合数(Composite Number)是指在大于1的自然数中,除了1和它本身以外,还有其他因数的数。

暴力查找质数

 既然知道了素数只能被1和自身整除,那么通过迭代遍历的方式查找除1和本身外,是否有数能将该数整除。

以下为代码实现,通过循环判断数i是否为一个质数。

    for j in range(2, int(pow(i, 0.5)) + 1):
        if i % j == 0:
            break
    else:
        cnt += 1

Q:在暴力搜索寻找质数中,为何要将for循环的上限设置为根号i加一

A: 如果一个数 i 是合数,那么它一定可以被分解为两个因子 ab,其中 ab 均小于或等于根号i。因此,在判断 i 是否为质数时,只需判断 i 能否被小于或等于根号i的数整除,就可以确定 i 是否为质数。这种操作一定程度上降低了程序的时间开销。

埃氏筛查找质数

若一个数i为素数,则它的倍数2*i,3*i,4*i...n*i一定不是素数。

埃氏筛(Sieve of Eratosthenes)是一种用于筛选出一定范围内所有质数的算法。这个算法的基本思想是从2开始不断遍历每个数,将一个质数所有的倍数标记为非质数,最终剩下未被标记的数就是质数。

    # 埃氏筛
    li = [0 for _ in range(10**8+1)]
    li[0],li[1] = 1,1
    for i in range(2,10**8+1):
        if li[i] == 0:
            for j in range(i+i,10**8,i):
                li[j] = 1

 在这段代码中我们通过埃氏筛查找了[0,10**8]范围内的质数,首先我们定义了存放质数的结果数组res和状态数组li,li内所有元素初始化为0,然后将li中0和1位置的值修改为1,在外层循环中我们首先判断li[i]是否为0,即是否是质数,如果是,将其添加至res中,并进入内层循环。

在内层循环中,i有倍数j,j初始为2i,将所有li[j]修改为1,表示j不是质数。

这个视频可以帮你更好的理解埃氏筛:

http://【埃拉托色尼筛法寻找质数】 https://www.bilibili.com/video/BV1rw411u7Mp/?share_source=copy_web&vd_source=af1af512c6fc41b01eb432ec33b76873

欧拉筛查找质数

在埃氏筛中,我们将质数的倍数n*i(n*i > i)通过筛选置为非质数,但是这种筛选存在着重复操作。

如在i = 2时,其倍数2,4,6,8,10,12等数被筛为非质数。

 在i = 3时,其倍数6,9,12,15,18,21等数被筛为非质数。

 我们发现,在两次筛选中,存在着重复的操作,即6和12被筛选了超过一次,重复的筛选会造成额外的时间开销。

欧拉筛在埃氏筛的基础上作出的改进即为使每个合数被筛选的次数只有一次。

    n = 10**8
    li = [0 for _ in range(n+1)]
    li[0],li[1] = 1,1
    res = []
    s = 0
    for i in range(2, n + 1):
        if not li[i]:
            res.append(i)
        s = len(res)
        for j in range(s):
            if i * res[j] > n:
                break
            li[i * res[j]] = 1
            if i % res[j] == 0:
                break

当i % res[j] == 0时, 意味着ires[j]的倍数, 也就是说res[j]i的最小质因子不需要再重复标记, 因此可以跳出内层循环, 进行下一个数的筛选。

例题 13届蓝桥杯python研究生组决赛  小蓝做实验

 

def ques_d():
    nums = []
    cnt = 0
    # with open('primes.txt') as f:
    f = open('primes.txt')
    for l in f:
        nums.append(int(l))
    # print(li)

    #暴力
    # for i in nums:
    #     for j in range(2,int(pow(i,0.5))+1):
    #         if i % j == 0:
    #             break
    #     else:
    #         cnt += 1


    # # 埃氏筛
    # res = []
    # li = [0 for _ in range(10**8+1)]
    # li[0],li[1] = 1,1
    # for i in range(2,10**8+1):
    #     if li[i] == 0:
    #         res.append(li[i])
    #         for j in range(i+i,10**8,i):
    #             li[j] = 1
    # for n in nums:
    #     if li[n] == 0:
    #         cnt += 1

    #欧拉筛

    n = 10**8
    li = [0 for _ in range(n+1)]
    li[0],li[1] = 1,1
    res = []
    s = 0
    for i in range(2, n + 1):
        if li[i] == 0:
            res.append(i)
        s = len(res)
        for j in range(s):
            if i * res[j] > n:
                break
            li[i * res[j]] = 1
            if i % res[j] == 0:
                break
    cnt = 0
    for n_ in nums:
        if li[n_] == 0:
            cnt += 1


    return cnt

print(ques_d())

  • 21
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值