算法-如何求解质数
1、判断某个数是否为质数
- 质数的定义
- 所有的偶数都不是质数
- 数学定理
- 已存在的质数表
1.1、质数的定义
定理
总所周知,质数(又称素数)是指在大于 1 的自然数中,除了 1 和它本身以外不再有其他因数的自然数,例如, 7 = 1 * 7
,7只有 1和7这两个因素,所以 7 是一个质数;而 8 = 1 * 8 = 2 * 4
,它的因数有 1、2、4、8,总共有4个,所以 8 不是质数。我们的代码逻辑可以以质数的定义作为切入点。
假设要判断的数字是 x,从 2 开始,一般判断到 x - 1 为止,如果中途没有任何一个数被 x 整除,那么就可以判断 x 是一个质数,反之,x 肯定不是质数。
C
/** PrimeNumber.c -- 判断某个数是否为素数 */
#include <stdio.h> /* 标准输入输出函数库 */
#include <string.h> /* 字符串函数库 */
/******************* 主函数 *******************/
int main(void)
{
// 定义一个 int 变量 x 用于接收要判断的数
int x;
// 提示性输出
puts("请输入一个数:");
// 1.读取要判断的整型数字 x
scanf("%d", &x);
// 定义 isPrime 保存判断的结果
int isPrime = 1, count = 0, i;
// 2.从 2 到 x - 1 循环进行判断
for(i = 2; i < x; i++) {
// 计数器 + 1
count++;
// 如果 x 能被整除,说明不是其质数,直接跳出循环即可
if(x%i == 0) {
isPrime = 0;
break;
}
}
// 打印循环次数
printf("总共循环了%d次\n", count);
// 3.输出循环的判断结果
if(isPrime) {
printf("%d是一个素数\n", x);
} else {
printf("%d不是一个素数\n", x);
}
return 0;
}
Java
import java.util.Scanner;
/**
* 判断某个数是否为素数
*
* @author wl
*/
public class PrimeNumber {
public static void main(String[] args) {
// 创建一个输入扫描仪用于从标准系统输入(控制台)中读取数据
Scanner in = new Scanner(System.in);
// 提示性输出
System.out.println("请输入一个数:");
// 1.定义 x 并使用从标准系统输入中读取的数据进行初始化
int x = in.nextInt();
int count = 0;
// 定义布尔变量isPrime,保存判断的结果
boolean isPrime = true;
// 2.循环从 2 到 x - 1 进行判断
for(int i = 2; i < x; i++) {
// 计数器 + 1
count ++;
// 如果 x 能被整除,说明不是质数,直接跳出循环即可
if(x%i == 0) {
isPrime = false;
break;
}
}
// 打印循环次数
System.out.println("方法总共循环了" + count + "次");
// 3.输出循环的判断结果
if(isPrime) {
System.out.println(x + "是一个素数");
} else {
System.out.println(x + "不是一个素数");
}
}
}
OK!让我们来分析一下上面这段代码的时间复杂度,假设 x 是一个不是质数,循环 2 开始,最多要走 x - 1 次 ;如果 x 是一个素数,那么循环最少要走 x - 1 次,最终我们可以得出这段代码的时间复杂度约为
O
(
n
)
O(n)
O(n),可能你会想着这段代码还不错,但是别太高兴,如果 x 是一个非常大的数字呢,循环执行的次数就会很多,假设 x 的值是一百万,一千万,一千万亿呢? 那么循环就可能要运行近百万次,近千万次,近千万亿次,效率会非常非常差,那么有没有什么更好的方法?答案是有的
1.2、所有的偶数都不是质数
前言
从质数的定义,我们不难发现,除了奇数之外,所有的偶数都不可能是质数(除了 2)。比如 4、6、 8、9、10,这些一看都不是质数。因为偶数都是 2 的倍数,能够被 2 整除,所以偶数最少都拥有三个因数(除了 2 )。那么我们可以借此来改进我们的代码,如果 x 是偶数,那么肯定不是质数,循环可以从 3 开始,每次 +2,最少只要循环
(
x
−
3
)
2
+
1
\frac {(x - 3)} { 2 }\;+\;1
2(x−3)+1 遍,当 x 很大的时候就是
x
2
\frac {x} {2}
2x 次。
C
/** PrimeNumber.c -- 判断某个数是否为素数 */
#include <stdio.h> /* 标准输入输出函数库 */
#include <string.h> /* 字符串函数库 */
#include <stdbool.h> /* 布尔类型变量及函数库 */
/****************** 函数声明 ******************/
/*
判断某个数是否为质数
@param x 要判断的数字
@return 判断结果,如果是质数返回true,反之返回false
*/
bool isPrime01(int x);
/*
判断某个数是否为质数
@param x 要判断的数字
@return 判断结果,如果是质数返回true,反之返回false
*/
bool isPrime02(int x);
/******************* 主函数 *******************/
int main(void)
{
// 定义一个 int 变量 x 用于接收要判断的数
int x;
// 提示性输出
puts("请输入一个数:");
// 1.读取要判断的整型数字 x
scanf("%d", &x);
// 2.判断并输出结果
if((isPrime01(x), isPrime02(x))) {
printf("%d是一个素数\n", x);
} else {
printf("%d不是一个素数\n", x);
}
return 0;
}
/*
判断某个数是否为质数
@param x 要判断的数字
@return 判断结果,如果是质数返回true,反之返回false
*/
bool isPrime01(int x)
{
// 定义 count 变量记录循环次数
int count = 0;
// 定义布尔变量isPrime,保存判断的结果
bool isPrime = true;
// 从 2 到 x - 1 循环进行判断
int i;
for(i = 2; i < x; i++) {
count++;
// 如果 x 能被整除,说明不是质数,直接跳出循环即可
if(x%i == 0) {
isPrime = false;
break;
}
}
// 打印循环次数
printf("函数1总共循环了%d次\n", count);
return isPrime;
}
/*
判断某个数是否为质数
@param x 要判断的数字
@return 判断结果,如果是质数返回true,反之返回false
*/
bool isPrime02(int x)
{
// 定义 count 变量记录循环次数
int count = 0;
// 定义布尔变量isPrime,保存判断的结果
bool isPrime = true;
/*
当x等于1时或可以被2整除且不等于2时
说明其不是质数
*/
if(x == 1 || (x%2 == 0 && x != 2)) {
isPrime = false;
}
// 循环从 2 到 x - 1 进行判断
int i;
for(i = 3; i < x; i += 2) {
// 计数器 + 1
count++;
if(x%i == 0) {
isPrime = false;
break;
}
}
// 打印循环次数
printf("函数2总共循环了%d次\n", count);
return isPrime;
}
Java
/**
* 判断某个数是否为素数
*
* @author wl
*/
public class PrimeNumber {
public static void main(String[] args) {
// 创建一个输入扫描仪用于从标准系统输入(控制台)中读取数据
Scanner in = new Scanner(System.in);
// 提示性输出
System.out.println("请输入一个数:");
// 1.定义 x 并使用从标准系统输入中读取的数据进行初始化
int x = in.nextInt();
// 2.判断并输出结果
if(PrimeNumber.primeJudge01(x) && PrimeNumber.primeJudge02(x)) {
System.out.println(x + "是一个素数");
} else {
System.out.println(x + "不是一个素数");
}
}
/**
* 第一个方法
* 质数只能被1和自身整除
*
* @param x 要判断的数字
* @author wl
* @date 2022/8/2 22:55
* @return 判断的结果
*/
public static boolean primeJudge01(int x) {
// 定义 count 变量记录循环次数
int count = 0;
// 定义 布尔变量isPrime
boolean isPrime = true;
// 循环从 2 到 x - 1 进行判断
for(int i = 2; i < x; i++) {
count++;
if(x%i == 0) {
isPrime = false;
break;
}
}
System.out.println("方法1总共循环了" + count + "次");
return isPrime;
}
/**
* 第二个方法
* 所有的偶数都不是质数
*
* @param x 要判断的数字
* @author wl
* @date 2022/8/2 22:55
* @return 判断的结果
*/
public static boolean primeJudge02(int x) {
// 定义 count 变量记录循环次数
int count = 0;
// 定义 布尔变量isPrime
boolean isPrime = true;
if(x == 1 || (x%2 == 0 && x != 2)) {
// 当 x 为 1 时或可以被2整除且不是2时不是质数
isPrime = false;
}
// 循环从 2 到 x - 1 进行判断
for(int i = 3; i < x; i += 2) {
count++;
if(x%i == 0) {
isPrime = false;
break;
}
}
System.out.println("方法2总共循环了" + count + "次");
return isPrime;
}
}
通过实际的对比,我们可以很明显地看出,第二种方法,比第一种方法快了一半,时间复杂度约为
O
(
n
2
)
O(\frac {n} {2})
O(2n),这足以说明这个方法比前一种方法高明多了,但是我相信在座的各位同学,肯定不会止步于此,是的,确实还有更快的方法。
1.3、数学定理
我们先做一个假设,如果 a * b = 81 ,那么 81 的平方根会以 9 为中心,a 和 b 中一定有一个是小于等于 9 的,而另外一个是大于等于 9 的。
-
假设都是大于 9 —— 9.1 ∗ 9.1 > 81 9.1 * 9.1 > 81 9.1∗9.1>81
-
假设都是小于 9 —— 8.9 ∗ 8.9 < 81 8.9 * 8.9 < 81 8.9∗8.9<81
由此,我们可以得出一个结论:一个数的因数一定小于或等于这个数的平方根
C
/** PrimeNumber.c -- 判断某个数是否为素数 */
#include <stdio.h> /* 标准输入输出函数库 */
#include <string.h> /* 字符串函数库 */
#include <stdbool.h> /* 布尔类型变量及函数库 */
#include <math.h> /* 数学函数库 */
/****************** 函数声明 ******************/
/*
判断某个数是否为质数
@param x 要判断的数字
@return 判断结果,如果是质数返回true,反之返回false
*/
bool isPrime01(int x);
/*
判断某个数是否为质数
@param x 要判断的数字
@return 判断结果,如果是质数返回true,反之返回false
*/
bool isPrime02(int x);
/*
判断某个数是否为质数
@param x 要判断的数字
@return 判断结果,如果是质数返回true,反之返回false
*/
bool isPrime03(int x);
/******************* 主函数 *******************/
int main(void)
{
// 定义一个变量用于接受要判断的数
int x;
// 提示性输出
puts("请输入一个数:");
// 1.读取要判断的整型数字 x
scanf("%d", &x);
// 2.判断并输出结果
if((isPrime01(x), isPrime02(x), isPrime03(x))) {
printf("%d是一个素数\n",x);
} else {
printf("%d不是一个素数\n",x);
}
return 0;
}
/*
判断某个数是否为质数
@param x 要判断的数字
@return 判断结果,如果是质数返回true,反之返回false
*/
bool isPrime01(int x)
{
// 定义 count 变量记录循环次数
int count = 0;
// 定义布尔变量isPrime,保存判断的结果
bool isPrime = true;
// 从 2 到 x - 1 循环进行判断
int i;
for(i = 2; i < x; i++) {
count++;
// 如果 x 能被整除,说明不是质数,直接跳出循环即可
if(x%i == 0) {
isPrime = false;
break;
}
}
// 打印循环次数
printf("函数1总共循环了%d次\n", count);
return isPrime;
}
/*
判断某个数是否为质数
@param x 要判断的数字
@return 判断结果,如果是质数返回true,反之返回false
*/
bool isPrime02(int x)
{
// 定义 count 变量记录循环次数
int count = 0;
// 定义布尔变量isPrime,保存判断的结果
bool isPrime = true;
/*
当x等于1时或可以被2整除且不等于2时
说明其不是质数
*/
if(x == 1 || (x%2 == 0 && x != 2)) {
isPrime = false;
}
// 循环从 2 到 x - 1 进行判断
int i;
for(i = 3; i < x; i += 2) {
// 计数器 + 1
count++;
if(x%i == 0) {
isPrime = false;
break;
}
}
// 打印循环次数
printf("函数2总共循环了%d次\n", count);
return isPrime;
}
/*
判断某个数是否为质数
@param x 要判断的数字
@return 判断结果,如果是质数返回true,反之返回false
*/
bool isPrime03(int x)
{
// 定义 count 变量记录循环次数
int count = 0;
// 定义布尔变量isPrime,保存判断的结果
bool isPrime = true;
// 循环从 2 到 x - 1 进行判断
if(x == 1 || (x%2 == 0 && x != 2)) {
// 当 x 为 1 时不是质数
isPrime = false;
}
int i;
// 利用 math.h 头文件中的平方根函数sqrt()求解质数
for(i = 3; i < sqrt(x); i += 2) {
count++;
if( x % i == 0) {
isPrime = false;
break;
}
}
printf("函数3总共循环了%d次\n", count);
return isPrime;
}
Java
import java.util.Scanner;
/**
* 判断某个数是否为素数
*
* @author wl
* @date 2022/8/2 22:09
*/
public class PrimeNumber {
public static void main(String[] args) {
// 创建一个输入扫描仪用于从标准系统输入(控制台)中读取数据
Scanner in = new Scanner(System.in);
// 提示性输出
System.out.println("请输入一个数:");
// 1.定义 x 并使用从标准系统输入中读取的数据进行初始化
int x = in.nextInt();
// 2.判断并输出结果
if(PrimeNumber.primeJudge01(x)
&& PrimeNumber.primeJudge02(x)
&& PrimeNumber.primeJudge03(x)) {
System.out.println(x + "是一个素数");
} else {
System.out.println(x + "不是一个素数");
}
}
/**
* 第一个方法
* 质数只能被1和自身整除
*
* @param x 要判断的数字
* @author wl
* @date 2022/8/2 22:55
* @return 判断的结果
*/
public static boolean primeJudge01(int x) {
// 定义 count 变量记录循环次数
int count = 0;
// 定义 布尔变量isPrime
boolean isPrime = true;
// 循环从 2 到 x - 1 进行判断
for(int i = 2; i < x; i++) {
count++;
if(x%i == 0) {
isPrime = false;
break;
}
}
System.out.println("方法1总共循环了" + count + "次");
return isPrime;
}
/**
* 第二个方法
* 所有的偶数都不是质数
*
* @param x 要判断的数字
* @author wl
* @date 2022/8/2 22:55
*/
public static boolean primeJudge02(int x) {
// 定义 count 变量记录循环次数
int count = 0;
// 定义 布尔变量isPrime
boolean isPrime = true;
if(x == 1 || (x%2 == 0 && x != 2)) {
// 当 x 为 1 时或可以被2整除且不是2时不是质数
isPrime = false;
}
// 循环从 2 到 x - 1 进行判断
for(int i = 3; i < x; i += 2) {
count++;
if(x%i == 0) {
isPrime = false;
break;
}
}
System.out.println("方法2总共循环了" + count + "次");
return isPrime;
}
/**
* 第三个方法
* 如果在 x 的平方根范围内不能被整除,
* 那么 x 就一定是个质数
*
* @param x 要判断的数字
* @author wl
* @date 2022/8/2 22:55
* @return 判断的结果
*/
public static boolean primeJudge03(int x) {
// 定义 count 变量记录循环次数
int count = 0;
// 定义 布尔变量isPrime
boolean isPrime = true;
if(x == 1 || x == 0) {
isPrime = false;
} else {
// 循环从 2 到 Math.sqrt(x) 进行判断
for(int i = 3; i < Math.sqrt(x); i += 2) {
count++;
if(x%i == 0) {
isPrime = false;
break;
}
}
}
System.out.println("方法3总共循环了" + count + "次");
return isPrime;
}
}
这段代码的时间复杂度约为 O ( n 1 2 ) O(n^{\frac{1}{2}}) O(n21)。可以看出,相同的数,第三个方法只执行了二十多次,简直是比闪电侠还快。不过这个方法也不是最优解,实际上,我们还可以通过已存在的质数,创建一张质数表来判断质数
1.4、已存在的质数表
现在不需要拿比 x 小的数来测试 x 是不是质数,只要拿出比 x 小的质数来测试它是不是质数。因为质数是相对比较少的数,大部分的数字甚至都不是质数,所以比 x 小的质数是比 sqrt(x) 相对更少,但是,这就有个前提,我们需要有一张已有质数的表,然后根据这张表我们才可以判断,我们的这个 x 是不是质数,所以这个程序就特别适合于:我正在构造这么一张表,然后我在构造表的过程中考虑如何把新的数据项加到这个表里面去。
C
/** PrimeNumber.c -- 判断某个数是否为素数 */
#include <stdio.h> /* 标准输入输出函数库 */
#include <stdlib.h> /* 标准工具函数库 */
#include <string.h> /* 字符串函数库 */
#include <stdbool.h> /* 布尔类型变量及函数库 */
#include <math.h> /* 数学函数库 */
#define N 32677 /* 定义常量N作为数组的长度 */
/****************** 函数声明 ******************/
/*
判断某个数是否为质数
@param x 要判断的数字
@return 判断结果,如果是质数返回true,反之返回false
*/
bool isPrime01(int x);
/*
判断某个数是否为质数
@param x 要判断的数字
@return 判断结果,如果是质数返回true,反之返回false
*/
bool isPrime02(int x);
/*
判断某个数是否为质数
@param x 要判断的数字
@return 判断结果,如果是质数返回true,反之返回false
*/
bool isPrime03(int x);
/*
使用已存在的质数表
判断某个数是否为质数
@param x 要判断的数字
@return 判断结果,如果是质数返回true,反之返回false
*/
bool isPrime04(int x);
/*
构造 N 以内的质数表
@param knowPrimes 要构造的质数表
*/
void createTable(int knowPrimes[]);
/*
判断一个数在数组中是否为质数
是的话返回 true,反之返回false
@param x 要判断的数
@param knownPrimes 质数表
@return 结果
*/
int isPrimeInArray(int x, int knownPrimes[], int numberOfKnownPrimes);
/******************* 主函数 *******************/
int main(void)
{
// 定义一个变量用于接受要判断的数
int x;
// 提示性输出
puts("请输入一个数:");
// 1.读取要判断的整型数字 x
scanf("%d", &x);
// 2.判断并输出结果
if((isPrime01(x), isPrime02(x), isPrime03(x), isPrime04(x))) {
printf("%d是一个素数\n",x);
} else {
printf("%d不是一个素数\n",x);
}
return 0;
}
/*
判断某个数是否为质数
@param x 要判断的数字
@return 判断结果,如果是质数返回true,反之返回false
*/
bool isPrime01(int x)
{
// 定义 count 变量记录循环次数
int count = 0;
// 定义布尔变量isPrime,保存判断的结果
bool isPrime = true;
// 从 2 到 x - 1 循环进行判断
int i;
for(i = 2; i < x; i++) {
count++;
// 如果 x 能被整除,说明不是质数,直接跳出循环即可
if(x%i == 0) {
isPrime = false;
break;
}
}
// 打印循环次数
printf("函数1总共循环了%d次\n", count);
return isPrime;
}
/*
判断某个数是否为质数
@param x 要判断的数字
@return 判断结果,如果是质数返回true,反之返回false
*/
bool isPrime02(int x)
{
// 定义 count 变量记录循环次数
int count = 0;
// 定义布尔变量isPrime,保存判断的结果
bool isPrime = true;
/*
当x等于1时或可以被2整除且不等于2时
说明其不是质数
*/
if(x == 1 || (x%2 == 0 && x != 2)) {
isPrime = false;
}
// 循环从 2 到 x - 1 进行判断
int i;
for(i = 3; i < x; i += 2) {
// 计数器 + 1
count++;
if(x%i == 0) {
isPrime = false;
break;
}
}
// 打印循环次数
printf("函数2总共循环了%d次\n", count);
return isPrime;
}
/*
判断某个数是否为质数
@param x 要判断的数字
@return 判断结果,如果是质数返回true,反之返回false
*/
bool isPrime03(int x)
{
// 定义 count 变量记录循环次数
int count = 0;
// 定义布尔变量isPrime,保存判断的结果
bool isPrime = true;
// 循环从 2 到 x - 1 进行判断
if(x == 1 || (x%2 == 0 && x != 2)) {
// 当 x 为 1 时不是质数
isPrime = false;
}
int i;
// 利用 math.h 头文件中的平方根函数sqrt()求解质数
for(i = 3; i < sqrt(x); i += 2) {
count++;
if( x % i == 0) {
isPrime = false;
break;
}
}
printf("函数3总共循环了%d次\n", count);
return isPrime;
}
/*
使用已存在的质数表
判断某个数是否为质数
@param x 要判断的数字
@return 判断结果,如果是质数返回true,反之返回false
*/
bool isPrime04(int x)
{
int count = 0, i;
bool isPrime = true;
if(x == 1 || (x%2 == 0 && x != 2)) {
isPrime = false;
} else {
// 因为 x 的值可能非常大,故而申请堆内存
int *prime = (int*) malloc(sizeof(int) * N);
// 创建质数表
createTable(prime);
for (i = 0; i < N && prime[i] < sqrt(x); i++)
{
count++;
if(x % prime[i] == 0) {
isPrime = false;
break;
}
}
// 释放借来的堆内存,有借有还再借不难
free(prime);
}
printf("函数4总共循环了%d次\n", count);
return isPrime;
}
/*
构造 N 以内的质数表
@param knowPrimes 要构造的质数表
*/
void createTable(int knowPrimes[])
{
knowPrimes[0] = 2;
int count = 1, i = 3;
/*{
printf("\t\t");
int i;
for (i = 0; i < N; i++)
{
printf("%d\t", i);
}
putchar('\n');
}*/
while (count < N)
{
if (isPrimeInArray(i, knowPrimes, count)) {
knowPrimes[count++] = i;
}
// 调试代码
/*{
printf("i=%d \tcnt=%d\t", i, count);
int i;
for (i = 0; i < n; i++)
{
printf("%d\t", knowPrimes[i]);
}
putchar('\n');
}*/
i++;
}
/*for (i = 0; i < N; i++)
{
printf("%d", knowPrimes[i]);
if ((i+1)%5 != 0) {
putchar('\t');
} else {
putchar('\n');
}
}*/
}
/*
判断一个数在数组中是否为质数
是的话返回 true,反之返回false
@param x 要判断的数
@param knownPrimes 质数表
@return 结果
*/
int isPrimeInArray(int x, int knownPrimes[], int numberOfKnownPrimes)
{
int ret = 1, i;
for (i = 0; i < numberOfKnownPrimes; i++)
{
if (x % knownPrimes[i] == 0) {
ret = 0;
break;
}
}
return ret;
}
Java
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
/**
* 判断某个数是否为素数
*
* @author wl
*/
public class PrimeNumber {
/**
* 质数表的长度
*/
private final static Integer N = 10;
/**
* 质数表
*/
public final static List<Integer> PRIME_LIST = new ArrayList<>(N);
static {
createTable(PRIME_LIST);
}
public static void main(String[] args) {
// 创建一个输入扫描仪用于从标准系统输入(控制台)中读取数据
Scanner in = new Scanner(System.in);
// 提示性输出
System.out.println("请输入一个数:");
// 1.定义 x 并使用从标准系统输入中读取的数据进行初始化
int x = in.nextInt();
// 2.判断并输出结果
if (PrimeNumber.primeJudge01(x)
&& PrimeNumber.primeJudge02(x)
&& PrimeNumber.primeJudge03(x)
&& PrimeNumber.primeJudge04(x)) {
System.out.println(x + "是一个素数");
} else {
System.out.println(x + "不是一个素数");
}
}
/**
* 第一个方法
* 质数只能被1和自身整除
*
* @param x 要判断的数字
* @author wl
* @date 2022/8/2 22:55
* @return 判断的结果
*/
public static boolean primeJudge01(int x) {
// 定义 count 变量记录循环次数
int count = 0;
// 定义 布尔变量isPrime
boolean isPrime = true;
// 循环从 2 到 x - 1 进行判断
for (int i = 2; i < x; i++) {
count++;
if (x % i == 0) {
isPrime = false;
break;
}
}
System.out.println("方法1总共循环了" + count + "次");
return isPrime;
}
/**
* 第二个方法
* 所有的偶数都不是质数
*
* @param x 要判断的数字
* @author wl
* @date 2022/8/2 22:55
* @return 判断的结果
*/
public static boolean primeJudge02(int x) {
// 定义 count 变量记录循环次数
int count = 0;
// 定义 布尔变量isPrime
boolean isPrime = true;
if (x == 1 || (x % 2 == 0 && x != 2)) {
// 当 x 为 1 时或可以被2整除且不是2时不是质数
isPrime = false;
}
// 循环从 2 到 x - 1 进行判断
for (int i = 3; i < x; i += 2) {
count++;
if (x % i == 0) {
isPrime = false;
break;
}
}
System.out.println("方法2总共循环了" + count + "次");
return isPrime;
}
/**
* 第三个方法
* 如果在 x 的平方根范围内不能被整除,
* 那么 x 就一定是个质数
*
* @param x 要判断的数字
* @author wl
* @date 2022/8/2 22:55
* @return 判断的结果
*/
public static boolean primeJudge03(int x) {
// 定义 count 变量记录循环次数
int count = 0;
// 定义 布尔变量isPrime
boolean isPrime = true;
if (x == 1 || x == 0) {
isPrime = false;
} else {
// 循环从 2 到 Math.sqrt(x) 进行判断
for (int i = 3; i < Math.sqrt(x); i += 2) {
count++;
if (x % i == 0) {
isPrime = false;
break;
}
}
}
System.out.println("方法3总共循环了" + count + "次");
return isPrime;
}
/**
* 第四个方法
* 使用已存在的质数表
* 判断某个数是否为质数
*
* @param x 要判断的数字
* @author wl
* @date 2022/8/2 22:55
* @return 判断的结果
*/
public static boolean primeJudge04(int x) {
int count = 0;
boolean isPrime = true;
if (x == 1 || (x % 2 == 0 && x != 2)) {
isPrime = false;
} else {
double sqrt = Math.sqrt(x);
for (int i = 0; i < N && PRIME_LIST.get(i) < sqrt; i++) {
count++;
if (x % PRIME_LIST.get(i) == 0) {
isPrime = false;
break;
}
}
}
System.out.println("方法4总共循环了" + count + "次");
return isPrime;
}
/**
* 构造 N 以内的质数表
*
* @param knowPrimes 要构造的质数表
*/
private static void createTable(List<Integer> knowPrimes) {
knowPrimes.add(2);
int count = 1, idx = 3;
// 调试代码
/*System.out.print("\t\t\t\t");
for (int i = 0; i < N; i++) {
System.out.print(i + "\t");
}
System.out.println();*/
while (count < N) {
if (isPrimeInArray(idx, knowPrimes, count)) {
knowPrimes.add(idx);
count++;
}
// 调试代码
/*System.out.print("idx=" + idx + "\tcnt=" + count + "\t");
for (Integer knowPrime : knowPrimes) {
System.out.print(knowPrime + "\t");
}
System.out.println();*/
idx++;
}
/*for (int i = 0; i < knowPrimes.size(); i++) {
System.out.print(knowPrimes.get(i));
if ((i + 1) % 5 != 0) {
System.out.print("\t");
} else {
System.out.println();
}
}*/
}
/**
* 判断一个数在数组中是否为质数
* 是的话返回 true,反之返回false
*
* @param x 要判断的数
* @param knownPrimes 质数表
* @return 判断的结果
*/
private static boolean isPrimeInArray(int x, List<Integer> knownPrimes, int numberOfKnownPrimes) {
boolean ret = true;
for (int i = 0; i < numberOfKnownPrimes; i++) {
if (x % knownPrimes.get(i) == 0) {
ret = false;
break;
}
}
return ret;
}
}
2、构造一张纯素数表
前面做了很多判断质数的方法,但是代码的基本思路其实是一样的,都是说输入一个数,然后想办法构造一些东西对它做整除,看看能不能整除,能被整除就不是素数。不同的是用来除它的数越来越少。我们在每一次的判断中尽量使得程序越来越快,循环的次数也越来越少,但是在求解质数的方法上,并不是只有这一种想法,如果用相反的想法来思考这件事情,假设现在我们不是去判断某个数是否为质数,而是构造出这样一张表,当这张表构造完成的时候,表里留下的全部数据都是素数,这样想的话是不是会好很多?
算法是这样的,如果要构造 n 以内(不含 n)的素数表:
- 令 x 为 2
- 将 2x、3x、4x 直至
ax <n
的数(即 x 的倍数)标记为非素数 - 令 x 为下一个没有被标记为非素数的数,然后重复第二步,直到所有的数都尝试完毕后
伪代码
-
开辟
prime[n]
,初始化其所有元素为 1,prime[x]
为1表示 x 是素数
-
令
x = 2
-
如果 x 是素数,则对于
(i =2; x*i < n; i++)
令prime[i*x] = 0
-
令
x++
,如果x < n
,重复 3,否则结束
代码实现
/** constructPrimerNumberTable.c -- 构造一张纯素数表 */
#include <stdio.h>
/*
求 maxNumber 以内的素数
*/
int main(void)
{
// 初始化程序
const int maxNumber = 5000;
int prime[maxNumber];
int i, x;
// 1.初始化数组
for (i = 0; i < maxNumber; i++)
{
prime[i] = 1;
}
// 2.标记数组中不是素数的数据
for (x = 2; x < maxNumber; x++)
{
if (prime[x]) {
for (i = 2; i*x < maxNumber; i++)
{
prime[i*x] = 0;
}
}
}
// 3.输出未被标记的数据(即素数)
int count = 0;
printf("从 1 到 %d 的素数有:\n", maxNumber);
for (i = 2; i < maxNumber; i++)
{
if (prime[i]) {
printf("%d", i);
if ((count+1)%5 != 0) {
putchar('\t');
} else {
putchar('\n');
}
count++;
}
}
putchar('\n');
return 0;
}
3、总结
通过上面的练习,我们可以了解到,算法和人的思维方式不一定相同,只有通过不断的练习和发掘其中的关联信息,找到最优解,才能优化或改进代码。事实上,历史上有一些算法就是改进其他算法而得来,比如希尔排序,也被称为最小增量排序,其排序算法就是在直接插入排序的基础上稍加改进而得来的。