目录
朴素判断
判断一个数是不是素数,最简单的方法就是暴力判断这个数除了1和本身之外有没有其他的因子
原理:因子都是成对出现,所以从2判断到根号n即可找出所有其他因子
复杂度:sqrt(n)
bool is_prime(int p)
{
for(int i = 2;i <= sqrt(p);i++)
{
if(p % i == 0) return false;
}
return true;
}
如果要判断1~n中有多少素数,该方法n*sqrt(n)的复杂度就很不乐观
埃氏筛法
很明显如果一个数是素数,那么这个数的倍数一定不是素数
利用这一结论可以最多一次筛掉n/p个合数(p为当前素数),该方法即为埃氏筛
原理:初始化所有数都未被筛掉,从2开始判断某个数是否被标记为合数筛掉
若未被筛掉则表明改数不是前面任何数的倍数,即为素数,再利用上面的结论筛掉所有该数的倍数
复杂度为n*log(log(n)),详细证明可百度
void Eratosthenes()
{
for(int i = 2;i <= n;i++)
{
isprime[i] = true;
}
for(int i = 1;i <= n;i++)
{
if(isprime[i])
{
prime[++cnt] = i;
for(int j = i*i;j <= n;j+=i)
{
isprime[i] = false;
}
}
}
}
欧拉筛(线性筛)
埃氏筛法仍有优化空间:
一个数可以是两个互质的数相乘,可能会被筛掉很多次
若能保证每个数只被标记一次那么就可以将复杂度优化到O(n)
原理:利用已经筛出来的素数来筛掉他们的倍数i*prime[j]
标记一个数i*prime[j]为合数的同时判断prime[j]是否是i的因子,若是则直接跳出循环
核心:若i%prime[j] = 0则说明prime[j]是i的因子,那么剩下大于prime[j]的素数与i的乘积产生的合数一定会被prime[j]的倍数筛掉
该筛法保证了每个数只被筛一次,所以复杂度为O(n)
void Euler()
{
b[1] = 1;
for(int i = 2;i <= n;i++)
{
//如果不是合数就记录该素数
if(!b[i])
{
prime[++cnt] = i;
}
for(long long j = 1;j <= cnt && i*prime[j] <= n;j++)
{
//将记录的素数的倍数筛掉
b[i*prime[j]] = 1;
//核心语句:若prime[j]是i的因子则跳出循环避免重复筛
if(i%prime[j] == 0) break;
}
}
}
唯一分解定理
引理: 设P为素数,若有,那么
和
至少有一个成立
唯一分解定理:
若A为整数,则必有
为素数,
可以相同
将相同的素数合并:
为整数,且该表示唯一
若A为素数,那么A只能分解为A
若A为合数,那么A可以分解为两个数的乘积B*C,继续分解B,C
不断重复这两步,最后得到的结果一定是上述的第二个式子,且这个式子唯一
将A分解为和
有和
因为p,q都为素数,所以存在
可以证明对于每个都至少有另一个
与之相等,即两个分解完全相等且唯一