0x01 位运算(1)

0x01 位运算(1)

本节主要内容:

第一部分:补码反码、移位运算、快速幂、快速乘法

第二部分:二进制状压、成对变换、lowbit()运算,Barrett模乘(补充)

一、 补码与反码

1. 补码

开头一个符号位,其余为数值位

32位补码表示unsigned intint
000000...00000000
0111111...11111121474836472147483647
100000...0000002147483678-2147483648
111111...1111114294967295-1
1. 反码

开头一个符号位,其余为数值位

32位反码表示32位整数
000000...0000000
111111...1111110
011111...1111112147483647
100000...000000-2147483647

二、 移位运算

  1. 算术右移:高位以符号位补充,低位越界舍弃

  2. 逻辑右移:高位以0补充,低位越界舍弃

三、 快速幂取模

此时尝试计算

$$
a^bmod\quad p
$$

这是一个典型的求幂运算,在一些与数学有关的问题中极为常见。

通常使用循环方法求幂,时间复杂度为O(b),然而众所周知,取模运算的速度极慢,这种暴力方法不足以满足我们的需求,我们需要尽可能减少运算次数。

接下来介绍利用位运算加速的求幂方法,先给出理论推导

假设指数b在二进制下表示为

$$
b_{(2)} = \overline{c_{k-1}c_{k-2} \dots c_0}
$$

写为多项式,即为

$$
b = c_{k-1} \cdot 2^{k-1} + c_{k-2} \cdot 2^{k-2} + \dots + c_0\cdot2^0
$$

从而

$$
\begin{aligned} a^b &= a^{c_{k-1}\cdot{2^{k-1}}} * a^{c_{k-2}\cdot{2^{k-1}}}*\dots*a^{c_0*2^0}\\ &=(a^{c_{k-1}\cdot{2^{k-2}}} * a^{c_{k-2}\cdot{2^{k-3}}}*\dots*a^{c_1*2^0})^2*a^{c_0\cdot2^0}\\ &=((a^{c_{k-1}\cdot{2^{k-3}}} * a^{c_{k-2}\cdot{2^{k-4}}}*\dots*a^{{c_2}*2^0})^2*a^{c_1*2^0})^2*a^{c_0\cdot2^0}\\ &=\dots\\ &=(((a^{c_{k-1}\cdot2^0})^2*a^{c_{k-2}\cdot2^0})^2*\dots)^2*a^{c_0\cdot2^0}\\ &=(((a^{c_{k-1}})^2*a^{c_{k-2}})^2*\dots)^2*a^{c_0} \end{aligned}
$$

于是,可以用下面的代码求出a^b:

int power(int a,int b){   //calculate a^b
    int ans = 1;
    while(b){
        if (b & 1) ans *= a;
        a *= a;
        b >>= 1;
    }
    return ans;
}

同时,考虑到

$$
a\cdot b \quad \%p == a\%p\quad\cdot\quad b\%p
$$

则在循环中每次计算取模

int power(int a,int b, int p){   //calculate a^b
    int ans = 1 % p;
    while(b){
        if (b&1) ans = (ll)ans * a  % p;//暂时转化为ll,以免越界,赋值时再转化回int
        a = (ll)a * a % p;
        b >>= 1;
    }
    return ans;
}

从而,我们计算出a^b mod p,时间复杂度为O(log2b)

四、 64位整数乘法

题目链接:64位整数乘法——ACWing

类似于快速幂取模,我们用相似的方法处理乘法。

此时尝试计算

$$
ab\quad mod\quad p
$$

假设b在二进制下表示为

$$
b = \overline{c_{k-1}c_{k-1}\dots c_0}
$$

写为多项式,即为

$$
b = c_{k-1} \cdot 2^{k-1} + c_{k-2} \cdot 2^{k-2} + \dots + c_0 \cdot 2^0
$$

从而

$$
\begin{aligned} ab &= ac_{k-1}\cdot 2^{k-1}+ac_{k-2}\cdot2^{k-2}+\dots+ac_0\cdot2^0\\ &= (ac_{k-1}\cdot 2^{k-2} + ac_{k-2}\cdot2^{k-3}+\dots+ac_1\cdot2^0)*2+ac_0\cdot2^0\\ &= ((ac_{k-1}\cdot 2^{k-3} + ac_{k-2}\cdot2^{k-4}+\dots+ac_2\cdot2^0)*2+ac_1\cdot2^0)*2+ac_0\cdot2^0\\ &=\dots\\ &=(((ac_{k-1}\cdot2^0)*2+ac_{k-2}\cdot2^0)*2+\dots)*2+ac_0\cdot2^0\\ &=((ac_{k-1}*2+ac_{k-2})*2+\dots)*2+ac_0\\ &=[((c_{k-1}*2+c_{k-2})*2+\dots)*2+c_0]\cdot a\\ \end{aligned}
$$

于是,可以用下面的代码求出ab

ll mul(ll a, ll b){
    ll ans = 0;
    while(b){
        if (b & 1) ans += a;
        a *= 2;
        b >>= 1;
    }
    return ans;
}

考虑取模:

ll mul(ll a, ll b){
    ll ans = 0;
    while(b){
        if (b & 1) ans = (ans + a) % p;
        a = a * 2 % p;
        b >>= 1;
    }
    return ans;
}

从而,我们计算出ab mod p,时间复杂度为O(log2b)

本文由“小苏打NaHCO3”原创,未经允许,严禁转载!

如果觉得本文对你有用,不妨点赞收藏一下吧!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值