起因:
在洛谷做题遇到了这道题~
一看咿呀,又是道数学题~
首先我们要了解一下,什么是质数?
我记得好像有年高考题的前几题好像考了这玩意来着,质数的概念好像在小学学过,上了初中后基本都没有用过了~
质数就是素数,我们以前应该写过一个程序叫判断某个范围的数里面有多少个素数,其实在这里我们就已经了解过质数的概念了~
以下是GPT回答的参考:
质数和素数在数学上指的是同一个概念,两者都是只能被1和它本身整除的正整数,而且大于1。在数学文献中,通常使用“素数”这个词,特别是在欧洲和北美地区,而“质数”则更多地在亚洲和拉丁美洲使用。不过,这两个词指的是完全相同的概念。
例如,2、3、5、7、11、13、17等都是质数(或素数),因为它们除了1和它们自身以外,没有其他的因数。相对的,像4、6、8、9、10等就不是质数(或素数),因为它们除了1和它们自身以外,还有其他的因数。
OK,已经知道概念了,因为题目中称呼这种数为质数,我们为统一命名,后文中使用质数来称呼这种数,让我们来看看题~
已知两个质数的乘积,求其中一个最大的质数,好像并不是很难,如果是用常规的思路的话,用判断质数的那个思路就可以解决掉这道题,前提是不限时~
于是我第一次提交的代码是这样的~
第一次提交的代码:
#include<iostream>
#include <iomanip>
#include<climits>
using namespace std;
int main()
{
int a[10];
int n, count = 0;
cin >> n;
int fact = n;
for (int i = 2; i < n; i++) {
if (fact % i == 0)
a[count++] = i;
}
cout << a[count - 1] << endl;
return 0;
}
使用一个数组来存储所有可能被 n 整除的数,在数组中的数肯定都是 n 的因数,所以最后一个数组空间中存储的就是最大的质因数~
理想很丰满,现实很骨感,果不其然超时了 T^T
第一次提交的结果:
想想有没有什么可以提高速度的方法?
实际上,在判断某个数是否为质数时,只需要枚举到这个数开平方后的数(向下取整)就行了~
为什么质数只需要枚举到这个数开平方后呢?
首先,我们知道如果一个数 n
不是质数,那么它一定可以被表示为两个较小的正整数相乘,即 n = a * b
。
现在假设 a 和 b 都大于 sqrt(n)。那么我们有:
a * b = n
a > sqrt(n)
b > sqrt(n)
从上面的不等式可以看出,a
和 b
都必须大于 sqrt(n)
。但是,如果 a
和 b
都大于 sqrt(n)
,那么它们的乘积 a * b
一定大于 n
~
这就意味着,如果一个数 n
不是质数,那么它一定可以被表示为两个较小的正整数相乘,其中至少有一个数小于或等于 sqrt(n)
~
因此,我们只需要检查 2
到 sqrt(n)
之间的所有整数,就可以确定 n
是否为质数。如果找到任何一个能被 n
整除的数,那么 n
就不是质数。如果在这个范围内没有找到任何能被 n
整除的数,那么 n
就是质数~
这种方法的时间复杂度是 O(√n),相比于逐一检查 2
到 n-1
之间的所有整数,效率要高得多。这就是为什么判断质数只需要列举到 i <= sqrt(n)
就够了的原因~
但是这道题我们无法直接使用这种方法,题目中明确说了这个数是两个质数的乘积,所以这个数一定不是质数,如果使用我们上述说到的方法只会求到最小的质因数
提交就是一片全红,因为我们只求得了最小的质因数,而不是最大的质因数 -_-
使用上述方法的代码:
#include<iostream>
#include <iomanip>
#include<climits>
#include<cmath>
using namespace std;
int main()
{
int a[10];
int n, count = 0;
cin >> n;
int fact = n;
for (int i = 2; i <= sqrt(n); i++) {
if (fact % i == 0)
a[count++] = i;
}
cout << a[count - 1] << endl;
return 0;
}
使用上述方法的结果:
但是我们可以借助这种思路,先卖个关子~ ^v^
既然不能取到sqrt(n),那我扩大一些范围总行了吧~
第二次提交的代码:
#include<iostream>
#include <iomanip>
#include<climits>
#include<cmath>
using namespace std;
int main()
{
int a[10];
int n, count = 0;
cin >> n;
int fact = n;
for (int i = 2; i < sqrt(n)*2+1; i++) {
if (fact % i == 0)
a[count++] = i;
}
cout << a[count - 1] << endl;
return 0;
}
第二次提交的结果:
先开方,然后再对平方后的数进行扩大,这样做会有错误,因为会有一些边界条件未考虑到,比如如果数是6(2 * 3)的话,枚举到2就没了,结果肯定是错误的~
那对sqrt(n)取整试试?
第二次改进后的代码:
#include<iostream>
#include <iomanip>
#include<climits>
#include<cmath>
using namespace std;
int main()
{
int a[10];
int n, count = 0;
cin >> n;
int fact = n;
for (int i = 2; i <= sqrt(n)*2+1; i++) {
if (fact % i == 0)
a[count++] = i;
}
cout << a[count - 1] << endl;
return 0;
}
第二次改进后的结果:
可以看到还是有问题,我们的取值范围是不对的,取值范围太小了~
那我们把取值范围扩大一点试试呢?
第三次提交的代码:
#include<iostream>
#include <iomanip>
#include<climits>
#include<cmath>
using namespace std;
int main()
{
int a[10];
int n, count = 0;
cin >> n;
int fact = n;
int res = 1;
for (int i = 2; i < n/2; i++) {
if (fact % i == 0) {
a[count++] = i;
res *= i;
if (res == n)
break;
}
}
cout << a[count - 1] << endl;
return 0;
}
在这种思路中,我们扩大了搜索的范围,同时我们增加了一个判断条件:如果发现这个数的质数了,就用一个res(初始为1)相乘,如果res == n了,说明我们已经找到了这个数的最大质因数~
第三次提交的结果:
再扩大一下搜索范围试试?
第三次改进后的代码:
#include<iostream>
#include <iomanip>
#include<climits>
#include<cmath>
using namespace std;
int main()
{
int a[100];
int n, count = 0;
cin >> n;
int fact = n;
int res = 1;
for (int i = 2; i <= n/3; i++) {
if (fact % i == 0) {
a[count++] = i;
res *= i;
if (res == n)
break;
}
}
cout << a[count - 1] << endl;
return 0;
}
第三次改进后的结果:
可以看到搜索范围还是有问题的~
大脑.exe已停止运行~
感觉身体被掏空~
又冥思苦想10分钟后,还是AC不掉这道题,没办法,看看大佬的题解吧,弱鸡瑟瑟发抖 T^T
第四次提交的代码:
#include <iostream>
#include <cmath>
using namespace std;
int main()
{
int n; // 读取一个整数 n
cin >> n;
int largest_prime = 1; // 初始化 largest_prime 为 1
for (int i = 2; i <= sqrt(n); i++) { // 从 2 开始遍历到 sqrt(n) 之间的整数
if (n % i == 0) { // 如果 n 能被 i 整除
while (n % i == 0) { // 不断地将 n 除以 i,直到 n 不能被 i 整除为止
n /= i;
}
largest_prime = i; // 更新 largest_prime 为当前的 i
}
}
if (n > 1) { // 如果 n 大于 1,说明 n 本身就是一个素数
largest_prime = n; // 将其赋值给 largest_prime
}
cout << largest_prime << endl; // 输出 largest_prime
return 0;
}
第四次提交的结果:
是不是有点疑惑?为啥枚举到sqrt(n) ,就可以得出正确结果了呢?之前不是直接错了吗?
这是因为在前面我们说过,这种方法只能找到最小的质因数,而不能找到最大的质因数,但是题目中明确的告诉我们这是两个质数相乘,所以不用再次判断这个数是否为质数,因此只要找到小的那个质数就可以直接通过相除找到最大的那个质因数~
以下是我当天AC这道题后写下的一些解释:
1.为什么可以直接用n除以较小的那一个质数就可以得到较大的那一个质数?
质数本来就不可能再被分解,5和7不能被除它以外的数整除,所以5*7=35的两个因子只可能是5和7,不可能是其他因子,故两个质数的乘积分解也会是这两个质数,中间不可能会出现其他结果
2.为什么要枚举到<=sqrt?
因为两个质数相乘的极端情况就是两个质数相等,比如5*5=25,此时需要在for循环里枚举到5
其他情况肯定就是一个质数大,另一个质数小,但是从for循环开始肯定是先枚举到较小的那一个质数,所以通过质数相乘原理就可以直接得出那一个较大的质数
什么,你跟我说你还不懂?那来看看我优化后的代码吧~
第四次改进后的代码:
#include<iostream> // 包含输入输出流库
#include<cmath> // 包含数学运算库
using namespace std;
int main()
{
int n; // 声明整数变量 n,用于存储输入的正整数
cin >> n; // 输入正整数 n
for (int i = 2; i <= sqrt(n); i++) { // 从 2 开始迭代直到 sqrt(n)
if (n % i == 0) { // 如果 n 能被 i 整除
n /= i;
}
}
cout << n << endl; // 输出最大质数
return 0;
}
第四次改进后的结果:
可以看到,只要我们找到了最小的那个质因数,通过相除就可以直接找到最大的那个质因数,连while循环都不用,这道题就是一道纯纯的数学题~
又用了一坤时才写完,以后得提高一下水文(划掉)的速度了啊~
如果您觉得这篇文章对您有帮助的话,那不妨点个赞或者收藏呗,谢谢您~
如果您觉得我的文章有问题,请您私信我,我看到后就会及时改正,谢谢您!