一篇不大正經的有關素數的小結


素數:

也稱質數、不可約數,不存在非平凡因子。

平凡因子:

即對於任意數\(n\)都至少存在兩個因子,一個是\(1\),另一個是\(n\)本身,我們就叫它倆為\(n\)的平凡因子,其他的,都為n的不平凡因子。

性質:

\(\pi (n)\)為不超過\(n\)的質數個數
那么,\(\pi (n) \backsim \frac {n}{\ln n}\)\(n\)越大,估計的越准確)

質因數分解:

Code:

inline int factorize(int x,int p[]) {
	int cnt=0;
	for(int i=2; i*i<=x; ++i) {
		if(x%i==0) {
			p[cnt++]=i;
			x/=i;
		}
	}
	if(x>1) p[cnt++]=x;
	return cnt;
}

例題:

質數有無限個,如何證明?

反證法:假設質數是有限的
\(\because假設為p_1,p_2,\cdots p_n\)
\(\therefore M=p_1*p_2*\cdots p_n+1\)
\(又\therefore M \bmod p_1=1\)
\(M \bmod p_2=1\)
\(\cdots\)
\(M \bmod p_n=1\)
\(\therefore M \bmod 任何質數都是1,M不是任何質數的倍數,M是質數,與假設沖突,所以質數有無限個\)

這樣一想,求它是不是就有很多種方法啦~(\(Emma,19260817\)是個質數)
1.一個毒瘤的判斷素數法子(跑的賊快的那種,時間復雜度 \(O(\sqrt{n}/3)\)):

首先看一個關於質數分布的規律:
\(\geq5\)的質數一定和\(6\)的倍數相鄰。
\(證明:令x\geq 1,將\geq 5的自然數表示如下:\)
\(\cdots 6x-1,6x,6x+1,6x+2,6x+3,6x+4,6x+5,6(x+1),6(x+1)+1 \cdots\)
\(可以看到,不和6的倍數相鄰的數為6x+2,6x+3,6x+4,由於2(3x+1),3(2x+1),2(3x+2),所以它們一定不是素數,再除去6x本身,顯然,素數要出現只可能出現在6x的相鄰兩側。\)
這種方法裁剪了不和\(6\)的倍數相鄰的數,雖然都沒有降低時間復雜度的階數,但都一定程度上加快了判斷的速度。

inline int prime(int n) {
	if(n==1) return false;
	if(n==2 || n==3) return true;
	if(n%6!=1 && n%6!=5) return false;
	for(register int i=5; i<=sqrt(n); i+=6)
		if(n%i==0 || n%(i+2)==0) return false;
	return true;
}

2.非常朴素的一種算法(判斷有沒有能整除的數)

#include<bits/stdc++.h>
using namespace std;
int main() {
	int n;
	cin>>n;
	for(int i=2; i<=n; i++) {
		if(n%i==0) {
			cout<<"flase";
			return 0;
		} else {
			cout<<"true";
			return 0;
		}
	}
}

3.網絡上流傳的素數打表:

/*
遇到素數需要打表時,先估算素數的個數:
num = n / lnx;
num為大概數字,越大誤差越小(只是估計,用於估算素數表數組大小)
這個打表法效率貌似很高,網上說幾乎達到了線性時間(不知道是真是假=。=)
*/
#include<iostream>
#include<string.h>
#include<stdio.h>
#include<stdlib.h>
#define maxn 10000000
using namespace std;
bool visit[maxn+1000000];
int prime[maxn],n; ///prime的大小大概估計一下再開數組。大概是(x/lnx)
void getprime() {
	memset(visit, false, sizeof(visit));
	int num = 0;
	for (int i = 2; i <= n; ++i) {
		if ( !visit[i] )  prime[++num] = i;
		for (int j = 1; j <= num && i * prime[j] <= n ;  j++) {
			visit[ i  *  prime[j] ]  =  true; 
			if (i % prime[j] == 0) break; 
		}
	}
	for(int i=2;i<=n;i++){
		if(visit[i]==false)
		cout<<i<<' ';
	}
}
int main() {
	freopen("素數打表.txt","w",stdout); 
	scanf("%d",&n);
	getprime();
	return 0;
}

