程序设计中的数学

符号认识

  • 整除:\(a|b\),即 \(b\) 整除于 \(a\),或 \(a\)\(b\) 的因子。
  • 互质:\(m⊥n\),即 \(gcd(m,n)=1\)
  • 取整:\(\lceil x \rceil\) 向上取整,\(\lfloor x \rfloor\) 向下取整,\([x]\) 向零取整。
  • 同余:\(a \equiv b \pmod{m}\),即 \(a,b\) 在模 \(m\) 的意义下同余。
  • 累加求和:\(\sum_{i=1}^{n}i = 1+2+3+...+n\)
  • 累乘求积:\(\prod_{i=1}^{n}i = 1*2*3*...*n\)

奇数偶数

问题:求 \(1,2,3,...,n\) 的奇数和,其中 \(n≤2^{32}\)

等差数列:当首项为 \(a_1\),末项为 \(a_n\),项数为 \(n\),其前 \(n\) 项和 \(s=\frac{(a_1+a_n)* n}{2}\)

分析:循环肯定TLE,明显存在规律,有\(n\) 为奇数和偶数时有不同情况,分情况讨论;

  1. \(n\) 是奇数的时候,\(s=1+3+5+...+n;\)
    假设有 \(k\) 项元素,则 \(1+(k-1)*2=n\),解出 \(k=\frac{n-1}{2}+1=\frac{n+1}{2}\)
    \(s=\frac{(1+n)*k}{2}=\frac{(n+1)^2}{4}\)
    在程序中 \(2^{64}\) 会溢出,所以可以写成 \(((n+1)/2) * ((n+1)/2)\).

  2. \(n\) 是偶数的时候,\(s=1+3+5+...+n-1;\)
    假设有 \(k\) 项元素,则 \(1+(k-1)*2=n-1\),解出 \(k=\frac{n-1-1}{2}+1=\frac{n}{2}\)
    \(s=\frac{(1+n-1)*k}{2}=\frac{(n)^2}{4}\)
    在程序中 \(2^{64}\) 会溢出,所以可以写成 \((n/2) * (n/2)\)

算术平方根

\(x*x=n\),也可以记为 \(x=\sqrt{n}\),其中\(\sqrt{n}\) 就被称为对 \(n\) 开根号。

\(x\) 就被称为 \(n\) 的算术平方根。

当给定 \(n\),如何准确的求解 \(x\) 呢?

  • 牛顿迭代法

可以假定一个解 \(x_0\),那么可以得到另外一个解:\(x_1=n/x_0\),其中 \(x_0*x_1=n\)

我们可以取两个解的平均值作为新解,再按照同样的方式继续推导满足误差的解。

于是得到迭代公式:\(x=(x+n/x)/2\);

  • 二分法

确定答案区间,二分区间,直到满足误差的解。

double sqrt1(double n){
    double x=n/2;
    while(fabs(x*x-n) > eps) x = (x+n/x)/2;
    return x;
}
double sqrt12(double n){
    double x=n/2;
    for(int i=1; i<=100; i++) x = (x+n/x)/2;
    return x;
}
double sqrt2(double n){
    double l=0, r=n/2;
    while(r-l > eps){
        double mid=(l+r)/2;
        if(mid*mid >= n) r=mid;
        else l=mid;
    }
    return r;
}

素数

定义:大于 \(1\) 的自然数中,因子只有 \(1\) 和其本身的数,称为素数或质数。

素数判定:若 \(i|x\),则 \(\frac{x}{i}|x\),所以只需要检测 \(i \in [2,\sqrt{x}]\) 是否满足 \(i|x\) 即可,复杂度 \(O(\sqrt{x})\)

由于 \(i*i\leq m\) 容易爆 \(int\),改写为除法形式 \(i\leq m/i\)

bool isprime(int m){
    for(int i=2; i<=m/i; i++) if(m%i==0) return 0;
    return n>1;
}

朴素筛法,原理:一个数的倍数一定不是素数,时间复杂度 \(O(nlogn)\)

