Mathematics in ACM

素数與歐拉函數

素性判定 & 素因子分解

给出一个数 n,如何判断这个数是否是素数?

O(n)

boolean isPrime(int n)
{
    if (n < 2)
        return false;
    for (int i = 2; i <= n / i; i++)
        if (n % i == 0)
            return false;
    return true;
}

将一个数 n 素因子分解, O(n)

ArrayList<Integer> factor = new ArrayList<Integer>();
ArrayList<Integer> power = new ArrayList<Integer>();

void makeFactors(int n, ArrayList<Integer> factor, ArrayList<Integer> power)
{
    int factorCnt = 0;
    for (int i = 2; i <= n / i; ++i) {
        if ((n % i) != 0)
            continue;
        factor.add(i);
        power.add(0);
        for (; n % i == 0; n /= i)
            power.set(factorCnt, power.get(factorCnt) + 1);
        factorCnt++;
    }
    if (n > 1) {
        factor.add(n);
        power.add(1);
        factorCnt++;
    }
    // for (int i = 0; i < factorCnt; ++i)
    //    System.out.println(factor.get(i) + " " + power.get(i));
}

問題:下面代碼的複雜度是多少?

for (int i = 1; i <= n; ++i)
    makeFactors(i, factors[i], powers[i]);

篩法

埃拉托斯特尼篩法。

final int N = 123456;
final int P = 12345;
boolean[] isPrime = new boolean[N];
int[] prime = new int[P];

void makePrime()
{
    isPrime[0] = isPrime[1] = false;
    for (int i = 2; i < N; ++i)
        isPrime[i] = true;
    prime[0] = 0; // 用此數記錄質數數目
    for (int i = 2; i < N; ++i) {
        if (isPrime[i])
            prime[++prime[0]] = i;
        for (int j = i << 1; j < N; j += i)
            isPrime[j] = false;
    }
}
  0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
      2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
2)    2 3   5   7   9    11    13    15    17    19    21    23    25
3)    2 3   5   7        11    13          17    19          23    25
4)
5)    2 3   5   7        11    13          17    19          23
6)

算法的時間複雜度是多少?

limnk=1n1kln(n)=γγ0.57721566490153286060651209

優化 O(n)

void makePrime()
{
    isPrime[0] = isPrime[1] = false;
    for (int i = 2; i < N; ++i)
        isPrime[i] = true;
    prime[0] = 0; // 用此數記錄質數數目
    for (int i = 2; i < N; ++i) {
        if (isPrime[i])
            prime[++prime[0]] = i;
        for (int j = 1; j <= prime[0] && i * prime[j] < N; ++j) {
            isPrime[i * prime[j]] = false;
            if (i % prime[j] == 0)
                break;
        }
    }
}

得到素數表之後,對一個數進行素因數分解,就變得很快。

歐拉函數

定義 Φ(n)=|{x:1xn,gcd(n,x)=1}|

如果 p 是素數,那麼 Φ(p)=p1 Φ(pk)=pkpk1

p1,p2,... 設為素數。

Φ(pk11pk22)=(pk11pk111)(pk22pk212)

Φ(Πpkii)=Π(pkiipki1i)=nΠ(11pi)

求歐拉函數/歐拉函數表。

  1. 篩選素數之後素因數分解
  2. 篩法
final int N = 12345;
int[] euler = new int[N];

void eulerTable()
{
    for (int i = 0; i < N; ++i)
        euler[i] = i;
    for (int i = 2; i < N; ++i) {
        if (euler[i] != i)
            continue;
        for (int j = i; j < N; j += i)
            euler[j] -= euler[j] / i;
    }
}

歐拉函數的應用在後面模運算部分會有介紹。

模運算

模集合

定義:對於加減運算自我封閉的數集。形式話的說,如果 M 是模,那麼,

a,bMa±bM

  1. 空集是模,僅有一個 0 的集合是模(但因為太平凡我們不考慮這些狀況)。
  2. 整數集合是模,因為任意兩個數的差或者和都是整數。
  3. 偶數集合是模,因為任意兩個偶數的差或者和都是偶數。
  4. 奇數集合不是模,因為 3 - 1 = 2 不是奇數。
  5. xZ,M(x)={kx:kZ} 是模。

這裡我們只考慮整數模。

模的性質
  1. 任何模中有 0。
  2. M 是模,a,bMan+bmM,n,mZ
  3. G(a,b)={an+bm:n,mZ} 是模。
  4. 任何模都是其中最小正整數的全部倍數集合。(考查 r=ndnd )。

定義:對於兩個整數 a,b ,模 {an+bm:n,mZ} 中的最小正整數稱作 a,b 的最大公約數,用 (a,b) 表示。

