204. 计数质数 (埃式筛法详解)——【Leetcode每日一题】

这篇具有很好参考价值的文章主要介绍了204. 计数质数 (埃式筛法详解)——【Leetcode每日一题】。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

素数最朴素判断思路:(一般会超时)

对正整数 n,如果用 2 到 n \sqrt{n} n 之间的所有整数去除,均无法整除,则 n 为素数又称为质数。

为什么到 n \sqrt{n} n 就可以了,因为因数如果存在一定是成对出现的,如果存在小于根号n的因数,那么n除以它一定大于根号n。

首先要先知道以下几个知识点:

1、素数分解
  • 每一个数 都可以分解成 素数的乘积,且这种分解是唯一的,例如: 84 = 2 2 ∗ 3 1 ∗ 5 0 ∗ 7 1 ∗ 1 1 0 ∗ 1 3 0 ∗ 1 7 0 ∗ … 84 = 2^2 * 3^1 * 5^0 * 7^1 * 11^0 * 13^0 * 17^0 * … 84=22315071110130170
2、整除
  • x = 2 m 0 ∗ 3 m 1 ∗ 5 m 2 ∗ 7 m 3 ∗ 1 1 m 4 ∗ … x = 2^{m0} * 3^{m1} * 5^{m2} * 7^{m3} * 11^{m4} * … x=2m03m15m27m311m4
  • y = 2 n 0 ∗ 3 n 1 ∗ 5 n 2 ∗ 7 n 3 ∗ 1 1 n 4 ∗ … y = 2^{n0} * 3^{n1} * 5^{n2} * 7^{n3} * 11^{n4} * … y=2n03n15n27n311n4

如果 x x x 整除 y y y y y y mod x x x == 0),则对于所有 i i i m i < = n i m^i <= n^i mi<=ni

3、最大公约数最小公倍数
  • x x x y y y最大公约数为: g c d ( x , y ) = 2 m i n ( m 0 , n 0 ) ∗ 3 m i n ( m 1 , n 1 ) ∗ 5 m i n ( m 2 , n 2 ) ∗ . . . gcd(x,y) = 2^{min(m0,n0)} * 3^{min(m1,n1)} * 5^{min(m2,n2)} * ... gcd(x,y)=2min(m0,n0)3min(m1,n1)5min(m2,n2)...

    //辗转相除法
    int gcd(int a, int b) {
    	return b == 0 ? a : gcd(b, a % b);
    }
    
  • x x x y y y最小公倍数为: l c m ( x , y ) = 2 m a x ( m 0 , n 0 ) ∗ 3 m a x ( m 1 , n 1 ) ∗ 5 m a x ( m 2 , n 2 ) ∗ . . . lcm(x,y) = 2^{max(m0,n0)} * 3^{max(m1,n1)} * 5^{max(m2,n2)} * ... lcm(x,y)=2max(m0,n0)3max(m1,n1)5max(m2,n2)...

    //最小公倍数为两数的乘积除以最大公约数。
    int lcm(int a, int b) {
    	return a * b / gcd(a, b);
    }
    
4、使用位操作和减法求解最大公约数

对于 a 和 b 的最大公约数 f(a, b),有:

  • 如果 a 和 b 均为偶数,f(a, b) = 2*f(a/2, b/2);
  • 如果 a 是偶数 b 是奇数,f(a, b) = f(a/2, b);
  • 如果 b 是偶数 a 是奇数,f(a, b) = f(a, b/2);
  • 如果 a 和 b 均为奇数,f(a, b) = f(b, a-b);

乘 2 和除 2 都可以转换为移位操作。

public int gcd(int a, int b) {
    if (a < b) {
        return gcd(b, a);
    }
    if (b == 0) {
        return a;
    }
    boolean isAEven = isEven(a), isBEven = isEven(b);
    if (isAEven && isBEven) {
        return 2 * gcd(a >> 1, b >> 1);
    } else if (isAEven && !isBEven) {
        return gcd(a >> 1, b);
    } else if (!isAEven && isBEven) {
        return gcd(a, b >> 1);
    } else {
        return gcd(b, a - b);
    }
}

204. 计数质数

给定整数 n ,返回 所有小于非负整数 n 的质数的数量 。

示例 1:

输入:n = 10
输出:4
解释:小于 10 的质数一共有 4 个, 它们是 2, 3, 5, 7 。

示例 2:

输入:n = 0
输出:0

示例 3:

输入:n = 1
输出:0

提示:
  • 0 < = n < = 5 ∗ 1 0 6 0 <= n <= 5 * 10^6 0<=n<=5106

思路:(埃式筛法)

埃式筛法

埃拉托斯特尼算法是希腊数学家埃拉托斯特尼(阿基米德的好友)发明的用来筛选素数的方法,为了方便我们一般简称为埃式筛法或者筛法。

  • 埃式筛法的思路非常简单,就是用已经筛选出来的素数过滤所有能够被它整除的数
  • 这些素数就像是筛子一样去过滤自然数,最后被筛剩下的数自然就是 不能被前面素数整除的数,根据素数的定义,这些剩下的数也是素数。

