zoukankan      html  css  js  c++  java
  • 『笔记』数学数论(一)

    写在前面

    由于作者也是边学习边记录,所以必然会存在某些神笔错误,感谢指正。

    本文顺序大概率会照着 Oi-Wiki 来,一些严谨定义还会参照百度百科,内容的话主要还是来自于听课笔记、学长遗产、网络资料等,经仔细筛选,作者认为自己有用的才会修改添加。

    由于 \(OI\) 涉及到的数学数论知识实在太多,本内容也会分为几篇文章共同呈上。

    过后这里会添加完整笔记的索引

    \[\Huge{(一)素数与最大公因数} \]

    素数

    前置

    如果存在一个整数 \(k\),使得 \(a = kd\),则称 \(d\) 整除 \(a\),记做 \(d \mid a\),称 \(a\)\(d\)倍数,如果 \(d > 0\),称 \(d\)\(a\)约数

    特别地,任何整数都整除 \(0\)

    定义

    素数,又称为质数,是指在大于 \(1\) 的自然数中,除了 \(1\) 和它本身以外不再有其他因数的自然数,即不存在其它因数。

    否则称之为合数

    性质

    • \(1\) 即不是素数,也不是合数。

    • 任何一个合数都可以分解为几个素数的积。 即任何一个合数都可以用素数的积的形式表示出来。

    • 素数有无穷多个。

      • 证明:反证法

        假设素数集为有限集,从小到大依次为 \(p_1,p_2,\dots,p_n\) ,设 \(N=p_1\times p_2 \times \dots ,\times p_n\) ,则 \(N+1\) 可能是素数也可能为合数。

        • \(N+1\) 为素数,则 \(N+1\) 要大于 \(p_1,p_2,\dots,p_n\) ,则其不在上述的素数集中。

        • \(N+1\) 为合数,因为任何一个合数都可以分解为几个素数积,则 \(N\)\(N+1\) 的最大公约数为 \(1\) ,那么就不可能被 \(p_1,p_2,\dots,p_n\) 整除,所以由该合数分解得到的素因数也不在素数集中。

        因此无论是合数还是素数,都意味着在假设的有限集外还存在其他素数。上述假设不成立。

        该证明源自欧几里得的《几何原本》。

    • 素数计数函数:小于或等于 \(x\) 的素数的个数,用 \(\pi(x)\) 表示。随着 \(x\) 的增大,有这样的近似结果:\(\pi(x) \sim \frac{x}{\ln(x)}\)

    判定与查找

    定义法

    直接按照定义从小到大判断其是否可以被除 \(1\) 和自身外的某个数整除。

    bool check_prime(int x)
    {
        if (x < 2)
            return false;
        for (int i = 2; i < x; ++i)
            if (x % i == 0)
                return false;
        return true;
    }
    

    小学数学老师给我们讲过:

    如果 \(x\)\(a\) 的约数,那么 \(\frac{a}{x}\) 必然也是 \(a\) 的约数。

    所以对于每一对 \((x, \frac{a}{x} )\),只需要检验其中的一个就好了。为了方便起见,我们之考察每一对里面小的那个数。不难发现,所有这些较小数就是 \([1, \sqrt{a}]\) 这个区间里的数。而 \(1\) 直接忽略即可。

    bool check_prime(int x)
    {
        if (x < 2)
            return false;
        for (int i = 2; i < sqrt(x); ++i)
            if (x % i == 0)
                return false;
        return true;
    }
    

    如果要寻找某一范围内的素数,只需多加一层循环然后判定统计答案即可。

    时间复杂度 \(O(n\sqrt{n})\)

    筛法求素数

    即筛去所有从2(素数是指大于1的自然数)开始的某一范围内的非素数,剩下的则都为素数。

    埃式筛

    埃拉托斯特尼筛法(Eratosthenes),简称埃氏筛爱氏筛,是一种由希腊数学家埃拉托斯特尼所提出的一种简单检定素数的算法。

    要得到自然数 \(n\) 以内的全部素数,必须把不大于根号 \(n\) 的所有素数的倍数剔除,剩下的就是素数。

    也就是说,对于任意整数 \(x\) 的倍数 \(2x,3x,\dots\) 都是不素数。根据素数的定义,该命题显然成立。

    不难想到,如果我们已经找到某个素数,那么它一定是后面应该被筛去=8/某个合数的质因子。如此往复下去,就可以筛掉范围内所有的合数。

    时间复杂度 \(O(n\log\log n)\)

    int find_prime(int n) //1 ~ n 内所有的素数
    {
        memset(prime, true, sizeof(prime));
        int p = 0;
        prime[0] = false;
        prime[1] = false;
        for (int i = 2; i <= n; i++)
        {
            if (is_prime[i])
            {
                prime[p++] = i;
                for (int j = 2 * i; j <= n; j += i)
                    prime[j] = false;
            }
        }
        return p;
    }
    

    欧拉筛

    线性筛,类似于埃式筛,区别就是不会像埃式筛那样重复筛去某一个数。

    欧拉筛通过“从小到大累积质因子”的方式标记每一个非素数,这样每个素数就可以以唯一形式表达。

    设数组 \(pf\) 记录每个数的最小质因子,

    1. 一次考虑 \(2 \sim n\) 之间的每一个数 \(i\)
    2. \(pf[i]=i\) ,则 \(i\) 为质数,将它记录下来
    3. 扫描不大于 \(pf[i]\) 的每个质数 \(p\) ,令 \(pf[i*p]=p\) 。即在 \(i\) 的基础上累积一个质因子 \(p\) 。由于 \(p \leq pf[i]\) ,所以 \(p\) 即为 \(i*p\) 的最小质因子。

    代码:

    int pf[_]; //Prime_Factor ,用来记录最小素因子
    int find_prime(int n)
    {
        int p = 0;
        for (int i = 2; i <= n; i++)
        {
            if (!pf[i]) //如果没有找到素因子,即为素数
            {
                prime[p++] = i;
                pf[i] = i;
            }
            for (int j = 0; j < p && prime[j] * i <= n; j++)
            {
                pf[prime[j] * i] = prime[j];
                if (!(i % prime[j])) //筛的数已经被前面的数筛过了
                    break;
            }
        }
        return p;
    }
    

    最大公因数

    定义

    约数的定义详见前置部分。

    也成为最大公约数、最大公因子(Greatest Common Divisor,即 gcd),是指两个及以上整数共同拥有的所有因数中最大的一个。

    欧几里得算法(Euclidean algorithm)

    已知两个数 \(a\)\(b\) ,如何求二者的最大公因数呢?

    如果两个数 \(a\)\(b\) 满足 \(\gcd(a, b) = 1\),我们称 \(a\)\(b\) 互质。

    假设 \(a > b\) ,

    • 显然,若 \(b\)\(a\) 的因数,则 \(b\) 即为二者的最大公因数。

    • 否则,

      • \(a=bk+c\) ,则有

        \[c=a \bmod b \]

        \(d \mid a\)\(d \mid b\) ,则

        \[\begin{align} c=a-bk,\\ \frac{c}{d}=\frac{a}{d}-\frac{b}{d}k \end{align} \]

        由(2)式可得 \(\frac{c}{d}\) 为整数,即 \(d \mid c\) 所有对于 \(a,b\) 的公约数,也会是 \(a \bmod b\) 的公约数。

      • 相反,

        \(d \mid b,~\mid (a \bmod b)\),可以得到

        \[\begin{align} \frac{a\bmod b}{d}=\frac{a}{d}-\frac{b}{d}k\\ ~\frac{a\bmod b}{d}+\frac{b}{d}k=\frac{a}{d} \end{align} \]

        显然(3)式子为整数,所以 \(\frac{a}{d}\) 也为整数,即 \(d \mid a\),所以 \(b,a\bmod b\) 的公约数也是 \(a,b\) 的公约数。

        既然两式公约数都是相同的,那么最大公约数也会相同。

        所以得到式子 \(\gcd(a,b)=\gcd(b,a\bmod b)\)

        既然得到了 \(\gcd(a, b) = \gcd(b, r)\),这里两个数的大小是不会增大的,那么我们也就得到了关于两个数的最大公约数的一个递归求法,即

        \[\gcd(a,b)=\gcd(b,a\bmod b) \]

        上述算法即为欧几里得算法,又被称作辗转相除法

    代码:

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

    函数递归至 \(b=0\) (即上层 \(a\%b=0\) )时返回。

    补充: 如果两个数 \(a\)\(b\) 满足 \(gcd(a,b)=1\) ,则 \(a\)\(b\) 互质。

    时间复杂度 \(O(\log n)\)

    如果实在想卡时间的话把题目构造成求斐波那契数列相邻两项的最大公约数就可以把时间卡到最坏复杂度。

    多个数的最大公约数

    计算出其中两个数的最大公约数,放回到原序列,重复进行知道求出最后两个数的最大公约数。

    素数与最小公因数篇 完结~

  • 相关阅读:
    我常用的find命令
    Linux压缩和解压汇总
    windows下bat批处理实现守护进程
    windows2003批量添加和导出所有ip
    bat批量去除文件首行和合并到文件
    windows下批量删除文件
    windows添加和删除服务
    【转】自动实时监控Windows2003服务器终端登录并发邮件和发短信通知
    设置windows网络连接别名和linux网络连接别名
    我常用的crontab命令
  • 原文地址:https://www.cnblogs.com/Frather/p/14604118.html
Copyright © 2011-2022 走看看