埃式筛法,原理:素数的倍数一定不是素数+算数基本定理,时间复杂度 \(O(nloglogn)\)

欧拉筛法,原理:每一个数都具有唯一最小素因子,时间复杂度 \(O(n)\)

const int N=1e6+10;
int st[N],primes[N],pn=0; // st[i]=1 表示i被筛除
void simple_sieve(int n){ // 普通筛法 O(nlogn)
    st[0] = st[1] = 1;
    for(int i=2; i<=n; i++){
        if(!st[i]) primes[++pn]=i;
        for(int j=2; j<=n/i; j++) st[i*j]=1;
    }
}
void eratosthenes_sieve(int n){ // 埃式筛法 O(nloglogn)
    st[0] = st[1] = 1;
    for(int i=2; i<=n; i++){
        if(!st[i]){
            primes[++pn]=i;
            for(int j=2; j<=n/i; j++) st[i*j]=1;
        }
    }
}
void euler_sieve(int n){ // 欧拉筛法 O(n)
    st[0] = st[1] = 1;
    for(int i=2; i<=n; i++){
        if(!st[i]) primes[++pn]=i;
        for(int j=1; primes[j]<=n/i; j++){
            st[primes[j]*i]=1;
            if(i%primes[j]==0) break; // 最小素因子
        }
    }
}

约数

质因数分解:算术基本定理,唯一分解定理

定义:一个大于 \(1\) 的正整数可以唯一分解为若干个质数的乘积。

记:\(N=\prod_{i=1}^k p_i^{ai} = p_1^{a_1}*p_2^{a_2}...*p_k^{a_k}\),其中 \(p_1<p_2<...<p_k\), \(p_i\) 是质数。

约数个数定理\(num=\prod_{i=1}^{k}(a_i+1)=(a_1+1)(a_2+1)...(a_k+1);\)

约数和定理\(sum=\prod_{i=1}^{k}\sum_i^{k}p_i^{a_k}=(p_1^0+p_1^1+…p_1^{a_1})(p_2^0+p_2^1+…p_2^{a_2})…(p_k^0+p_k^1+…p_k^{a_k});\)

  • 证明:

\(p_1^{a_1}\) 的约数有:\(p_1^0, p_1^1,p_1^2,...,p_1^{a_1}\),共 \(a_1+1\) 个;

\(p_2^{a_2}\) 的约数有:\(p_2^0, p_2^1,p_2^2,...,p_2^{a_2}\),共 \(a_2+1\) 个;

\(p_k^{a_k}\) 的约数有:\(p_k^0, p_k^1,p_k^2,...,p_k^{a_k}\),共 \(a_k+1\) 个;

根据乘法原理

\(num=\prod_{i=1}^{k}(a_i+1)=(a_1+1)(a_2+1)...(a_k+1);\)

\(sum=\prod_{i=1}^{k}\sum_i^{k}p_i^{a_k}=(p_1^0+p_1^1+…p_1^{a_1})(p_2^0+p_2^1+…p_2^{a_2})…(p_k^0+p_k^1+…p_k^{a_k});\)

map<int,int> prime_factorization(int m){// 质因数分解
    map<int,int> mp;
    for(int i=2; i<=m/i; i++){
        if(m%i==0){
            int num=0;
            while(m%i==0) m/=i, num++;
            mp[i]=num;
        }
    }
    if(m>1) mp[m]=1;
    return mp;
}
LL num(map<int,int> m){
    LL res=0;
    for(auto u:m) res=res*(u.second+1)%mod;
    return res;
}
LL sum(map<int,int> m){
    LL res=1;
    for(auto u:m){
        LL a=u.first, b=u.second, t=1;
        while(b--) t=(t*a+1)%mod;
        res = res*t%mod;
    }
    return res;
}

欧几里德算法/辗转相除法

原理:\(a,b\) 的最大公约数等于 \(b,a\%b\) 的最大公约数。

记作:\(gcd(a,b)=gcd(b,a\%b), gcd(a,0)=a.\)

  • 证明:

