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

    \[\Huge{(二)最小公倍数、快速幂与同余} \]

    最小公倍数

    前置-算数基本定理

    每一个正整数都可以表示成若干整数的乘积,这种分解方式在忽略排列次序的条件下是唯一的,又称唯一分解定理

    也就是说,

    任何一个大于 \(1\) 的自然数 \(x\) ,如果 \(x\) 不为质数,那么 \(x\) 可以唯一分解成有限个质数的乘积 \(x = p_1^{k_1} \times p_2^{k_2} \times \cdots \times p_s^{k_s}\),这里 \(P_1 < P_2 < P_3 \cdots < P_n\) 均为质数,其中指数 \(a_i\) 是正整数。

    定义

    两个或多个整数公有的倍数叫做它们的公倍数,其中除 \(0\) 以外最小的一个公倍数就叫做这几个整数的最小公倍数(Least Common Multiple, LCM)

    思想

    由算术基本定理:

    \[a = p_1^{k_{a_1}} \times p_2^{k_{a_2}} \times \cdots \times p_s^{k_{a_s}},\\ b = p_1^{k_{b_1}} \times p_2^{k_{b_2}} \times \cdots \times p_s^{k_{b_s}} \]

    \(a\)\(b\) 的最大公约数为

    \[p_1^{\min(k_{a_1}, k_{b_1})} \times p_2^{\min(k_{a_2}, k_{b_2})} \times \cdots \times p_s^{\min(k_{a_s}, k_{b_s})} \]

    最小公倍数即为

    \[p_1^{\max(k_{a_1}, k_{b_1})} \times p_2^{\max(k_{a_1}, k_{b_1})} \times \cdots \times p_s^{\max(k_{a_s}, k_{b_s})} \]

    显然有

    \[\max(k_a, k_b)+\min(k_a, k_b)=k_a+ k_b \]

    那么就有

    \[\gcd(a,b) \times \operatorname{lcm}(a, b) = a \times b \]

    移项可得

    \[\operatorname{lcm}(a, b) = \frac{a \times b}{\gcd(a,b)} \]

    综上,要求两个数的最小公倍数,只需要求这两个数的最大公因数即可。

    如何求两个数的最大公因数?请参照『笔记』数学数论(一)最大公因数部分。

    快速幂

    定义

    快速幂(Exponentiation by squaring,平方求幂),快速计算底数 \(a\)\(n\) 次幂。

    时间复杂度维持在 \(O(\log n)\) ,相较于朴素算法 \(O(n)\) 的确快了不少。

    思想

    先来举个栗子:

    计算 \(7\)\(10\) 次方,如何使时间尽可能少?

    方法1

    朴素算法

    计算 \(7 \times 7 = 49\) ,然后计算 \(49 \times 7 = 343\) ,再然后计算 \(343 \times 7 = 2401\)\(\cdots\) ,得到最后结果 \(^{10} = 282475249\)

    每次只让CPU计算一个数乘上一个个位数,CPU的优势得不到利用,这样做效率实在太低。

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

    方法2

    多次幂折中计算

    显然,\(7^{10} = 7^5 \times 7^5\) ,那么我们只需要先计算 \(7 \times 7 \times 7 \times 7 \times 7 = 7^5 = 16807\) ,然后在计算 \(7^5 \times 7^5 = 16807^2 = 282475249\)

    那么我们可以想到,既然可以折中将次数折中计算,那么我们是否可以将每次折中计算完成以后的结果的次数再次这种呢?

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

    方法3

    快速幂法

    由方法2启发可得,要计算 \(7^{10}\) ,可以先计算 \(7^2 = 49\) ,然后有

    \[\begin{aligned} 7^10 &= 7^{5^2}\\ &= 7^{2^2} \times 7\\ &= 7^2 \times 7^2 \times 7\\ &= 49 \times 49 \times 7\\ &= 282475249 \end{aligned} \]

    总共进行了 \(4\) 次乘法运算,时间复杂度 \(O(\log n)\)

    递归快速幂

    刚才方法3实际上就是快速幂,利用二分的思想。按照上述例子,可以轻松地得到一个递归方程

    \[a^{n}=\left\{\begin{array}{ll} a^{n-1} \cdot a, & n \% 2 = 0 \\ a^{\frac{n}{2}} \cdot a^{\frac{n}{2}}, &n \ne 0 \text{ \&\& } n \% 2 \ne 0 \\ 1, & n=0 \end{array}\right. \]

    方程不难理解,代码也不难写:

    //递归快速幂
    int Qpow(int a, int n)
    {
        if (n == 0)
            return 1;
        else if (n % 2 == 1)
            return Qpow(a, n - 1) * a;
        else
        {
            int tmp = Qpow(a, n / 2);//tmp 记录 a^{\frac{n}{2}} 结果,避免程序时间复杂度退化为 O(n)
            return tmp * tmp;
        }
    }
    

    非递归快速幂

    对于 \(7^{10}\) ,我们可以把它写成二进制的形式,即 \(7^{{(1010)}_2}\)

    那么我们就可以对 \((1010)_2\) 进行分解,原式就可以写成若干个 \(7^{{(100\cdots)}_2}\) 相乘的形式。

    还原为十进制理解一下就会发现,上式恰好就变成了 \(7^1 \times 7^2 \times 7^4 \times \cdots\) 。因此,我们只需要不断把底数平方即可。

    模拟一下运算过程:

    \[\begin{array}{lll} a & n _{\text {(binary) }} & \text { ans } \\ 7^{(1)_{2}} & 101\color{red}{0} & 1 \\ 7^{(10)_{2}} & 10\color{red}{1} & 7^{(10)_{2}} \\ 7^{(100)_{2}} & 1\color{red}{0} & 7^{(10)_{2}} \\ 7^{(1000)_{2}} & \color{red}{1} & 7^{(10)_{2}} \cdot 7^{(1000)_{2}} \end{array} \]

    代码:

    //非递归快速幂
    int Qpow(int a, int n)
    {
        int ans = 1;
        while (n)
        {
            if (n & 1)    //如果 n 的当前末位为 1
                ans *= a; //ans 乘上当前的 a
            a *= a;       //a 平方
            n >>= 1;      //n 往右移一位
        }
        return ans;
    }
    

    Tips:

    & 是按位与,&1 可以理解为取出二进制数的最后一位,相当于 %2==1

    瞎扯

    个人认为有关快速幂的题目中难点应该在矩阵快速幂,毕竟快速幂只是作为一个工具出现,正式比赛中又会有哪位良心出题人直接上模板呢?

    矩阵快速幂的代码可能相对稍微难理解一点,其实背过就好啦

    如果有题目数据范围非常大并且要求对结果取模,代码实现中应该是对递归的每一层或者是循环的每一步都取模。

    同余

    \[????? \]

    定义

    同余定理(Congruence theorem),是数学数论中的一个重要概念。

    给定一个正整数 \(m\) ,如果 两个整数 \(a\)\(b\) 满足 \(a-b\) 能够被 \(m\) 整除,即 $(a-b) \div m $ 得到一个整数,那么就称整数 \(a\)\(b\) 对模 \(m\) 同余,记作 \(a \equiv{b} (\bmod{\ m})\)

    简单来说,就是 如果 \(a\)\(b\) 除以 \(m\) 的余数相同,那么就说 \(a\)\(b\) 关于模 \(m\) 同余,记作 \(a \equiv b ( \bmod \ m)\)

    或者说 如果两个数 \(a\)\(b\) 的差能够被 \(m\) 整除,那么就说 \(a\)\(b\) 对模数 \(m\) 同余(关于 \(m\) 同余)。

    根据定义得出:

    • \(a \equiv b ( \bmod \ m)\),则m|a;

    • \(a \equiv b ( \bmod \ m)\) 等价于 \(a\)\(b\) 分别用 \(m\) 去除,余数相同。

    性质

    • 反身性:\(a \equiv a ( \bmod \ m)\)

    • 对称性:若 \(a \equiv b ( \bmod \ m)\) ,则有 \(b \equiv a ( \bmod \ m)\)

    • 传递性:若 \(a \equiv b ( \bmod \ m)\)\(b \equiv a ( \bmod \ m)\) ,那么 \(a \equiv c ( \bmod \ m)\)

    • 同余式基本运算:

      • 加减法:若 \(a \equiv b ( \bmod \ m), \quad b \equiv c ( \bmod \ m)\) ,则 \(a \pm c \equiv b \pm c ( \bmod \ m)\)

      • 乘法:若 \(a \equiv b ( \bmod \ m), \quad b \equiv c ( \bmod \ m)\) ,则 \(ac \equiv bc ( \bmod \ m)\)

      • 线性运算:若 \(a \equiv b (\bmod \ m), \quad c \equiv d (\bmod \ m), \quad\) 那么 \(a \pm c \equiv b \pm d (\bmod \ m ), \quad\)\(ac \equiv bd(\bmod \ m)\)

      • 除法:若ac \(\equiv bc (\bmod \ m) , c \neq 0\)\(a \equiv b (\bmod \ m / gcd ( c , m ))\)
        显然有,当 \(gcd ( c , m )=1\)\(a \equiv b (\bmod \ m )\)

      • 幕运算:如果 \(a \equiv b(\bmod \ m)\), 那么 \(a^n \equiv b^n(\bmod \ m)\)

    • \(a \equiv b (\bmod \ m ), n \mid m\), 则 \(a \equiv b (\bmod \ n )\)

    • \(a \equiv b (\bmod \ m_i )\) 其中 \(( i=1,2 \cdots n )\)\(a \equiv b (\bmod \ lcm(m_1, m_2, \cdots m_n))\)

    最小公倍数、快速幂与同余 完结!!

  • 相关阅读:
    Netty快速入门(09)channel组件介绍
    Netty快速入门(08)ByteBuf组件介绍
    Netty快速入门(07)Netty入门例子
    Netty快速入门(06)Netty介绍
    Netty快速入门(05)Java NIO 介绍-Selector
    Netty快速入门(04)Java NIO 介绍-Channel
    Netty快速入门(03)Java NIO 介绍-Buffer
    Netty快速入门(02)Java I/O(BIO)介绍
    Netty快速入门(01)Linux I/O模型介绍
    老虎集团【4面】
  • 原文地址:https://www.cnblogs.com/Frather/p/14645898.html
Copyright © 2011-2022 走看看