數學問題

1.素數篩法

題目:
輸入一個整數n(2<=n<=10000),輸出所有1到這個整數之間(不包括1和這個整數),各位爲1的整數,如果沒有則輸出-1。
輸入:
100
輸出:
11 31 41 61 71
來源:
2008年北京航空航天大學計算機研究生機試真題


思路:
我們知道若一個數X是素數,那麼2*X,3*X,4*X……必然不是素數,當我們判斷出X是素數之後,便可標記K*X爲非素數(K>=1)。所以,從2開始遍歷2到10000的所有整數,若當前整數沒有因爲它是某個小於其的素數的倍數而被標記成非素數,則判定其爲素數,並標記其它的所有的倍數爲非素數,然後繼續遍歷下一個數,直到遍歷完2到10000之間的所有整數。此時,沒有被標記成非素數的數即爲非素數。


代碼:

#include <bits/stdc++.h>

using namespace std;

int prime[10010];//保存所得素數
int primesize;//保存素數個數
bool mark[10010];//此時mark初始化爲false 若mark[i]爲true 表示i爲非素數

void init()
{
    primesize = 0;
    for(int i = 2; i <= 10000; i++)
    {
        if(mark[i] == true) continue;
        prime[primesize++] = i;
        for(int j = i*i; j <= 10000; j += i)//標記該數的倍數爲非素數
            mark[j] = true;
    }
}
int main()
{
    init();
    while(scanf("%d", &n) != EOF)
    {
        bool isoutput = false;//標記是否有輸出
        for(int i = 0; i < primesize; i++)
            if(prime[i] < n && prime[i] % 10 == 1)
            {
                if(!isoutput)
                {
                    printf("%d", prime[i]);
                    isoutput = true;
                }
                else
                {
                    printf(" %d", prime[i]);
                }
            }
        if(!isoutput)
            printf("-1\n");
        else
            printf("\n");
    }

    return 0;
}

2.分解素因數

題目: 輸入一個正整數n(1< n <10^9),輸出n的質因數的個數。相同質因數需重複計算。如120=2*2*2*3*5,共有5個質因數。
輸入:
120
輸出:
5
來源:
2007清華大學計算機研究生機試真題


思路:
對任意正整數X,均可進行素因數分解,得到如下形式:
這裏寫圖片描述
所以e1+e2+…+en即爲所求結果。


代碼:

#include <bits/stdc++.h>

using namespace std;
int prime[100001];
int primesize;
bool mark[100001];

void init()
{
    primesize = 0;
    for(int i = 2; i <= 100000; i++)
    {
        if(mark[i] == true) continue;
        prime[primesize++] = i;//i爲素數
        if(i > 1000) continue;
        for(int j = i*i; j <= 100000; j += i)
            mark[j] = true;
    }
}//篩出2到100000之間的素數


int main()
{
    init();
    int n;
    while(scanf("%d", &n) != EOF)
    {
        if(n <= 0) break;
        int factor[30];//保存分解出的素因數
        int exp[30];//保存分解書的素因數對應的冪指數
        int facsize = 0;//保存分解出的素因數的個數
        for(int i = 0; i < primesize; i++)//測試每一個素數
        {
            if(n % prime[i] == 0)
            {
                factor[facsize] = prime[i];
                exp[facsize] = 0;
                while(n % prime[i] == 0)//計算該素因數的個數
                {
                    exp[facsize]++;
                    n /= prime[i];
                }
                facsize++;
            }
            if(n == 1) break;//若易被分解成1 則分解提前終止
        }
        if(n != 1)//存在大於100000的素因數
        {
            factor[facsize] = n;//記錄該大素因數
            factor[facsize++] = 1;//其冪指數只能是1
        }
        int ans = 0;
        for(int i = 0; i < facsize; i++)
            ans += exp[i];//累加各素因數的冪指數
        printf("%d\n", ans);
    }

    return 0;
}

