素数

本文详细阐述了素数判断算法的优化过程,从基本判断到复杂优化,包括剔除偶数判断、减少判断范围、剔除重复判断等。同时介绍了如何在构造素数序列时利用已有的素数序列来提高效率。最后,通过实例展示了如何将整数进行质因数分解。
1. 根据概念判断:

如果一个正整数只有两个因子, 1和p,则称p为素数.
代码:
bool isPrime(int n) { if(n < 2) return false; for(int i = 2; i < n; ++i) if(n%i == 0) return false; return true; }
时间复杂度O(n).


2. 改进, 去掉偶数的判断(素数都是奇数)

代码:
bool isPrime(int n) { if(n < 2) return false; if(n == 2) return true; for(int i = 3; i < n; i += 2) if(n%i == 0) return false; return true; }
时间复杂度O(n/2), 速度提高一倍.


3. 进一步减少判断的范围(素数都是成对出现)

定理: 如果n不是素数, 则n有满足1<d<=sqrt(n)的一个因子d.
证明: 如果n不是素数, 则由定义n有一个因子d满足1<d<n.
如果d大于sqrt(n), 则n/d是满足1<n/d<=sqrt(n)的一个因子.

代码:
bool isPrime(int n) { if(n < 2) return false; if(n == 2) return true; for(int i = 3; i*i <= n; i += 2) if(n%i == 0) return false; return true; }
时间复杂度O(sqrt(n)/2), 速度提高O((n-sqrt(n))/2).


4. 剔除因子中的重复判断.只要尝试小于√x 的质数即可

比如要判断101是否质数,101的根号取整后是10,那么,按照境界4,需要尝试的奇数分别是:3,5,7,9。

但是你发现没有,对9的尝试是多余的。不能被3整除,必然不能被9整除,如果能被9整除,已经被前面一个因子整除。


定理: 如果n不是素数, 则n有满足1<d<=sqrt(n)的一个"素数"因子d.
证明: I1. 如果n不是素数, 则n有满足1<d<=sqrt(n)的一个因子d.
I2. 如果d是素数, 则定理得证, 算法终止.

I3. 令n=d, 并转到步骤I1.

由于不可能无限分解n的因子, 因此上述证明的算法最终会停止.

代码:
// primes[i]是递增的素数序列: 2, 3, 5, 7, ... // 更准确地说primes[i]序列包含1->sqrt(n)范围内的所有素数 bool isPrime(int primes[], int n) { if(n < 2) return false; for(int i = 0; primes[i]*primes[i] <= n; ++i) if(n%primes[i] == 0) return false; return true; }
5. 构造素数序列primes[i]: 2, 3, 5, 7, ...

由4的算法我们知道, 在素数序列已经被构造的情况下, 判断n是否为素数效率很高;

但是, 在构造素数序列本身的时候, 是否也可是达到最好的效率呢?

事实上这是可以的! -- 我们在构造的时候完全可以利用已经被构造的素数序列!

假设我们已经我素数序列: p1, p2, .. pn

现在要判断pn+1是否是素数, 则需要(1, sqrt(pn+1)]范围内的所有素数序列,

而这个素数序列显然已经作为p1, p2, .. pn的一个子集被包含了!

代码:
// 构造素数序列primes[] 获得0-num所有的素数 void makePrimes(int primes[], int num) { int i, j, cnt; primes[0] = 2; primes[1] = 3; for(i = 5, cnt = 2; cnt < num; i += 2) { int flag = true; for(j = 1; primes[j]*primes[j] <= i; ++j) //判断I是否是素数 { if(i%primes[j] == 0) { flag = false; break; } } if(flag) primes[cnt++] = i; } }
makePrimes的时间复杂度比较复杂, 而且它只有在初始化的时候才被调用一次.

在一定的应用范围内, 我们可以把近似认为makePrimes需要常数时间.


6.将整数num进行质因数分解,例如:输入90,打印出90=2*3*3*5。

#include<stdio.h>
#include<math.h>
int main()
{
    int n,i;
    while(scanf("%d",&n)&&n)
    {
        while(n%2==0)
  <span style="white-space:pre">	</span> {
               n/=2; 
<span style="white-space:pre">		</span>printf("%d ",2);
  <span style="white-space:pre">	</span> }
<span style="white-space:pre">	</span>for(i=3;i<=sqrt(n);i+=2)
            while(n%i==0)  {
               n/=i; 
<span style="white-space:pre">	</span>printf("%d ",i);
   <span style="white-space:pre">	</span>}
        printf("%d/n",n);
    }
    return 0;
}


明白了!你说的“不是质数素数”,应该是说: > 判断一个数是否是**素数质数)**,而不是合数。 我们来重新梳理一下你的原始代码意图: 你希望判断一个数是否是**素数**(即质数),而你的函数 `haha` 实际上是判断一个数是否是**合数**(非质数),所以逻辑是反的。 --- ## ✅ 正确判断素数的函数(优化版) ```c #include <stdio.h> #include <stdbool.h> #include <math.h> // 判断一个数是否为素数 bool is_prime(int a) { if (a < 2) return false; // 0和1都不是素数 int sqrt_a = (int)sqrt(a); // 只需要检查到平方根即可 for (int i = 2; i <= sqrt_a; i++) { if (a % i == 0) return false; // 能整除则不是素数 } return true; } ``` > **说明**: - 时间复杂度从原来的 `O(a/2)` 优化到 `O(√a)`。 - 更加高效,尤其在处理大数时提升明显。 --- ## ✅ 修正主函数逻辑 你原来的主函数中,有一个 `while(k--)` 的写法是错误的,因为 `k` 是在循环中被递减的,导致第一次循环后 `k` 就变成 0,后续循环无法再进入。 我们来修正整个逻辑,使用**滑动窗口**或**前缀和**来高效地处理连续 `k` 个元素的和。 ### ✅ 改进后的完整代码 ```c #include <stdio.h> #include <stdbool.h> #include <math.h> bool is_prime(int a) { if (a < 2) return false; int sqrt_a = (int)sqrt(a); for (int i = 2; i <= sqrt_a; i++) { if (a % i == 0) return false; } return true; } int main() { int n, k; scanf("%d%d", &n, &k); int a[n]; // 输入数组 for (int i = 0; i < n; i++) { scanf("%d", &a[i]); } int count = 0; // 对每个起点 i,计算连续 k 个元素的和(循环数组) for (int i = 0; i < n; i++) { int sum = 0; for (int j = 0; j < k; j++) { int index = (i + j) % n; // 循环取数 sum += a[index]; } if (is_prime(sum)) { count++; } } printf("%d\n", count); return 0; } ``` > **说明**: - `is_prime` 函数用于判断和是否是素数。 - 每次从位置 `i` 开始,取 `k` 个元素(循环数组),求和后判断是否为素数。 - 总时间复杂度为 `O(n * k + n * √sum)`,在合理范围内。 --- ## ✅ 示例 输入: ``` 5 2 2 3 5 7 11 ``` 输出: ``` 5 ``` 说明:所有连续两个数的和都是素数。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值