(a,b) 的性質
  1. x,yZ,s.t.(a,b)=ax+by
  2. (a,b)|ax+by
  3. e|a,e|be|(a,b)
  4. (a,b) a,b 的公共因子中最大的一個。
  5. [a,b](a,b)=ab

歐幾里得算法

問題:求 (a,b)

考查 G(a,b)={an+bm:n,mZ} ,回顧性質 4,如果 b0

(a,b)=(b,amodb)

所以得到算法:

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

一次不定方程

對於一次不定方程 ax+by=n

  1. 有整數解的充要條件是 (a,b)|n
  2. 如果 (a,b)=1 x0,y0 是方程的解。那麼方程的全部解可以表示為:
    x=x0+bty=y0at
  3. (a,b)=1,a>0,b>0 ,那麼大於 abab 的數可以表成 ax+by,x0,y0 abab 不能這樣表示。
擴展歐幾里得算法
class LinearFunction
{
    int x, y;
    int g, tmp;

    int gcd(int a, int b)
    {
        if (b == 0) {
            x = 1;
            y = 0;
            return a;
        }
        g = gcd(b, a % b);
        tmp = x;
        x = y;
        y = tmp - a / b * y;
        return g;
    }
}

gcd(a, b) 使得 x,y ax+by=(a,b) 的解。那麼 13 行之後, x,y bx+(a%b)y=(b,a%b) 的解。

那麼 bx+(abab)y=(a,b) ay+(xaby)b=(a,b)

那麼 y,xaby ax+by=(a,b) 的解。

找到 xt,yt ax+by=(a,b) 的解之後,它也就是 a(a,b)x+b(a,b)y=1 的解。利用性質 2 我們得到了後面方程的所有解。

我們關心 ax+by=n 的解,當 (a,b)|n 的時候,它有解,並且可以利用前面解決的方程的解通過線性變換得到所有解。

模逆元

In modular arithmetic, the modular multiplicative inverse of an integer a modulo m is an integer x such that

ax1(modm).

我們在約定 m 之後,可以把 x 記錄為 a1

我們發現這就是要找 ax+my=1 的解,我們一般使用 1x<m 的解 x 作為模逆元。故 (a,m)=1 的時候才存在模逆元。

所以我們可以使用擴展歐幾里得算法解決模逆元。

模逆元表

現在對於一個模 M (一般取素數),要處理 1,2,...,N(N<M) 的逆元。

除了依次使用擴展歐幾里得算法以外,還可以 O(N) 的計算。

發現: M%i=MMii=Mii(modM) ,那麼 i1=(M%i)1(Mi)=(M%i)1(MMi)(modM)

final int N = 12345;
long[] inv = new long[N];

void invTable(int M)
{
    inv[1] = 1;
    for (int i = 2; i < N; ++i)
        inv[i] = inv[M % i] * (M - M / i) % ((long) M);
}
求組合數的模值

(nk)=n!k!(nk)!

fi=i!modM gi=(i!)1modM 。那麼 (nk)=fngkgnkmodM

fi=fi1×imodM
gi=gi1×i1modM

歐拉定理

(a,m)=1aΦ(m)=1(modm)

費馬小定理

p 是素數,那麼 ap1=1(modp)

指數循環節
  1. (a,b)=1
    ab=abmodΦ(c)(modc)
  2. b>Φ(c)
    ab=abmodΦ(c)+Φ(c)(modc)

矩陣與快速冪

複習:二進制冪

long modPow(long a, long b, long M)
{
    /**@return
     * pow(a, b) % M
     */
    long result = 1L % M;
    for (; b != 0; b >>= 1) {
        if ((b & 1) != 0)
            result = result * a % MOD;
        a = a * a % MOD;
    }
    return result;
}

類似的我們可以實現二進制乘法

long modSum(long a, long b, long M)
{
    /**@return
     * (a * b) % M
     */
    long result = 0;
    for (; b != 0; b >>= 1) {
        if ((b & 1) != 0)
            result = (result + a) % M;
        a = (a << 1) % M;
    }
    return result;
}    

思考:二進制的加法有何意義?

快速冪與矩陣結合

斐波那契數列

(1110)n=(fn+1fnfnfn1)

複雜度 O(23log(n))

線性遞推

f0=a,f1=b,fi=pfi1+qfi2 ,求 fn%M

(p1q0)(fn1fn2)=(fnfn1)

(p1q0)n1(f1f0)=(fnfn1)

例三

sn=f0+f1+...+fn ,求 sn%M

111101001fn1fn2sn1=fnfn1sn

111101001n1f1f0s1=fnfn1sn

作業

  1. |{p:apb,p }| ,其中 b=5527326628,a=b105

  2. i=115527326628fimod(1012+7)

作者

張靜之

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值