zoukankan      html  css  js  c++  java
  • 一篇不大正经的有关素数的小结

    素数:

    也称质数、不可约数,不存在非平凡因子。

    平凡因子:

    即对于任意数(n)都至少存在两个因子,一个是(1),另一个是(n)本身,我们就叫它俩为(n)的平凡因子,其他的,都为n的不平凡因子。

    性质:

    (pi (n))为不超过(n)的质数个数
    那么,(pi (n) acksim 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;
    }
    

    例题:

    质数有无限个,如何证明?

    反证法:假设质数是有限的
    (ecause假设为p_1,p_2,cdots p_n)
    ( herefore M=p_1*p_2*cdots p_n+1)
    (又 herefore M mod p_1=1)
    (M mod p_2=1)
    (cdots)
    (M mod p_n=1)
    ( herefore M mod 任何质数都是1,M不是任何质数的倍数,M是质数,与假设冲突,所以质数有无限个)

    这样一想,求它是不是就有很多种方法啦~((Emma,19260817)是个质数)
    1.一个毒瘤的判断素数法子(跑的贼快的那种,时间复杂度 (O(sqrt{n}/3))):

    首先看一个关于质数分布的规律:
    (geq5)的质数一定和(6)的倍数相邻。
    (证明:令xgeq 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 素数个数

    综上所述:我还是喜欢毒瘤,噗嗤

  • 相关阅读:
    Linux学习总结(16)——CentOS 下 Nginx + Tomcat 配置负载均衡
    photo的复数是photos
    APUE1.11:系统调用 库函数
    Linux的man手册共有以下几个章节
    [关于宝宝的一些网上摘抄]
    ZT-Android深入浅出之Binder机 制
    jclass和jobject的迷惑
    ZT JAVA WeakReference
    ZT————pull push mode
    看了xici有写给孩子的信,maybe我也要写给孩子一些东西了
  • 原文地址:https://www.cnblogs.com/morbidity/p/10809532.html
Copyright © 2011-2022 走看看