利用位运算实现加减乘除

位运算基本概念

runoob 位运算

位运算运算规则说明
& 与运算0 & x = 01. 清零:数n与0按位与
2. 取指定位:这些位为1,其余为全为0
3. 判断奇偶:a & 1 === 0
I 或运算1 I x = 1设置指定位为1:这些位为1,其余位全为0
^ 异或运算相同为0,不同为1
x ^ x = 0
x ^ 0 = 0
1. 无进位加法
2. 翻转指定位:这些位为1,其他位全为0
3. a ^ b可判断a,b是否同号
~ 取反运算补码的计算:取反+1
<< 左移运算低位补0,高位溢出相当于 *2
>> 右移运算低位溢出,无符号数高位补0,有符号数在逻辑右移中高位补0、在算数右移中高位补符号位相当于 /2

运算

加法

位运算实现加减乘除运算

  • 算法描述:
    1. 求无进位加法结果 sum【按位异或^】
    2. 求进位 carry【按位与& 并左移一位<<】
    3. 如果carry为0,sum为最终结果;否则重复步骤1~3,求和sum + carry
function add (a, b) {
    let sum = a ^ b
    let carry = (a & b) << 1
    while (carry) {
        a = sum
        b = carry
        sum = a ^ b
        carry = (a & b) << 1
    }
    return sum
}

减法

  • 算法描述:
    • a - b = a + (-b)
    • 负数在计算机中以补码的形式保存,对b按位取反后+1
function substract (a, b) {
    b = add(~b, 1) // -b
    return add(a, b)
}

乘法

  • 算法描述:
    • a * b,a 加b次的自己即可
    • 在二进制中,由于只有0和1,这种运算可以转化为:如果b[i]=0,跳过本次加法操作(加 0 );如果为1,加数为a左移一位
      在这里插入图片描述
function mutiply (a, b) {
    // 计算最终符号,并取绝对值
    let x = a < 0 ? add(~a, 1) : a
    let y = b < 0 ? add(~b, 1) : b

    let product = 0
    while (y) {
    
    	// product = add(product, x)
        // y = substract(y, 1)
        // 等效于
        
        if (y & 0x1) { // 最后一位为1
            product += x
        }
        x = x << 1

        // 在10进制中,下一个循环前,b需要-1
        // 在2进制中,表示该位已经处理完成,右移将最后一位更新为下一位
        y = y >> 1
    }

    // 还原符号位
    if ((a ^ b) < 0) {
        product = add(~product, 1)
    }

    return product

}

除法

  • 算法描述:
    • 思路和乘法一致,a减b,直到a < b
function divide(a, b) {
    let x = a > 0 ? a : add(~a, 1)
    let y = b > 0 ? b : add(~b, 1)

    let quotient = 0 // 商
    let remainder = 0 // 余数

    while (x >= y) {
        quotient = add(quotient, 1)
        x = substract(x, y)
    }

    // 确定商符号
    if ((a ^ b) < 0) {
        quotient = add(~quotient, 1)
    }

    // 计算余数, 余数符号只和除数符号相关
    remainder = b < 0 ? y : add(~y, 1)

    return quotient
}
  • 算法优化:
    • 上述算法,如果被除数很大,除数很小,每次减一个除数,会导致循环很多次。
    • 可以加大减法步长,即每次减一个小于被除数的最大倍数除数,在二进制中,可以通过左移运算扩大倍数。
function divide_v2(a, b) {
    let x = a > 0 ? a : add(~a, 1)
    let y = b > 0 ? b : add(~b, 1)

    let quotient = 0 // 商
    let remainder = 0 // 余数

    for(let i = 31; i >= 0; i--) {
        /* 这里要比较 x 与 y * (2^i)的关系
         * 但是y * (2^i)可能会溢出,所以比较 x / (2^i) 与 y的关系
         * 效果是一致的
         */
        if ((x >> i) >= y) {
            quotient = add(quotient, 1 << i) // 步长为 1 << i
            x = substract(x, y << i)
        }
    }

    // 确定商符号
    if ((a ^ b) < 0) {
        quotient = add(~quotient, 1)
    }
    // 计算余数
    remainder = b < 0 ? add(~y, 1) : y

    return quotient
}

ECMAScript 位运算符

  • 在使用js语言时,要注意:
    • js中使用64位保存数据,但是进行位运算时,会强制转化为32位。
    • 在上述代码中,如果a或b取值为 1 >> 31,求其补码后会得到 a = add(~a, 1)的异常结果,所以要对这种情况进行特殊讨论:
      • 如果b = 1 >> 31
        • a = 1 >> 31,则结果为1
        • 其余情况均为0
      if (b === MIN_NUM) {
          if (a === b) {
              return {
                  isSpecialCase: true,
                  result: 1
              }
          } else {
              return {
                  isSpecialCase: true,
                  result: 0
              }
          }
      }
      
      • 如果a = 1 >> 31且b !== 1 >> 31,先进行一次减法/加法运算,减小a的绝对值
      if (a === MIN_NUM) {
          a = b < 0 ? substract(a, b) : add(a, b)
          quotient = add(quotient, 1)
      }
      