4.弟弟一般的朴素打表:

#include<bits/stdc++.h>
using namespace std;

int g_g(int x) {
	int flag=1;
	for(int i=2; i<=sqrt(x); ++i) {
		if(x%i==0)
			flag=0;
	}
	if(flag==1)
		return 1;
	else
		return 0;
}
int main() {
	freopen("sushu.out","w",stdout);
	for(int i=9784010; i<=100000000; ++i) {
		if(g_g(i)) {
			cout<<i<<",";
		}
	}
	return 0;
}

5.有點小優化的朴素判斷:

bool isprime(int n) {
	if(n<2)return false;
	if(n==2) return true;
	for(int i=2; i<=sqrt(n); i++)
		if(n%i==0)
			return false;
	return true;
}

6.埃氏篩總得聽過吧(stm找的一個代碼得明白)

#include<bits/stdc++.h>
using namespace std;
int n,m;
int fw;
int kk;
bool a[100000000];
int main()
{
    cin>>n>>m;
    memset(a,0,sizeof(a));
    fw=sqrt(n+0.5);//防止四舍五入
    a[1]=1;//不判斷一,直接賦值
    for(int i=2;i<=fw;i++)//從二的倍數開始找
    {
        if(a[i]==0)//優化一,只有在a[i]不是合數下判斷。
        {
            for(int j=i*i;j<=n;j+=i)//j=i*i,是重點,應為2*i等已經被判斷過了
                a[j]=1;    
        }
    }
    for(int i=1;i<=m;i++)
    {
        cin>>kk;
        if(a[kk]==0)
            cout<<"Yes"<<endl;
        else
            cout<<"No"<<endl;
    }
    return 0;
}

7.miller rabin 算法(很**,反正我不會)

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <map>
#define ll long long
using namespace std;
const int times = 20;
int number = 0;
map<ll, int>m;
ll Random(ll n) { //生成[ 0 , n ]的隨機數
	return ((double)rand()/RAND_MAX*n+0.5);
}
ll q_mul(ll a, ll b, ll mod) { //快速計算 (a*b) % mod
	ll ans=0;
	while(b) {
		if(b&1) {
			b--;
			ans=(ans+a)%mod;
		}
		b/=2;
		a=(a+a)%mod;
	}
	return ans;
}
ll q_pow(ll a,ll b,ll mod) { //快速計算 (a^b) % mod
	ll ans=1;
	while(b) {
		if(b&1) {
			ans=q_mul(ans,a,mod );
		}
		b/=2;
		a=q_mul(a,a,mod);
	}
	return ans;
}
bool witness(ll a,ll n) { //miller_rabin算法的精華
	//用檢驗算子a來檢驗n是不是素數
	ll tem=n-1;
	int j=0;
	while(tem%2==0) {
		tem/=2;
		j++;
	}
	//將n-1拆分為a^r * s
	ll x=q_pow(a,tem,n); //得到a^r mod n
	if(x==1||x==n-1) return true;//余數為1則為素數
	while(j--) { //否則試驗條件2看是否有滿足的 j
		x=q_mul(x,x,n);
		if(x==n-1)return true;
	}
	return false;
}
bool miller_rabin(ll n) { //檢驗n是否是素數

	if(n==2)return true;
	if(n<2||n%2==0)return false;//如果是2則是素數,如果<2或者是>2的偶[]數則不是素數
	for(register int i=1; i<=times; i++) { //做times次隨機檢驗
		ll a=Random(n-2)+1;//得到隨機檢驗算子 a
		if(!witness(a,n))return false;//用a檢驗n是否是素數
	}
	return true;
}
int main() {
	ll x;
	while(cin>>x) {
		if(miller_rabin(x))
			cout<<"Yes"<<endl;
		else
			cout <<"No"<<endl;
	}
	return 0;
}

刮搜幾道弟弟(我這種人)喜歡做的題:

AT261 與えられた數より小さい素數の個數について
AT807 素數、コンテスト、素數
AT1476 素數判定
P3383 【模板】線性篩素數
P3912 素數個數

綜上所述:我還是喜歡毒瘤,噗嗤


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM