菜雞學數論(一)

先確認一下入門數論的基本知識點:

  1. 最大公約數

  2. 快速冪

  3. 素數篩


輾轉相除求最大公約數:

int gcd(int a,int b)
{
    if(b==0) return a;
    gcd(b,a%b);
}

素數篩的模板(線性篩):將2到n之間的整數記錄下來,其中最小的素數是2,將表中2的倍數全部不劃去,表中剩餘的最小的數字爲3,再將3的倍數全劃去,以此類推,就能找到所有的素數。這樣做的時間複雜度只有o(nlog log n)。

int prime[maxn];///用於標記曬是否已經篩過
int pri[maxn];///用於儲存素數
void pre_prime()
{
    memset(prime,0,sizeof(prime));
    cnt = 0;
    prime[0] = prime[1] = 1;
    for(int i = 2 ; i < maxn  ; i++)
    {
        if(!prime[i]) pri[cnt++] = i;
        for(int j = 0 ; j < cnt && i * pri[j] <= maxn ; j++)
        {
            prime[i * pri[j]] = 1;
            if(i % pri[j] == 0) break;///已經篩過的不要重複篩
        }
    }
}

快速冪:數論問題經常遇見非常大的數,例如 X ^ 22,通常的思路是採用for循環,但當數很大時運算效率會很低。

採用快速冪算法可以將X^22拆爲X^16 , X^4和X^2。時間複雜度直降至o(log n)。

    typedef long long ll;
    ll quick_pow(ll x,ll n,ll mod)
    {
        ll ans=1;
        while(n>0)
        {
            if(n&1) 
                ans=ans*x%mod;
            x=x*x%mod;
            n>>=1;
        }
        return ans;
    }

歐拉函數
歐拉函數是少於或等於n的數中與n互質的數的數目。
歐拉函數的性質:它在整數n上的值等於對n進行素因子分解後,所有的素數冪上的歐拉函數之積。
歐拉函數的值  通式:φ(x)=x(1-1/p1)(1-1/p2)(1-1/p3)(1-1/p4)…..(1-1/pn),其中p1, p2……pn爲x的所有質因數,x是不爲0的整數。φ(1)=1(唯一和1互質的數(小於等 於1)就是1本身)。 (注意:每種質因數只一個。比如12=2*2*3,那φ(12)=12*(1-1/2)*(1-1/3)=4)
推論:當n爲奇數時,有φ(2n)=φ(n)。
若n是質數p的k次冪,φ(n)=p^k-p^(k-1)=(p-1)p^(k-1),因爲除了p的倍數外,其他數都跟n互質。
設n爲正整數,以 φ(n)表示不超過n且與n互素的正整數的個數,稱爲n的歐拉函數值,這裏函數φ:N→N,n→φ(n)稱爲歐拉函數。
歐拉函數是積性函數——若m,n互質,φ(mn)=φ(m)φ(n)。
特殊性質:當n爲奇數時,φ(2n)=φ(n), 證明與上述類似。
那麼如果我們想要求出其歐拉函數的值就要先對n進行質因子分解:
如果進行直接進行篩因子的話其時間複雜度爲O(n),代碼如下

int prime(int n)
{
    int rea=n;
    for(int i=2; i<=n; i++)
        if(n%i==0)//第一次找到的必爲素因子
        {
            rea=rea-rea/i;
            do
                n/=i;//把該素因子全部約掉
            while(n%i==0);//比如12=2*2*3這樣可以將所有的2除去
        }
    return rea;
}

有沒有方法將其降低複雜度呢?我們知道 “ 由於任何一個合數都至少有一個不大於根號n的素因子 ”,所以只需遍歷到根號n即可

int prime(int n)
{
    int rea=n;
    for(int i=2; i*i<=n; i++)
        if(n%i==0)//第一次找到的必爲素因子
        {
            rea=rea-rea/i;
            do
                n/=i;//把該素因子全部約掉
            while(n%i==0);
        }
    if(n>1)
        rea=rea-rea/n;//如果到最後n有就將n直接處理 
    return rea;
}

最後學長說用篩法求歐拉函數很好(可能我太菜了吧,,,,沒有領會到)

primel prime[50000];
int pri[20000];
void prim()
{
    memset(prime,0,sizeof(prime));
    prime[0]=prime[1]=1;
    int k=0;
    for(int i=2; i<50000; i++)
    {
        if(!prime[i])
            pri [k++]=i;
        for(int j=0; j<k&&i*pri [j]<50000; j++)
        {
            prime[i*p[j]=1;
                if(!(i%pri [j]))
                break;
        }
}
}//篩選法打表
int phi(int n)
{
    int rea=n;
    for(int i=0; pri [i]*pri [i]<=n; i++)//對於一些不是素數的可不遍歷
        if(n%pri [i]==0)
        {
            rea=rea-rea/n;
            do
                n/=pri [i];
            while(n%pri [i]==0);
        }
    if(n>1)
        rea=rea-rea/n;
    return rea;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章