function specialCases (a, b, MIN_NUM, MAX_NUM) {
    switch(a) {
        case 0:
            return {
                isSpecialCase: true,
                result: 0
            }
        case 1:
            if (b === 1 || b === -1) {
                return {
                    isSpecialCase: true,
                    result: b
                }
            } else {
                return {
                    isSpecialCase: true,
                    result: 0
                }
            }
        case MIN_NUM:
            if (b === 1) {
                return {
                    isSpecialCase: true,
                    result: a
                }
            } else if (b === -1) {
                return {
                    isSpecialCase: true,
                    result: MAX_NUM
                }
            }
            break
    }
    if (b === MIN_NUM) {
        if (a === b) {
            return {
                isSpecialCase: true,
                result: 1
            }
        } else {
            return {
                isSpecialCase: true,
                result: 0
            }
        }    
    } else if (b === 1) {
        return {
            isSpecialCase: true,
            result: a
        } 
    }
    return {
        isSpecialCase: false,
        result: undefined
    }
}
function divide(a, b) {
    const MIN_NUM = 1 << 31
    const MAX_NUM = add(~add(MIN_NUM, 1), 1)
    const {isSpecialCase, result} = specialCases(a, b, MIN_NUM, MAX_NUM)
    if (isSpecialCase) {
        return result
    }

    let quotient = 0 // 商

    if (a === MIN_NUM) {
        a = b < 0 ? substract(a, b) : add(a, b)
        quotient = add(quotient, 1)
    }

    let x = a > 0 ? a : add(~a, 1)
    let y = b > 0 ? b : add(~b, 1)

    for(let i = 31; i >= 0; i--) {
        if ((x >> i) >= y) {
            quotient = add(quotient, 1 << i) // 步长为 1 << i
            x = substract(x, y << i)
        }
    }

    // 确定商符号
    if ((a ^ b) < 0) {
        quotient = add(~quotient, 1)
    }

    return quotient
}

总结

加减乘除位运算思路小结

  • 加法:异或运算计算无进位的和(新加数1),按位与并左移一位计算进位(新加数2),直到进位为0。
  • 减法:对减数求补码(取反+1),转化为加法。
  • 乘法:
    • 统一两个乘数为正数
    • 乘数最后一位为1,结果中加上左移后的被乘数
    • 最后计算积的符号
  • 除法:注意js
    • 统一两个参数为正数
    • 每次减操作,利用移位运算(i = 31)找到小于被除数的除数最大倍数(if((x >> i) >= y))
    • 最后计算商的符号,余数符号只和除数符号有关

对比字符串是否相同

使用位运算求解单词长度的最大乘积

  • 如果字符串中只含有数字、或只含有小写字母、或只含有大写字母,可以把该字符串转换为掩码后,使用位运算进行对比。
  • 例如只含有数字,就是10位掩码;只含有小写字母或只含有大写字母,就是26位掩码。
  • 判断两个字符串是否相同,即判断两个掩码按位与后是否为0,即 if (!(mask1 & mask2))。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
可以利用运算符重载来实现矩阵的乘除操作。以下是一个简单的实现示例: ```c++ #include <iostream> #include <vector> using namespace std; // 矩阵类定义 class Matrix { public: Matrix(int r, int c) : rows(r), cols(c), data(r, vector<double>(c)) {} // 矩阵运算符重载 Matrix operator+(const Matrix& other) const { if (this->rows != other.rows || this->cols != other.cols) { throw runtime_error("Matrix dimensions do not match."); } Matrix result(rows, cols); for (int i = 0; i < rows; ++i) { for (int j = 0; j < cols; ++j) { result.data[i][j] = this->data[i][j] + other.data[i][j]; } } return result; } // 矩阵运算符重载 Matrix operator-(const Matrix& other) const { if (this->rows != other.rows || this->cols != other.cols) { throw runtime_error("Matrix dimensions do not match."); } Matrix result(rows, cols); for (int i = 0; i < rows; ++i) { for (int j = 0; j < cols; ++j) { result.data[i][j] = this->data[i][j] - other.data[i][j]; } } return result; } // 矩阵乘法运算符重载 Matrix operator*(const Matrix& other) const { if (this->cols != other.rows) { throw runtime_error("Matrix dimensions do not match."); } Matrix result(this->rows, other.cols); for (int i = 0; i < this->rows; ++i) { for (int j = 0; j < other.cols; ++j) { double sum = 0; for (int k = 0; k < this->cols; ++k) { sum += this->data[i][k] * other.data[k][j]; } result.data[i][j] = sum; } } return result; } // 矩阵除法运算符重载 Matrix operator/(const Matrix& other) const { if (this->cols != other.rows) { throw runtime_error("Matrix dimensions do not match."); } Matrix inverse = other.inverse(); // 先求逆矩阵 return (*this) * inverse; // 矩阵乘法实现除法 } // 求逆矩阵 Matrix inverse() const { if (this->rows != this->cols) { throw runtime_error("Matrix is not square."); } int n = this->rows; Matrix result(n, n); // 先做初等变换,将原矩阵变成单位矩阵 for (int i = 0; i < n; ++i) { result.data[i][i] = 1; } for (int i = 0; i < n; ++i) { double factor = this->data[i][i]; for (int j = 0; j < n; ++j) { this->data[i][j] /= factor; result.data[i][j] /= factor; } for (int k = 0; k < n; ++k) { if (k == i) { continue; } double factor = this->data[k][i]; for (int j = 0; j < n; ++j) { this->data[k][j] -= factor * this->data[i][j]; result.data[k][j] -= factor * result.data[i][j]; } } } return result; } private: int rows, cols; // 矩阵行数和列数 vector<vector<double>> data; // 矩阵数据 }; int main() { Matrix A(2, 3); A.data = {{1, 2, 3}, {4, 5, 6}}; Matrix B(3, 2); B.data = {{7, 8}, {9, 10}, {11, 12}}; Matrix C = A + A; C = C - A; Matrix D = A * B; Matrix E = D / B; return 0; } ``` 在上面的示例代码中,我们定义了一个 `Matrix` 类来表示矩阵,并实现了矩阵乘除四种运算符重载。其中矩阵除法是通过先求逆矩阵再做矩阵乘法实现的。在 `Matrix` 类中还实现了求逆矩阵的函数 `inverse()`,用于求矩阵的逆矩阵。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值