学习指引
序、专栏前言
本专栏开启,目的在于帮助大家更好的掌握学习Java
,特别是一些Java学习者
难以在网上找到系统地算法学习资料帮助自身入门算法,同时对于专栏内的内容有任何疑问都可在文章末尾添加我的微信给你进行一对一的讲解。
但最最主要的还是需要独立思考,对于本专栏的所有内容,能够进行完全掌握,自己完完全全将代码写过一遍,对于算法入门肯定是没有问题的。
算法的学习肯定不能缺少总结,这里我推荐大家可以到高校算法社区将学过的知识进行打卡,以此来进行巩固以及复习。
学好算法的唯一途径那一定是题海战略,大量练习的堆积才能练就一身本领。专栏的任何题目我将会从【题目描述】【解题思路】【模板代码】【代码解析】等四板块进行讲解。
一、什么是素数?
素数,其实就是我们常说的质数
,大家从小学开始肯定就接触过的,非常熟悉。质数是指在大于1的自然数中,除了1和它本身以外不再有其他因数的自然数。
反之,不是质数的数我们称之为合数
。
二、【例题1】
2、解题思路
我们直接从质数的定义出发,运用到前面文章讲过的判断a
是否为b
的倍数的知识。由于质数的因数只有
2
2
2个,就是
1
1
1和它本身。所以我们可以统计
n
n
n的因数个数进行判断。
3、模板代码
1、方法1
import java.util.Scanner;
public class Main{
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
int n=sc.nextInt();
int cnt=0;
for (int i = 1; i <=n; i++) {
if (n%i==0) cnt++;
}
System.out.println(cnt==2?"YES":"NO");
}
}
2、方法2
import java.util.Scanner;
public class test222 {
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
int n=sc.nextInt();
for (int i = 2; i <n; i++) {
if (n%i==0){
System.out.println("NO");
}
}
System.out.println("YES");
}
}
3、方法3(试除法)
import java.util.Scanner;
public class Main{
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
int n=sc.nextInt();
for (int i = 2; i <=n/i; i++) {
if (n%i==0){
System.out.println("NO");
return;
}
}
System.out.println("YES");
}
}
4 、代码解析
- ( 1 ) (1) (1)因为质数的因数个数只有两个,所以方法1我们可以去记录从 [ 1 , n ] [1,n] [1,n]有多少个数是 n n n的因数,看结果是否为 2 2 2。
-
(
2
)
(2)
(2)从质数的定义可知,在区间
[
2
,
n
−
1
]
[2,n-1]
[2,n−1]是不能有
n
n
n的因数的,所以当我们判断到该区间存在
n
n
n的因数,可以直接输出
NO
了,当然别忘记return
结束程序,否则程序会继续进行。当循环能完整跑完,说明该区间没有 n n n的因数,我们输出YES
。 - ( 3 ) (3) (3)两种方法在判断到质数时,效率一样。但当一个数是合数时,方法2明显可以更早结束循环。如果当我们需要在一个区间内筛选质数,明显方法2效率更佳,正整数中合数的个数是远远多于质数的。
-
(
3
)
(3)
(3)但前两种都不是效率最高的方法,我们通常判断质数都采用的是——
试除法
。我们都知道,当一个数i
是n
的因数时,那么n/i
也一定是n
的因数。所以我们在判断n
是否为质数时,我们不需要遍历区间 [ 2 , t − 1 ] [2,t-1] [2,t−1],只需要遍历 [ 2 , t ] [2,\sqrt{t}] [2,t]是否存在因数即可。这样的复杂度为 O ( n ) O(\sqrt{n}) O(n)。 -
(
4
)
(4)
(4)注意在
试除法
中,循环判断时我们这里写的是i<=n/i
,大家不要写成i<=Math.sqrt(n)
,因为每次判断时该函数都会被调用一次,效率很低。还有i*i<=n
也不太好,i*i
在i
的值太大了有可能超出int
的取值范围。
三、【例题2】
2、解题思路
我们结合第一问中判断质数的方法,把区间 [ 1 , t ] [1,t] [1,t]中的每个数都判断一遍即可。
3、模板代码
import java.util.Scanner;
public class test222 {
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
int n=sc.nextInt();
for (int i = 1; i <=n; i++) {
boolean flag=true;
for (int j = 2; j <=n/j; j++) {
if (n%i==0){
flag=false;
break;
}
}
if (flag)System.out.println("YES");
}
}
}
- ( 1 ) (1) (1)由于是筛选质数,所以对于区间 [ 1 , t ] [1,t] [1,t]的每个数都需要进行质数判定。
-
(
2
)
(2)
(2)因为上述讲过,试除法是效率最快的,所以我们使用试除法来判断。当我们运行到
14
14
14行代码时,并不知道是内层循环自然结束完毕还是
break
跳出来的。 -
(
3
)
(3)
(3)所以我们需要一个
boolean
变量flag
,当判断到当前的数为合数时,将其置为false
。这样在输出时如果flag
为true
说明循环是自然结束的,该数为质数我们进行输出。 -
(
3
)
(3)
(3)判断每个数的时间复杂度是
O
(
n
)
O(\sqrt{n})
O(n),总共有
n
n
n个数,所以总体的筛选效率为
O
(
n
n
)
O(n\sqrt{n})
O(nn)
四、结语
对于质数的筛选还有更高效的方法,有复杂度为 O ( n l o g l o g n ) O(nloglogn) O(nloglogn)的埃式筛和 O ( n ) O(n) O(n)的欧拉筛(也叫线性筛)。质数的判断与筛选范围十分广泛,是一个很重要的内容。对于初学者掌握本章内容即可,所以暂时不引入两种筛子讲解。
五、课后习题
序号 | 题目链接 | 难度评级 |
---|---|---|
1 | 质数筛 | 1 |
2 | 哥德巴赫猜想 | 2 |