zoukankan      html  css  js  c++  java
  • 快速幂和分数取模

    快速幂

    快速幂就是求(a^{b} mod M),如果时间复杂度较低且不需要取模,那就可以直接调用pow(a, b)。

    快速幂的时间复杂度是$ O(log b) $。由此可见,这种算法可能运用到了二分或者参数右移啥之类的。这里运用的就是参数b
    右移。

    对于指数b来说,b可以转变为2的n次方之和。
    例如(29 = 2^{4} + 2^{3} + 2^{2} + 2^{0}),对于(a^{29})来说就是

    [a^{29} = a^{ 2^{4} + 2^{3} + 2^{2} + 2^{0} } = a^{2^{4}} * a^{2^{3}} * a^{2^{2}} * a^{2^{0}} ]

    我再将其转变一下就会成为:

    [a^{1 * 2^{4}} * a^{1 * 2^{3}} * a^{1 * 2^{2}} * a^{0 * 2^{1}} * a^{1 * 2^{0}} ]

    容易发现2的n次方前的系数,其实就是29的二进制,即为11101,那么求(a^{b} % MOD)就简化成为上面这个的公式了,只需
    要表示出这个公式即可,把不断重复发地乘a转变为乘a的2的n次方,时间复杂度自然就降低了。

    我们假设 (a^{b}=res) ,那么如果按照传统的一个a一个a的乘的话,那么res的初始值应该为1。此处我们照样是一个一个乘
    ,但是a的值会不断改变,由于我们此此处的b用的是二进制表示,那么从b的低位开始运算,每次循环的a都是前一次循环a的平
    方倍。公式表示就为:(a^{2^{i}} = a^{2^{i - 1}} * a^{2^{i - 1}})
    即下一循环的(a = a * a),只要b在此位为1,那么res就乘以a,否则就进入下一次循环。在这个过程中,将可能超出M的
    运算取模就行了。

    C++实现代码:

    ll ksm(ll a, ll b){
        ll res = 1;
        while(b) {
            if(b & 1) //判断b的二进制在此位是否为1
                res = res * a % M;
            a = a * a % M; //下一位的a的值
            b >>= 1;
        }
        return res;
    }
    

    分数取模

    分数取模就是计算 a / b % M, 运用小费马引理

    [b^{M−1}modM = 1 mod M ]

    转换可得 $$ b^{M−2}modM = b^{-1} mod M $$
    $$ a * b^{M−2}modM = a * b^{-1} mod M $$
    那么$$ a / b mod M = a * b^{M- 2} mod M$$
    所以这里只需结合快速幂函数,直接计算即可.

    C++实现代码

    ll res = a * ksm(b, M - 2) % M;
    
  • 相关阅读:
    Swift2.0 中的String(二):基本操作
    Swift2.0 中的String(一):常用属性
    在Swift中的ASCII到字符转换的问题
    iOS NSData
    UVALive
    Flipping Game(枚举)
    POJ 1182 :食物链(并查集)
    Java数据结构系列之——栈(2):栈的链式存储结构及其操作
    testing and SQA_动态白盒測试
    POJ 2392 Space Elevator
  • 原文地址:https://www.cnblogs.com/primoka/p/14337725.html
Copyright © 2011-2022 走看看