我们要筛选出n以内的所有素数:
1、我们知道2是最小的素数,我们先用2可以筛掉所有的偶数。
2、然后往后遍历到3,3是被2筛剩下的第一个数,也是素数,我们再用3去筛除所有能被3整除的数。
3、筛完之后我们继续往后遍历,第一个遇到的数是5,所以5也是素数,我们再重复以上的过程,直到遍历结束为止。
……
结束的时候,我们就获得了n以内的所有素数。

极致优化(线性筛)

  • 筛法的复杂度已经非常近似 O ( n ) O(n) O(n)了,因为即使在n很大的时候,经过两次ln的计算,也非常近似常数了,实际上在绝大多数使用场景当中,上面的算法已经足够应用了。
  • 但是仍然有大牛不知满足,继续对算法做出了优化,将其优化到了
    的复杂度。虽然从效率上来看并没有数量级的提升,但是应用到的思想非常巧妙,值得我们学习。

比较明显地可以看出来,对于一个合数而言,它可能会被多个素数筛去。比如12,它有23这两个素因数,那么它就会被置为两次;

这就带来了额外的开销,如果对于每一个合数我们只更新一次,那么是不是就能优化到 O ( n ) O(n) O(n)了呢?

  • 这里要用到上面素数分解的定理,就是每个合数分解质因数的结果是唯一的。既然是唯一的,那么一定可以找到最小的质因数,如果我们能够保证一个合数只会被它最小的质因数更新为True,那么整个优化就完成了。

那我们具体怎么做呢?其实也不难:
1、我们假设整数n的最小质因数是m
2、那么我们用小于m的素数i乘上n可以得到一个合数。我们将这个合数消除;
3、对于这个合数而言,i 一定是它最小的质因数。因为它等于i * nn最小的质因数是mi 又小于m,所以i是它最小的质因数。
……
我们用这样的方法来生成消除的合数,这样来保证每个合数只会被它最小的质因数消除。

代码:(Java)

public class CountPrimes {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		int n = 10;
		System.out.println(countPrimes(n));
	}
	public static int countPrimes(int n) {
		if(n <= 1) {
			return 0;
		}
		int count = 0; //个数
		boolean[] notPrimes = new boolean[n + 1]; //数组存储是否为素数
		
		for(int i = 2; i < n; i++) {
			if(notPrimes[i]) {
				continue;
			}
			count++;
			//i是素数
			//从 i * i开始(如果 k < i ,那么 k*i 在之前就已经去除过了)
			for(long j = (long)i * i; j < n; j += i) {
				notPrimes[(int) j] = true;
			}
		}
		return count;
    }
}

极致优化(线性筛)

import java.util.ArrayList;
import java.util.List;

public class CountPrimes {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		int n = 10;
		System.out.println(countPrimes(n));
	}
	public static int countPrimes(int n) {
		if(n <= 1) {
			return 0;
		}
		
		boolean[] notPrimes = new boolean[n + 1]; 
		List<Integer> primes = new ArrayList<Integer>();
		
		for(int i = 2; i < n; i++) {
			if(!notPrimes[i]) {//如果是素数则加入primes
				primes.add(i);
			}
			for(int j = 0; j < primes.size() && i * primes.get(j) < n; j++) {
				notPrimes[i * primes.get(j)] = true;
				if(i % primes.get(j) == 0) {// 到 合数i 的最小质数时,停止
					break;
				}
			}
		}
		return primes.size();
    }
}
运行结果:

204. 计数质数 (埃式筛法详解)——【Leetcode每日一题】

复杂度分析:

埃式筛法

  • 时间复杂度 O ( n ∗ l n l n ( n ) ) O(n*lnln(n)) O(nlnln(n)),我们一共有了两层循环,最外面一层循环固定是遍历n次。而里面的这一层循环遍历的次数一直在变化,并且它的运算次数和素数的大小相关,看起来似乎不太方便计算。实际上是可以的,根据素数分布定理以及一系列复杂的运算.
  • 空间复杂度 O ( n ) O(n) O(n),需要开辟长度为n的数组,n为给定的整数

极致优化(线性筛)

  • 时间复杂度 O ( n ) O(n) O(n),赋值的次数减少了.
  • 空间复杂度 O ( n ) O(n) O(n).
注:仅供学习参考, 如有不足,欢迎指正!

题目来源:力扣。文章来源地址https://www.toymoban.com/news/detail-404311.html