總結:
爲什麼素數篩法只需要篩到100000即可,而不是與輸入數據同規模的1000000000。這是因爲:n至多存在一個大於sqrt(n)的素因素,(否則兩個大於sqrt(n)的數相乘必大於n)。這樣,我們只需將n所有小於sqrt(n)的素數從n中除去,剩餘部分必爲該大素因數,且冪指數也只能爲1。
BTW,當我們完成素因數分解後,我們同樣可以確定被分解整數的因數個數爲(e1+1)*(e2+1)*…*(en+1)(由所有素因數不同組合數得出)。

3.整除問題(n!分解素因數)

題目:
給定n,a求最大的k,使n!可以被 a^k整除但不能被a^(k+1)整除。其中2<=n,a<=1000。
輸入:
6 10
輸出:
1
來源:
2011上海交通大學計算機研究生機試真題


思路:
n!和a^k可能非常龐大,甚至超過long long的表示範圍。
思考若整數a能整除整數b則他們之間有什麼關係?不防對a和b分解素因數:
這裏寫圖片描述

若 a 能整除 b,則該式爲一個整數,但考慮到若素數 p1 能夠整除素數 p2, 則 p1 必等於 p2(兩個素數必互質)。則我們可以得出如下規律:
若 a 存在素因數 px 則 b 也必存在該素因數,且該素因數在 b 中對應的冪指 數必不小於在 a 中的冪指數。
現我們設 x = n!,y = a ^ k,我們對 n!與 a 分解素因數,令:
這裏寫圖片描述
相應的我們也可以得到 a 的 k 次的素因數分解情況爲:
這裏寫圖片描述
即我們要確定最大的非負整數 k,使 a 中任一素因數的冪指數的 k 倍依舊小 於或等於該素因數在 x 中對應的冪指數。要求得該 k,我們只需依次測試 a 中每 一個素因數,確定 b 中該素因數對應的冪指數是 a 中冪指數的幾倍(利用整數除法),這樣所有倍數中最小的那個即爲我們要求的 k。
分析到這裏,剩餘的工作似乎只剩下對 a 和 n!分解素因數,對 a 分解素因 數我們在前文中已經探討過了。那麼多 n!呢?千萬不要指望將 n!計算出來後 再類似 a 一樣對其分解質因數,由於 n!數值非常巨大(當 n>30 時),想要這樣 操作幾乎是不可能的,那麼我們該如何對其分解素因數呢?試着考慮 n!中含有 素因數 p 的個數,即確定素因數 p 對應的冪指數。我們易知,n!中包含了 1 到n 區間內所有整數的乘積,這些乘積中每一個 p 的倍數(包括其本身)都將對 n! 貢獻至少一個 p 因子,且我們知道在 1 到 n 中 p 的倍數共有 n/p(整數除法)個, 則 p 的因子數至少爲 n/p 個,即有 n/p 個整數至少貢獻了一個 p 因子。那麼有多 少個整數將貢獻至少兩個 p 因子呢,有了以上的分析讀者應該知道所有 p*p 的倍 數將爲 n!貢獻至少 2 個 p 因子,且這樣的整數有 n/(p*p);同理 p*p*p 的倍數將 貢獻至少 3 個,這樣的數有 n/(p*p*p);p 的四次方的倍數將貢獻至少 4 個,這樣 的數由(n/(p*p*p*p))。那麼分析出這些結果對我們分解 n!的質因數有什麼幫 助呢,且看如下過程。
1.計算器清零,該計數器表示 n!中將有幾個 p 因子,即 n!分解質因數後 素因子 p 對應的冪指數。
2.計算 n/p,有 n/p 個整數可以向 n!提供一個 p 因子,則計數器累加 n/p。若 n/p 爲 0,表示沒有一個整數能向 n!提供一個或一個以上的 p 因子,分解結束。
3.計算 n/(p*p),有 n/(p*p)個整數可以向 n!提供兩個 p 因子,但它們在 之前步驟中(p 的倍數必包括 p*p 的倍數)每個數都已經向計數器累加了 1 個 p 因子,所以此處他們還能夠向計數器貢獻 n/(p*p)個素因子(即每個再貢獻一 個),累加器累加 n/(p*p)。若 n/(p*p)爲 0,表示沒有一個整數能向 n!提供 兩個或兩個以上的 p 因子,分解結束。
4.計算 n/(p*p*p),有 n/(p*p*p)個整數可以向 n!提供三個 p 因子,但它 們在之前步驟中(p 和 p*p 的倍數必包括 p*p*p 的倍數)每個數都已經計算向計 數器累加了 2 個 p 因子,所以此處他們還能夠向計數器貢獻 n/(p*p*p)個素因 子,累加器累加 n/(p*p*p)。若 n/(p*p*p)爲 0,表示沒有一個整數能向 n!提 供三個或三個以上的 p 因子,分解結束。
依次累加 p 的更高次的倍數能夠再提供的素因子數,即每次向計數器累加 n/(p^k),直到 n/(p^k)變爲 0,表示沒有整數能提供更多的 p 因子,關於 p 的分解 結束。
完成這些步驟後,就能計算出 n!中所有 p 的因子數,即計數器中累加的結 果即爲素因數 p 的冪指數。
有了對 n!分解素因數的方法,我們只需依次遍歷可能成爲其素因子(小於
等於 n 的所有素數)的素數,計算它們所對應的冪指數,即可完成對 n!的素因數分解。