\[\begin{aligned} 设 p &=gcd(a,b),则 a=p*a',b=p*b', gcd(a', b')=1;\\ a\%b &=a-\lfloor{\frac{a}{b}}\rfloor*b=p*a'-\lfloor{\frac{a}{b}}\rfloor*p*b',所以 p|gcd(b,a\%b)。\\ \\ 但如&何证明 p 最大?只需要证明 d =gcd(b', a'-\lfloor{\frac{a}{b}}\rfloor*b')=1,\\ b' &=d*b'',a'-\lfloor{\frac{a}{b}}\rfloor*b'=d*c,\\ a' &=\lfloor{\frac{a}{b}}\rfloor*d*b'' + d*c \\ &=d*(\frac{a}{b}*b''+c) \\ & 所以 d 是 a', b'的公约数,又 gcd(a', b')=1, 所以 d=1。 \end{aligned} \]

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

更相减损术

原理:\(a,b\) 的最大公约数等于 \(b,a-b\) 的最大公约数 \((a>b)\)

记作:\(gcd(a,b)=gcd(b,a-b), gcd(a,0)=a.\)

  • 证明:

对于 \(a,b\) 的任意公约数 \(d\),有 \(d|a、d|b、d|(a-b)\),所以 \(d\) 也是 \(b,a-b\) 的公约数。

\(a,b\) 的公约数与 \(b,a-b\) 的公约数集合相同,于是最大公约数也相同。

也可以换等式证明:gcd(a,b)=gcd(a,a-b)
设a=k1*d,b = k2*d,则gcd(a, b)=d,gcd(k1, k2)=1,(a>b);
则a-b=k1*d-k2*d=(k1-k2)*d;
则gcd(b, a-b) = d = gcd(a, b);

int gcd(int a,int b){
    if(a<b) swap(a,b);
    return b ? gcd(b,a-b):a;
}

最小公倍数

定理:两个数的乘积等于它们最大公约数与最小公倍数的乘积。

证明:a*b=gcd(a,b)*lcm(a,b)
gcd(a,b) 表示a,b的最大公约数;
lcm(a,b) 表示a,b的最小公倍数;
令 a=k1*d, b=k2*d, 则:gcd(a,b)=d;
又lcm(a,b)=k1*k2*d(k1,k2互质)
则得证:a*b= gcd(a,b)*lcm(a,b).

int lcm(int a,int b){
    return a/gcd(a,b)*b;
}

扩展欧几里得

原理:对于给定的两个整数 \(a,b\),必存在整数 \(x,y\),使得 \(ax+by=gcd(a,b)\)

int exgcd(int a,int b,int &x,int &y){
    if(!b){ x=1, y=0; return a; }
    int d=exgcd(b,a%b, y, x);
    y -= a/b*x;
    return d;
}

欧拉函数

\(1∼N\) 中与 \(N\) 互质的数的个数被称为欧拉函数,记为 \(\varphi(N)\)

\(N=p_1^{a_1}p_2^{a_2}…p_k^{a_k}\)\(\varphi(N)=N(1-\frac{1}{p_1})(1-\frac{1}{p_2})...(1-\frac{1}{p_k})\)

  • 证明

欧拉函数是一个积性函数,当 \(m,n\) 互质时,\(φ(mn)=φ(m)∗φ(n)\)

根据唯一分解定理知 \(N=p_1^{a_1}p_2^{a_2}…p_k^{a_k}\),其中 \(p_1<p_2<...<p_k\), \(p_i\) 是质数。

\(φ(N)=φ(p_1^{a_1})∗φ(p_2^{a_2})...*φ(p_k^{a_k})\)

\(p_i^{a_i}\) 不互质的数有 \(p_i,2p_i,3p_i,...,p_i^{a_i-1}p_i\)\(p_i^{a_i-1}\) 个,即 \(φ(p_i^{a_i})=p_i^{a_i}-p_i^{a_i-1}\)

\[\begin{aligned} \varphi(N)&=\varphi(p_1^{a_1})*\varphi(p_k^{a_k})*...*\varphi(p_k^{a_k}) \\ \\ &=(p_1^{a_1}-p_1^{a_1-1})*(p_2^{a_2}-p_2^{a_2-1})*...*(p_k^{a_k}-p_k^{a_k-1})\\ \\ &=p_1^{a_1}(1-\frac{1}{p_1})*p_2^{a_2}(1-\frac{1}{p_2})*...*p_k^{a_k}(1-\frac{1}{p_k})\\ \\ &=p_1^{a_1}p_2^{a_2}...p_k^{a_k}*(1-\frac{1}{p_1})(1-\frac{1}{p_2})...(1-\frac{1}{p_k})\\ \\ &=N*(1-\frac{1}{p_1})(1-\frac{1}{p_2})...(1-\frac{1}{p_k})\\ \\ &=N*\prod_{i=1}^{k}(1-\frac{1}{p_i})=N*\prod_{i=1}^{k}\frac{p_i-1}{p_i} \end{aligned} \]

int euler(int m){
    int res=m;
    for(int i=2; i<=m/i; i++){
        if(m%i==0){
            while(m%i==0) m/=i;
            res=res/i*(i-1);
        }
    }
    if(m>1) res=res/m*(m-1);
    return res;
}

欧拉筛法求欧拉函数

质数 \(i\) 的欧拉函数即为 \(phi[i]=i-1\)

\(phi[primes[j]*i]\) 分为两种情况:

\(i\%primes[j]==0\)\(primes[j]\)\(i\) 的最小素因子,也是 \(primes[j]*i\) 的最小质因子,因此 \(1 - \frac{1}{primes[j]}\)\(phi[i]\) 中计算过了,结果为 \(phi[i] * primes[j]\)

\(i \% primes[j] != 0\)\(primes[j]\) 不是 \(i\) 的质因子,只是 \(primes[j]*i\) 的最小质因子,需要补上\(1-\frac{1}{primes[j]}\) 这一项,结果 \(phi[i]*primes[j]*(1-\frac{1}{primes[j]})=phi[i]*(primes[j]-1)\)

void euler_sieve(int n){ // 欧拉筛法 O(n)
    phi[1]=1;
    for(int i=2; i<=n; i++){
        if(!st[i]) primes[++pn]=i, phi[i]=i-1;
        for(int j=1; primes[j] <=n/i; j++){
            st[primes[j]*i]=1;
            if(i%primes[j]==0) {
                phi[primes[j]*i]=phi[i]*primes[j];
                break; // 最小素因子
            }
            phi[primes[j]*i]=phi[i]*(primes[j]-1);
        }
    }
}

裴蜀定理

裴蜀定理:方程 \(ax+by=c\) 有整数解的充要条件是 \(gcd(a,b)|c\)

\[\begin{aligned} 裴蜀定理的证明: &1. 必要性:a*x+b*y=c 有整数解 -> gcd(a,b)|c。 \\ &设 gcd(a,b)=p, a=a'*p,b=b'*p,\\ &则 c=a'*p*x+b'*p*y=p*(a'*x+b'*y), \\ &\therefore gcd(a,b)|c. \\ \\ &2. 充分性:gcd(a,b)|c -> a*x+b*y=c 有整数解。\\ &待证 \end{aligned} \]

同余方程

\[\begin{aligned} &设 a*x+b*y= gcd(a,b), b*x1+a\%b*y1=gcd(b,a\%b);\\ &b*x1+(a-\lfloor\frac{a}{c}\rfloor *b) *y1=a*y1+b*(x1-\lfloor\frac{a}{b}\rfloor*y1);\\ &\therefore x=y1, y=(x1-\lfloor\frac{a}{b}\rfloor*y1);\\ &当 x=1, y=0时,为一组特解。\\ \end{aligned} \]

快速幂

\(a^n \ mod \ p\)

两种思路:二分递归、进制原理

二分递归证明如下:

\(n\) 为偶数的时候:\(a^{n} = a^{n/2} * a^{n/2}\)

\(n\) 为奇数的时候:\(a^n = a^{n/2} * a^{n/2} * a\) ;

经常对于这样的题目,会说对某个数取模,这里我们可以记住以下几个取余运算相关性质:

(a + b) % p = (a % p + b % p) % p
(a - b) % p = (a % p - b % p) % p
(a * b) % p = (a % p * b % p) % p
除法不满足,需要使用逆元求解

这里我们选择第一个进行证明。

\[\begin{aligned} 证明:&(a+b) \% p = (a \% p + b \% p) \% p; \\ \\ 令:a &= k1 * p + r1 , \quad b= k2 * p + r2, \\ \\ (a+b)\%p &= (k1*p+r1 + k2*p+r2) \% p \\ \\ &= ((k1+k2)*p + r1 + r2) \% p \\ \\ &= (r1+r2) \% p \\ \\ (a\%p + b\%p) \% p &= ((k1*p + r1)\%p + (k2*p + r2)\%p) \% p \\ \\ &= (r1+r2) \% p ,原式得证。 \end{aligned} \]

由于减法其实就是加上一个负数,乘法就是多个加法,所以也可以伪证另外两条性质。

注意:快速幂运算是呈几何倍数的增加,容易爆范围,开 long long。

LL halfpow(LL a, LL n, LL p){
    if(n==0) return 1;
    LL ans=halfpow(a, n/2);
    ans = ans*ans%p;
    if(n&1) ans=ans*a%p;
    return ans;
}
  • 二进制原理实现快速幂(二分快速幂思想不变,但是从进制原理的角度去理解)

  • \(a^{n} = a^{XB}\)

  • \(a^{11} = a^{8+2+1} = a^{2^{3} +2^{1} + 2^{0}} = a^{1011B}\)

发现仅当二进制位为 \(1\) 时,才会对其进行累乘,而累乘的数值为该进制位的权值次幂(基数)。

比如:\(1011\),从右向左,仅当第 \(0\) 位,第 \(1\) 位,第 \(3\) 位时才进行累乘计算。

算法流程:
    初始值:ans=1, a=a, X=1011
    如果X的二进制最低位为1,即 lowbit(X)=1,则 ans = ans*a;
    基数一直: a = a*a;

主要在于:a = a*a 的理解
    次数:数值
    0:a    = a^(2^0)
    1:a^2  = a^(2^1)
    2:a^4  = a^(2^2)
    3:a^8  = a^(2^3)
    4:a^16 = a^(2^4)
LL fastpow(LL a, LL n, LL p){
    LL ans=1;
    while(n){
        if(n&1) ans=ans*a%p;
        a = a*a%p;
        n >>= 1; // n/=2;
    }
    return ans;
}

费马小定理:如果 \(p\) 是一个质数,而整数 \(a\) 不是 \(p\) 的倍数,则有 \(a^{p-1}≡1(mod\ p)\)

乘法逆元

若整数 \(b,m\) 互质,并且对于任意的整数 \(a\),如果满足 \(b|a\),则存在一个整数 \(x\),使得 \(a/b≡ax(mod\ m)\),则称 \(x\)\(b\) 的模 \(m\) 乘法逆元,记为 \(b^{−1}(mod\ m)\)

\(b\) 存在乘法逆元的充要条件是$ b$ 与模数 \(m\) 互质。

由费马小定理可知,\(a*a^{p-2}≡1(mod\ p);\)

当模数 \(m\) 为质数时,\(b^{m−2}\%p\) 即为 \(b\) 的乘法逆元,可以用快速幂求解。

当模数 \(m\) 不是质数时,可以用扩展欧几里得算法求逆元:

\(a\) 有逆元的充要条件是 \(a,p\) 互质,即 \(gcd(a,p)=1\)

假设 \(a\) 的逆元为 \(x\), 则 \(ax≡1(mod \ p)\),等价于:\(ax+py=1\)

\(exgcd(a,p,x,y),\quad x=(x+p)\%p\)

posted @ 2021-11-06 17:31  HelloHeBin  阅读(517)  评论(0编辑  收藏  举报