基本数学知识

前言

省选以前的复习吧。。。(尽管一定选不上233333)

总结一下常用的数学知识。

线性筛素数

简介

线性筛素数是比较常用的算法之一。比普通的暴力要节约大量时间开销。

算法原理

其实是这样的:

每个数字都可以被打上标记。初始状态下,所有数字都没有标记

然后在扫描到一个数字后,如果这个数字有标记,则直接跳过这个数字。

如果没有标记,先将这个数字存储为质数,然后将这个数的所有整数倍的数字(不包含自己)都打上标记。这样做的结果是将所有质数的倍数全部筛除。

这样做,筛选 N 以内的质数,只需要 O(N) 的时间开销即可。预处理开销 O(N) ,访问开销 O(1)

另外,还有优化方案。

由于要将一个非质数分解成两个数的乘积,必定有其中一个数是比 sqrt(N) 小。
而所有非质数都能够被分解为质数的乘积。

所以,我们可以只预处理 0sqrt(n) 的素数,将它们存储到一个数组里面,然后每次询问 sqrt(n)N 区间的素数时,只需要将这个数与 0sqrt(n) 的素数进行整除验证,如果没有能整除的素数,则这个数就是一个素数。

这样做,可以节约预处理的时间开销和空间开销(一大部分呢)。预处理开销 O(sqrt(N)) ,访问 0sqrt(n) 开销 O(1) ,访问 sqrt(n)N 开销 O(Primes(0sqrt(n)))

编程实现

CPP代码

const int range=10000000;
int prime[10000],primes=0,handle_range;
bool notpri[10000];
void makeprime()
{
    handle_range=sqrt(range)+10;
    notpri[0]=notpri[1]=true;
    for(int i=2;i<=handle_range;i++)
    {
        if(notpri[i]) continue;
        for(int j=i*2;j<=handle_range;j+=i)
        {
            notpri[j]=true;
        }
        prime[++primes]=i;
    }
}
bool isprime(int x)
{
    if(x<=handle_range) return !notpri[x];
    for(int i=1;i<=primes;i++)
    {
        if(x%prime[i]==0) return false;
    }
    return true;
}

使用本代码前,需要引用的库函数:

  • cmath

当然,最好自己写。(然而没有人会傻到抄这样难看的代码吧233333)

排列组合

简介

排列组合也是常考的东西。数论问题经常要解决,有时候暴力也需要的。。。

阶乘

理论讲解

阶乘就是。。。再简单不过了吧。。。用“!”来表示。。。

N!=123N

这东西很常用。。。但是通常来讲,都会对一个数取模。。。

代码

const int MAXN=100005;
const long long mod=1e9+7;
long long jc[MAXN];
void memjc(int range)
{
    jc[0]=1;
    for(int i=1;i<=range;i++)
    {
        jc[i]=jc[i-1]*i%mod;
    }
}

(这东西没编译就放上来了。。。反正没人会看这种代码的23333)

排列

排列( A )是指:从 n 个东西里面,取出 m 个进行排序的方式数。

那么,它与组合的区别是:

拿ABC和CBA两个集合来讲:
对于排列,这两种就是不同的方式。
对于组合,这两种就是相同的方式。

计算公式是:

Amnn(n1)(n2)(nm+1)n!(nm)!

组合

相对的,组合( C )就是:从 n 个东西里面,取出 m 个进行组合的方式数。

计算公式是:

Cmnn(n1)(n2)(nm+1)n!m!(nm)!

排列组合的其他公式

C0n+C1n+C2n++Ckn++Cnn=2n

C0nC1n+C2nC3n++(1)nCnn=0

C0n+C2n+C4n+=C1n+C3n+C5n+=2n1

Cmn=Cm1n1+Cm1n

配合取模的排列组合

如果在阶乘预处理的时候进行了取模运算。。。(当然这种情况超级常见),那么就必须要用到乘法逆元了。

以下内容转载自:欧几里德算法

先说什么是乘法逆元。

一般来讲,如果要运算加法、减法、乘法、乘方,都应该满足以下式子:

(a+b)%c=(a%c+b%c)%c

(ab)%c=(a%cb%c)%c

(ab)%c=(a%cb%c)%c

ab%p=(a%p)b%p

然而这里出现了一个问题:

如果是除法,并不满足 (a/b)%c=(a%c/b%c)%c ,不信你代个数试试:

(63%3=26%33%3%3=RuntimeError

那怎么实现对除法的取余呢?

这里就引入乘法逆元这个东西。

他可以达到这样的效果:

ab%k=(ac)%k

你可以将它简单地理解为类似于倒数的东西,只不过是再对倒数取余而已,即:

(bc)%k=1

所以注意,逆元是针对一个数而言的,并不是针对一个表达式。

更加详细的欧几里德算法论证等请参见原博客。

那么,对于已经取模的阶乘,就必须要用乘法逆元了。

Amnn(n1)(n2)(nm+1)n!inverse((nm)!)

Cmnn(n1)(n2)(nm+1)n!inverse(m!)inverse((nm)!)

超级常用!请务必牢记!

二项式定理

(a+b)n=C0nanb0+C1nan1b1+...+Crnanrbr+...+Cnna0bn

在这里, (a+b)n 展开式的第 r+1 项为:

Tr+1=Crnanrbr

i 项系数可表示为 Cin ,即 n i 的组合数目, 因此系数亦可表示为杨辉三角。

唯一分解定理

唯一分解定理是指所有正整数都可以被分解为质数的乘积。

例如: 268=22671

所以分解方法很简单,只需要对于每个数:

将这个数与这个数以内的质数相除。

对于每个质数,除到不能再除为止。除的次数就是对应分解的结果。

欧拉函数

欧拉函数是用于计算:

小于 n 的正整数中与 n 互质的数的数目( φ(1)=1

通式:

φ(x)=x(11p1)(11p2)(11p3)(11pn)
(其中 p1,p2pn x 的所有质因数, x 是不为 0 的整数)

这样的话,使用上面的唯一分解定理配合求解,真是美哉。

费马小定理

费马小定理(Fermat Theory)是数论中的一个重要定理,其内容为:

假如 p 是质数,且 gcd(a,p)=1 ,那么 ap11 mod p

即:

假如 a 是整数, p 是质数,且 a,p 互质(即两者只有一个公约数 1 ),那么 a (p1) 次方除以 p 的余数恒等于 1

猜你喜欢

转载自blog.csdn.net/rentenglong2012/article/details/69451911