代碼:

#include <bits/stdc++.h>

using namespace std;

int prime[1010];
int primesize;
bool mark[1010];

void init()
{
    primesize = 0;
    for(int i = 2; i <= 1000; i++)
    {
        if(mark[i] == true) continue;
        prime[primesize++] = i;
        for(int j = i*i; j <= 1000; j += i)
            mark[j] = true;
    }
}

int cnt1[1010];//保存n!分解的素因數的指數
int cnt2[1010];//保存a分解的素因數的指數

int main()
{
    init();
    int n, a;
    while(scanf("%d%d", &n, &a) != EOF)
    {
        for(int i = 0; i < 1010; i++)
            cnt1[i] = cnt2[i] = 0;//計數器清零
        for(int i = 0; i < primesize; i++)
        {//對n!進行素因數分解
            int t = n;
            while(t)
            {
                cnt1[i] += t / prime[i];
                t /= prime[i];
            }//依次計算t/prime[i]^k,累加其值,直到t/prime[i]^k變爲0
        }
        int ans = 0x3f3f3f3f;//初始化爲較大的一個值
        for(int i = 0; i < primesize; i++)
        {//對a進行素因數分解
            while(a % prime[i] == 0)
            {
                cnt2[i]++;
                a /= prime[i];
            }
            if(cnt2[i] == 0) continue;//該素數對應指數爲0,跳過
            if(cnt1[i] / cnt2[i] < ans)//找到最小的商
                ans = cnt1[i] / cnt2[i];
        }

        printf("%d\n", ans);

    }

    return 0;
}

4.二分求冪

題目: 求A^B的最後三位數表示的整數。其中(1<=A,B<=10000),如果 A=0, B=0,則表示輸入數據的結束,不做處理。
輸入:
2 3
12 6
6789 10000
0 0
輸出:
8
984
1
來源:
九度教程第 57 題


思路:
a 的 2^k 次是可以由 a 的 1 次不斷求平方取得的。我們的目標即分解 a 的 b 次變爲若干個 a 的 2^k 次的積,並儘可能減少分解結果的個數。在指數層面即分解 b 爲若干個 2^k 的和, 並儘可能減少分解結果的個數。分解 b 爲若干個 2^k 的和且分解個數最小,這便是求 b 的二進制數。在求得 b 的二進制數後,各個二進制位爲 1 的數位所代表的權重即是分解的結果。以 2 的 31 次方爲例,我們首先求得 31 的二進制數 11111,在二進制表達式中 31 即被 表達成(11111) = 2^0 + 2^1 + 2^2 + 2^3 + 2^4,這就是我們所需的分解結果。


代碼:

#include <bits/stdc++.h>

using namespace std;

int main()
{
    int a, b;
    while(scanf("%d%d", &a, &b) != EOF)
    {
        if(a == 0 && b == 0) break;
        int ans = 1;
        while(b != 0)//對b轉換二進制過程未結束
        {
            if(b % 2 == 1)
            {
                ans *= a;
                ans %= 1000;//只保留後三位
            }
            b /= 2;
            a *= a;
            a %= 1000;//只保留後三位
        }
        printf("%d\n", ans);
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章