到了这里,关于204. 计数质数 (埃式筛法详解)——【Leetcode每日一题】的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • LeetCode 周赛 340,质数 / 前缀和 / 极大化最小值 / 最短路 / 平衡二叉树

    本文已收录到 AndroidFamily,技术和职场问题,请关注公众号 [彭旭锐] 提问。 大家好,我是小彭。 上周跟大家讲到小彭文章风格的问题,和一些朋友聊过以后,至少在算法题解方面确定了小彭的风格。虽然竞赛算法题的文章受众非常小,但却有很多像我一样的初学者,他们有

    2023年04月14日
    浏览(29)
  • 【Py/Java/C++三种语言详解】LeetCode每日一题240117【哈希集合】LeetCode2744、最大字符串匹配数目

    LeetCode2744、最大字符串匹配数目 给你一个下标从 0 开始的数组 words ,数组中包含 互不相同 的字符串。 如果字符串 words[i] 与字符串 words[j] 满足以下条件,我们称它们可以匹配: 字符串 words[i] 等于 words[j] 的反转字符串。 0 = i j words.length 请你返回数组 words 中的 最大 匹配数

    2024年01月18日
    浏览(40)
  • 【Py/Java/C++三种语言详解】LeetCode每日一题240109【动态规划】LeetCode2707题、字符串中的额外字符

    给你一个下标从 0 开始的字符串 s 和一个单词字典 dictionary 。你需要将 s 分割成若干个 互不重叠 的子字符串,每个子字符串都在 dictionary 中出现过。 s 中可能会有一些 额外的字符 不在任何子字符串中。 请你采取最优策略分割 s ,使剩下的字符 最少 。 示例 1: 输入 :s =

    2024年01月16日
    浏览(35)
  • 【Py/Java/C++三种语言详解】LeetCode每日一题240216【二叉树BFS】LeetCode103、二叉树的层序遍历II

    有LeetCode交流群/华为OD考试扣扣交流群可加: 948025485 可上全网独家的 欧弟OJ系统 练习华子OD、大厂真题 绿色聊天软件戳 od1336 了解算法冲刺训练 LeetCode103、二叉树的锯齿形层序遍历 给你二叉树的根节点 root ,返回其节点值的 锯齿形层序遍历 。(即先从左往右,再从右往左进

    2024年02月20日
    浏览(31)
  • leetcode2529-正整数和负整数的最大计数

    题目: 给你一个按 非递减顺序 排列的数组 nums ,返回正整数数目和负整数数目中的最大值。 换句话讲,如果 nums 中正整数的数目是 pos ,而负整数的数目是 neg ,返回 pos 和 neg 二者中的最大值。 注意: 0 既不是正整数也不是负整数。 示例 1: 示例 2: 示例 3: 分析: 这道题

    2024年04月14日
    浏览(21)
  • 【算法每日一练]-图论(保姆级教程 篇4(最短路,分层图) #最短路计数 #社交网络 #公园 #飞行路线 # 第二短路

    目录 今天知识点   di和sp求到每个点的最短路数  floyd求到点的最短路数和经过点的最短路数 求三点最短距离 每个点有多个状态,建立分层图 求第二短路 题目:最短路计数 思路: 题目:社交网络 思路: 题目:公园 思路: 题目:飞行路线  思路: 题目:第二短路 思路:

    2024年02月04日
    浏览(32)
  • 力扣(leetcode)第696题计数二进制字串(Python)

    题目链接:696.计数二进制字串 给定一个字符串 s,统计并返回具有相同数量 0 和 1 的非空(连续)子字符串的数量,并且这些子字符串中的所有 0 和所有 1 都是成组连续的。 重复出现(不同位置)的子串也要统计它们出现的次数。 示例 1: 输入:s = “00110011” 输出:6 解释

    2024年01月20日
    浏览(26)
  • LeetCode 1921. Eliminate Maximum Number of Monsters【贪心,计数排序】1527

    本文属于「征服LeetCode」系列文章之一,这一系列正式开始于2021/08/12。由于LeetCode上部分题目有锁,本系列将至少持续到刷完所有无锁题之日为止;由于LeetCode还在不断地创建新题,本系列的终止日期可能是永远。在这一系列刷题文章中,我不仅会讲解多种解题思路及其优化,

    2024年02月09日
    浏览(39)
  • LeetCode 周赛 342(2023/04/23)容斥原理、计数排序、滑动窗口、子数组 GCB

    本文已收录到 AndroidFamily,技术和职场问题,请关注公众号 [彭旭锐] 提问。 大家好,我是小彭。 前天刚举办 2023 年力扣杯个人 SOLO 赛,昨天周赛就出了一场 Easy - Easy - Medium - Medium 的水场,不得不说 LeetCode 是懂礼数的 😁。 接下来,请你跟着小彭的思路,一步步将问题做难,

    2023年04月24日
    浏览(29)
  • 欧拉函数和线性筛法:AcWing 874. 筛法求欧拉函数

    1. 属于是先手推数学式子,然后代码比较简单的题目 2. 线性筛法,之前接触过一道类似的线性筛法的题目:线性筛法 3. 线性筛法是一个算法模板 顺便写一下这一道题目的笔记:AcWing 868. 筛质数 4. 下面详细分析一下线性筛法之外的数学部分的内容 5. 从1一直到某一个质因子的

    2024年02月08日
    浏览(24)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包