一週總結 數 論1

1 素數的相關問題
1 不大於n的素數的個數約爲n/ln(n)個;
2 兩個數互爲素數的條件 gcd(a,b)=1;
gcd

int gcd(int a,int b)
{return b?gcd(b,a%b):a;}

判斷一個數是否是素數

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

求1-n之間的數有多少個素數。

bool ispirme[100000];
void eratos(int n)
{ int i,j;
isprime[0]=0;
isprime[1]=0;
for(int i=2;i<=n;i++)
isprime[i]=1;
for(int i=2;i*i<=n;i++)
if(isprime[i])
 for(int j=i*i;j<=n;j=j+i)
isprime[j]=false;
}

每個數都能分解爲素數相乘的形式。
n=p1 r1p2r2 p3r3…;
p爲素數 r爲指數。
應用 判斷兩個大數是否相等可以將大數拆分,看看其分解開的素因數是否相等。(double不適合大數的表示,精度問題)

int k=0;
        for(int i=2; i*i<=n;)
        {
            if(n%i==0)
            {
                p[k]=i;
                r[k]=0;
                while(n%i==0)
                {
                    r[k]++;
                    n=n/i;
                }
                k++;
            }
            if(i==2)
                i++;
            else
                i=i+2;
        }
        if(n!=1)
        {
            p[k]=n;
            r[k]=1;
            k++;
        }

結合篩法優化素數分解

bool isprime[10000];
int minfacter[100000];
//表示每個數的最小素因數。
void eratos(int n)
{ int i,j;
isprime[1]=isprime[0]=0;
minfactor[0]=minfactor[1]=-1;
for(int i=2;i<=n;i++)
{isprime[i]=1;
minfactor[i]=i;
}
for(int i=2;i*i<=n;i++)
{if(isprime[i])
{for(int j=i*i;j<=n;j=j+i)
{isprmie[j]=false;
if(minfactor[j]==j)
minfactor[j]=i;
}
}
vector <int> factorint x)
{vector<int> res;
while(x>1)
{ ret.push_back(minfactor[x]);
x/=minfactor[x];
}
return res;
}

n=p r1 p2 r2 p3 r3…
n的正約數的個數爲(r1+1)(r2+1)…(rn+1)
r爲指數
n 的所有正約數的和爲(1+p1+p1^2+…p1r1)*…(1+pk+pk2+…pkrk);
求n的所有因數的和

#include <iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#define ll long long
using namespace std;
long long power(long long base, long long power)
{
    long long result = 1;
    while (power > 0) {
        if (power & 1) {
            result = result * base;
        }
        power >>= 1;
        base = (base * base);
    }
    return result;
}
ll sum(ll p,ll n)
{
    if(n==0) return 1;
    if(n%2) return (sum(p,n/2)*(1+power(p,n/2+1)));
    else
    {
      return sum(p,n/2-1)*(1+power(p,n/2+1))+power(p,n/2);
    }
}
int main()
{
    int t;
    scanf("%d",&t);
    int p[10005]= {0},r[10005]= {0};
    while(t--)
    {
        ll n;
        scanf("%lld",&n);
        ll tt=n;
        int k=0;
        for(int i=2; i*i<=n;)
        {
            if(n%i==0)
            {
                p[k]=i;
                r[k]=0;
                while(n%i==0)
                {
                    r[k]++;
                    n=n/i;
                }
                k++;
            }
            if(i==2)
                i++;
            else
                i=i+2;
        }
        if(n!=1)
        {
            p[k]=n;
            r[k]=1;
            k++;
        }
        ll ans=1;
        for(int i=0; i<k; i++)
        {
            ans=ans*sum(p[i],r[i]);
        }
        printf("%lld\n",ans);
    }
    return 0;
}

最大公約數
gcd(a,b)=gcd(a,ka+b);
1 佔轉相除法

int gcd(int a,int b)
{return b?gcd(b,a%b):a;}

2 更相減損術

int gcd(int m,int n)
{if(m==n)return m;
if(m<n) return gcd(n,m);
if(m&1==0)
return (n&1==0)?2*gcd(m>>1,n>>1):gcd(m>>1,n);
return (n&1==0)?gcd(m,n>>1):gcd(n,m-n);
}

最小公倍數
lcm=ab/gcd(a,b);
lcm(ma,mb)=m
lcm(a,b);
lcm(a,b,c)=lcm(lcm(a,b),c);

ans=1;
for(int i=1;i<=n;i++)
{cin>>a[i];
ans=ans*a[i]/gcd(ans,a[i]);
}
cout<<ans<<endl;

和%相關的問題
1 在只含有加法和乘法的式子中,如果最後的運算結果需要對p 取模,那麼可以在計算中隨時取模。(a+b)%m=(a%m+b%m)%m;
2 4%3=1,2%3=2,1-2=-1 ,(4%3-2%3+3)%3;(a-b)%m=(a%m-b%m+m)%m;
總結
數論的東西往往用來解決值很大數據量很多很耗時間的問題,採用的策略往往是分解爲數的相乘,取模,用小小區間映射大區間,用小數映射大的數,小數和大數的聯繫可以解決很多問題。一個數的素因子最大爲根號下該數,那麼數的範圍就會被大大降低,而且,素數的倍數可以計算出合數,那麼小的區間就可以管理大的區間。需要取模的問題每一步都取模,能避免中間的值所會出現的數據溢出的問題。數組下表與取模結合,可以使得環狀重複出現的值重複使用,(循環要從零開始)。

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