力扣——204. 计数质数

题目

在这里插入图片描述

python代码

1.直接法

import timeit

def countPrimes(n):
    '''按照质数的定义,用常规的方法来取质数 '''
    primesList = []
    for i in range(2, n+1):
        # print(i)
        flag = True
        for divNum in range(2, i): # 从2到i-1,一个一个的除,如果有余数为0的状况,可以确定不是质数,退出循环
            if i % divNum == 0:
                flag = False
                break
        if flag:
            if i < n:
                primesList.append(i)
    # print(primesList)
    return len(primesList)

if __name__ == "__main__":
    print(timeit.timeit("countPrimes(10000)", "from __main__ import countPrimes", number=10))
    print(countPrimes(10000))

超出时间范围:
在这里插入图片描述

2.倍数筛选

根据质数的定义,非质数(也就是合数)可以分解成至少是两个非零非1数的乘积。也就是说,一个合数x是可以分解成x=a*b的这种形式,其中a,b都不能等于0或者1.那么。a的倍数和b的倍数都不可能是质数。
因此,获取质数的方法就很简单了。先将从2到n的所有数都列出来。然后筛除所有小于等于n/2的数字的倍数。为什么只需要筛选小于等于n/2 的数字呢?是因为n/2的倍数已经大于n了,大于n的数字已经与题目要求无关了。

import timeit

def countPrimes(n):
    '''倍数筛选法获取质数 '''
    if n <= 2:
        return 0
    elif n == 3:
        return 1
    elif n == 4:
        return 2
    primesList = list(range(2, n))
    # print(primesList)
    multList = []
    for i in range(0, n//2 + 1):
        multList += list(range(primesList[i], n+1))[::primesList[i]][1::]
    primesList = list(set(primesList) - set(multList)) # 利用集合过滤掉倍数
    primesList.sort()
    # print(primesList)
    return len(primesList)

if __name__ == "__main__":
    print(timeit.timeit("countPrimes(10000)", "from __main__ import countPrimes", number=10))
    print(countPrimes(10000))

超出时间范围
在这里插入图片描述

3.埃拉托斯特尼筛选

埃拉托斯特尼筛法简称为埃氏筛或爱氏筛,是一种由希腊数学家埃拉托斯特尼所提出的一种简单检定质数的算法。要得到自然数n以内的全部质数,必须把不大于根号n的所有质数的倍数剔除,剩下的就是质数。

从原理上看,埃拉托斯特尼筛法和倍数筛选法几乎是一样的。 稍有区别的地方就是剔除倍数的那些数到底是小于n/2还是根号n。很明显,在比较小时,可能差别还不是很大。n的值越大,这两者的区别越大,所需的时间可能是几何级的差距。

原理和倍数筛选法是一样的。一个合数n必定是2个自然数(这里无所谓是质数还是合数了)的乘积。这2个自然数必定是一个大于根号n、一个小于根号n,可以用归谬法来证明。因此只需要去除掉小于的倍数就足够了。以n为100000 为例,倍数筛选法需要筛选掉5000以内的倍数,而埃拉托斯特尼筛法只需要筛选掉100 以内的倍数,这绝对是几何级的差距。

从原理上来说,埃拉托斯特尼筛法比倍数筛选法要好很多。程序还可以更快一点。 比如,在倍数筛选法中,使用的是Python的集合来去除合数。这样做也没有问题。但仔细观察一下,在程序的开始,在筛选掉合数之前,所有的数字集合实际上就是一个线性的列表,而列表的index (下标)也是一个线性的列表。为什么非要去筛选数字集合呢?直接筛选下标不就可以了吗?将列表中所有的元素值都设置成True。在筛选时,将对应下标的值设置成False就可以了。最后只需要统计值为True的下标就可以了。这样做首先会省略集合运算,毕竟集合运算也是比较耗费时间的:然后用列表赋值替代了筛选的过程,节省了程序的运算时间。实际上赋的值是int. float. string 还是bool类型并不重要。这里使用bool类型也只是为了直观而已。

import timeit

def countPrimes(n):
    '''埃拉托斯特尼筛法获取质数 '''
    if n<3:
        return 0 # n<3时是没有质数的
    boolList = [True]*n
    boolList[0] = False
    boolList[1] = False
    for i in range(2, int(pow(n, 0.5)) + 1):
        if boolList[i]: #如果boolList[i]的值已经是False了,那说明在之前比较小的数的倍数已经筛选过一次了。可以不用再次筛选了。
            boolList[i::i] = [False]*len(boolList[i::i])
            boolList[i] = True
        #     print(i,boolList)
        # print('-------')

    # primesList = []
    # i = 0
    # while i < n:
    #     if boolList[i]:
    #         primesList.append(i)
    #     i += 1
    # print(primesList)
    return sum(boolList)

if __name__ == "__main__":
    print(timeit.timeit("countPrimes(10000)", "from __main__ import countPrimes", number=10))
    print(countPrimes(10000))

在这里插入图片描述
在这里插入图片描述

Guess you like

Origin blog.csdn.net/weixin_48994268/article/details/118675282