1. 程式人生 > >挑戰2.6

挑戰2.6

1.gcd

gcd(a,b)=gcd(b,a%b)

#include<iostream>
using namespace std;
int gcd(int a,int b)
{
    if(b==0)return a;
    return gcd(b,a%b);
}
int main()
{
    int a,b;
    cin>>a>>b;
    cout<<gcd(a,b)<<endl;
}
View Code

2.extgcd

一個事實:對於二元一次方程ax+by=gcd(a,b)必存在整數解(x,y)

ax+by=gcd(a,b)

又bx1+(a%b)y1=gcd(b,a%b)=gcd(a,b)

等價於bx1+(a-(a/b)*b)y1=gcd(a,b)

等價於ay1+b(x1-(a/b)y1)=gcd(a,b)

那麼可以得到x=y1,y=x1-(a/b)y1

x1=y2,y1=x2-(a/b)y2

這樣一直遞迴下去,直到a%b=0,此時gcd(a,b)=a,解為xn=1,yn=0,回代就可以求出x和y

#include<iostream>
using namespace std;
int extgcd(int a,int b,int &x,int
&y)//計算ax+by=gcd(a,b)的整數解,返回gcd(a,b) { int ans=a; if(b!=0) { ans=extgcd(b,a%b,y,x); y-=(a/b)*x; } else{ x=1; y=0; } return ans; } int main() { int a,b,x,y; cin>>a>>b; cout<<extgcd(a,b,x,y)<<endl; cout
<<x<<endl<<y<<endl; }
View Code

3.素數判斷

#include<iostream>
using namespace std;
bool is_prime(int x)
{
    for(int i=2;i*i<=x;i++)
    {
        if(x%i==0)return false;
    }
    return x!=1;
}
int main()
{
    int x;
    cin>>x;
    if(is_prime(x))cout<<"YES\n";
    else cout<<"NO\n";
}
View Code

4.埃氏篩

求1到n之間的素數

先將2到n所有的數字寫下來,做成一張表

對於最小的數字2,劃取表中所有2的倍數

表中剩餘的最小數字是3,劃去表中所有3的倍數

依次推下去,直到遍歷到表中最後一個數字n

#include<iostream>
#include<vector>
using namespace std;
vector<int>prime;
bool is_prime[1000005];
void sieve(int n)
{
    for(int i=1;i<=n;i++)is_prime[i]=true;
    is_prime[0]=is_prime[1]=false;
    for(int i=2;i<=n;i++)
    {
        if(is_prime[i])
        {
            prime.push_back(i);
            for(int j=2*i;j<=n;j+=i)is_prime[j]=false;
        }
    }
}
int main()
{
    int n;
    cin>>n;
    sieve(n);
    cout<<prime.size()<<endl;
}
View Code

4.區間篩

對於兩個特別大的數a,b,求區間[a,b)內素數的個數

首先對於[a,b)內任意一個合數x,x的最小的因子(除去1)小於√b,那麼對於區間[2,√b)內的每一個素數,篩掉它在[a,b)內的倍數,[a,b)內剩下的數就是素數了

#include<iostream>
#include<vector> 
using namespace std;
typedef long long ll;
bool is_prime1[1000005];//篩[2,sqrt(b)) 
bool is_prime2[1000005];//篩[a,b)
vector<ll>prime;//記錄[a,b)中的素數
void segment_sieve(ll a,ll b)
{
    for(int i=2;(ll)i*i<b;i++)is_prime1[i]=true;
    for(int i=1;i<=b-a;i++)is_prime2[i]=true;
    for(int i=2;(ll)i*i<b;i++)
    {
        if(is_prime1[i])
        {
            for(int j=2*i;(ll)j*j<b;j+=i)is_prime1[j]=false;
            for(ll j=max(2LL,(a+i-1)/i)*i;j<b;j+=i)is_prime2[j-(a-1)]=false;
        }
    }
    for(int i=1;i<=b-a;i++)
        if(is_prime2[i])prime.push_back(i+a-1);
} 
int main()
{
    ll a,b;
    cin>>a>>b;
    segment_sieve(a,b);
    cout<<prime.size()<<endl; 
}
View Code

5.快速冪

快速冪運算的演算法:反覆平方法

例如求x的22次方

22的二進位制是10110

10110從右到左分別對應x的1次方,x的2次方,x的4次方,x的8次方,x的16次方

二進位制含有1的位置是2,3,5,將這些位置對應的冪相乘就求得x得22次方

#include<iostream>
using namespace std;
typedef long long ll;
int mod_pow(ll x,ll n,ll mod)
{
    if(n==0)return 1%mod;//注意,mod可能是1!!! 
    int res=mod_pow((x%mod)*(x%mod)%mod,n/2,mod);
    if(n&1)res=res*(x%mod)%mod;
    return res;
}
int main()
{
    ll x,n,mod;
    cin>>x>>n>>mod;
    cout<<x<<"^"<<n<<" mod "<<mod<<"="<<mod_pow(x,n,mod)<<endl;
}
View Code