打素數表與歐拉函數

埃氏篩法

第一種
這種方法好處是每個素數的位置標記,可以直接取下標

int visit[maxn];  // 0 表示素數 1 表示非素數
void Prime(){
    memset(visit,0,sizeof(visit);           //初始化都是素數
    visit[0] = visit[1] = 1;  //0 和 1不是素數
    for (int i = 2; i <= maxn; i++) {
        if (!visit[i]) {         //如果i是素數,讓i的所有倍數都不是素數
            for (int j = i*i; j <= maxn; j += i) { 
                visit[j] = 1;
            }
        }
    }
}

第二種
這種方法的好處是素數是連續的,用vector保存,不耗費內存

const int maxn=1005;
bool bk[maxn];
vector<int> v;
void find_Prime()
{
    //初始化, 打表
	memset(bk, 1, sizeof(bk));
	for(ll i = 2; i < maxn; i++)
	{
		for(ll j = i * i; j < maxn; j += i)
		{
			bk[j] = 0;
		}
	}
    //取出相應的素數
	for(ll i = 2; i < maxn; i++)
	{
		if(bk[i] == 1)
			v.push_back(i);
	}
}

歐拉篩法

這種方法的好處是素數是連續的

const int maxn=10000005;
int prime[maxn],visit[maxn];
void Prime(){
    memset(visit,0,sizeof(visit));
    memset(prime, 0,sizeof(prime));
    for (int i = 2;i <= maxn; i++) {
        cout<<" i = "<<i<<endl;
        if (!visit[i]) {
            prime[++prime[0]] = i;      //紀錄素數, 這個prime[0] 相當於 cnt,用來計數
        }
        for (int j = 1; j <=prime[0] && i*prime[j] <= maxn; j++) {
//            cout<<"  j = "<<j<<" prime["<<j<<"]"<<" = "<<prime[j]<<" i*prime[j] = "<<i*prime[j]<<endl;
            visit[i*prime[j]] = 1;
            if (i % prime[j] == 0) {
                break;
            }
        }
    }
}

歐拉篩 求10億以上的素數

const int maxn=100000;
int prime[maxn],visit[maxn];
void Prime(){
    memset(visit,0,sizeof(visit));
    memset(prime, 0,sizeof(prime));
    for (int i = 2;i <= maxn; i++) {
        cout<<" i = "<<i<<endl;
        if (!visit[i]) {
            prime[++prime[0]] = i;      //紀錄素數, 這個prime[0] 相當於 cnt,用來計數
        }
        for (int j = 1; j <=prime[0] && i*prime[j] <= maxn; j++) {
//            cout<<"  j = "<<j<<" prime["<<j<<"]"<<" = "<<prime[j]<<" i*prime[j] = "<<i*prime[j]<<endl;
            visit[i*prime[j]] = 1;
            if (i % prime[j] == 0) {
                break;
            }
        }
    }
}
bool prime3(long long n)
{
    long long i;

    for(i=1;i<=prime[0];i++)
    {
        if(n%(prime[i])==0)
        {
            return false;
        }
    }
    return true;
}
int main()
{   

	Prime();
	long long j=1;
    for(long long i=1000000000; i<10000000000; i++)
        if(prime3(i)!=false)
            printf("%lld : %lld\n",j++,i);
}

歐拉函數求法

就是對於一個正整數n,小於n且和n互質的正整數(包括1)的個數,記作φ(n) 。

歐拉函數的通式:φ(n)=n(1-1/p1)(1-1/p2)(1-1/p3)*(1-1/p4)……(1-1/pn)*
其中p1, p2……pn爲n的所有質因數,n是不爲0的整數。φ(1)=1(唯一和1互質的數就是1本身)。

ll eular(ll n)
{
    ll ans = n;
    for(int i=2; i*i <= n; ++i)
    {
        if(n%i == 0)
        {
            ans = ans/i*(i-1);
            while(n%i == 0)
                n/=i;
        }
    }
    if(n > 1) ans = ans/n*(n-1);
    return ans;
}

歐拉篩加歐拉函數求法

void euler(int n)
{
	phi[1]=1;//1要特判 
	for (int i=2;i<=n;i++)
	{
		if (flag[i]==0)//這代表i是質數 
		{
			prime[++num]=i;
			phi[i]=i-1;
		}
		for (int j=1;j<=num&&prime[j]*i<=n;j++)//經典的歐拉篩寫法 
		{
			flag[i*prime[j]]=1;//先把這個合數標記掉 
			if (i%prime[j]==0)
			{
				phi[i*prime[j]]=phi[i]*prime[j];//若prime[j]是i的質因子,則根據計算公式,i已經包括i*prime[j]的所有質因子 
				break;//經典歐拉篩的核心語句,這樣能保證每個數只會被自己最小的因子篩掉一次 
			}
			else phi[i*prime[j]]=phi[i]*phi[prime[j]];//利用了歐拉函數是個積性函數的性質 
		}
	}
}

歐拉函數的一些性質:
① 當m,n互質時,有phi(m*n)= phi(m)*phi(n);

② 若i%p==0,有phi(i*p) = p * phi(i);

③ 對於互質x與p,有x^phi§≡1(mod p),因此x的逆元爲x^(phi§-1),即歐拉定理。
(特別地,當p爲質數時,phi(p)=p-1,此時逆元爲x^(p-2),即費馬小定理)

④ 當n爲奇數時,phi(2n)=phi(n)

⑤ 若x與p互質,則p-x也與p互質,因此小於p且與p互質的數之和爲phi(x)*x/2;

⑥N>1,不大於N且和N互素的所有正整數的和是 1/2 *N *eular(N)。

⑦若(N%a == 0 && (N/a)%a==0) 則有:E(N)=E(N/a)*a;

⑧若(N%a==0 && (N/a)%a!=0) 則有:E(N)=E(N/a)*(a-1);

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章