package com.demo.algorithm.bitmap位图;
/**
* 用位运算实现 加减乘除
*/
public class Code02_BitAddSubtractMultDiv {
/**
* 加法
* ^ 异或运算(都为1时 结果为0,且没有进位) 是 无符号相加 例如 0011^1010 = 0010
* (a & b) << 1 得到 a + b 的 进位信息
*
* a = a^b , b = (a & b) << 1 一直循环 到 进位信息没有 即 为 0
* @param a
* @param b
* @return
*/
public static int add(int a, int b){
int sum = a;
while (b != 0){
sum = a ^ b;
b = (a & b) << 1;
a = sum;
}
return sum;
}
/**
* 减法
* a + b = a +(-b)
* -b = ~b +1 ;
* 正数的反码与其原码相同;负数的反码是对正数逐位取反,符号位保持为1
* 假如 b = 10 = 0000 1010;
* 对 b 取反 得到 ~b = 0000 0101;
* 取反后加一 得到 ~b +1 = 0000 0110;
* @param a
* @param b
* @return
*/
public static int subtract(int a, int b){
return add(a,negNum(b));
}
/**
* 求相反数 取反加一
* @param n
* @return
*/
public static int negNum(int n){
return add(~n,1);
}
/**
* 乘法
* 1. 从左到右,依次 看 b 的每一位上的数 是否为1(看b的 哪些位置是1, 即 把 b 右移i 位,和 1 相与,若结果 为1 则说明 b的i位置 是1 ) ,
* 2. 如果i位置 是 1 则 把 a 左移i位,空位补0, 得到的结果 相加
* 3. 重复 上面步骤,循环 b 的 长度次。
* 例如 a = 10101, b = 101, 则 res = a*(2^0 + 2^2) = a*2^0 + a*2^2
* 而 a*2^n 等价于 把 a 左移n 位 ,
* 所以 a*b 的结果 就是 把 a左移0位 + 把 a 左移 2 位
* @param a
* @param b
* @return
*/
public static int multi(int a, int b){
int res = 0;
while (b != 0 ){ //这里 b!=0 而不是 b >0 ,因为b可能为负数
if((b & 1) != 0){
res += a;
}
a <<= 1;
b >>>= 1;//不带符号右移, 如果带符号右移的话,当 b 为 负数时,则会 循环移动 31 次, 但是实际结果 应该时 b 不带符号 循环右移 b的 实际长度次
}
return res;
}
/**
* 除法 有四种情况(由于整型最小数是没有绝对值的(最小数的 绝对值 = 最大数 +1 ,无法用整型数表示),所以不能直接用 div 方法)
* 1. 除数是整型最小值,被除数也是整型最小值 结果为1
* 2. 除数是最小值,被除数不是最小值
* 3. 除数不是最小值,被除数是最小值 结果为 0
* 4. 除数 和 被除数 都不是最小值
* @param dividend
* @param divisor
* @return
*/
public int divide(int dividend, int divisor) {
if (dividend == Integer.MIN_VALUE && divisor == Integer.MIN_VALUE){
return 1;
}else if (dividend == Integer.MIN_VALUE ){
if (divisor == -1){
return Integer.MAX_VALUE;
}else {
//由于最小数不能直接取绝对值,会越界,故 a/b 之间 a +1 再取绝对值
//step1. (a+1)/b = c;
//step2. a - c*b = d;
//step3. d /b = e;
//step4. a/b = c+e = (a+1)/b + (a-c*b)/b;
int c = div((dividend + 1),divisor);
int d = dividend - multi(c, divisor);
return c + div(d,divisor);
}
}else if (divisor == Integer.MIN_VALUE){
return 0;
}else {
return div(dividend, divisor);
}
}
/**
* 除法:
* 除数和被除数取绝对值后 再进行运算 , 得到的结果 再加上 符号(同符号为正,相反为负)
* 1. 被除数从 30 开始递减 移动 i位
* 2. 当 移动到i位时, 除数 最接近 被除数,且不小于被除数 时, 得知 商的组成部分中,在 i 位置肯定有一个1;
* 3. 用原来的数据减去 上一步移动后的 数,得到的结果 再重复 1,2 ,3 步
* @param x
* @param y
* @return
*/
public int div(int x, int y){
int res = 0;
int a = x < 0 ? -x : x;
int b = y < 0 ? -y : y;
//因为 第31 位 是符号位,故只需从 第30 位移起
for (int i = 30; i >= 0; i--) {
while ((a >> i) >= b){ //当 移动到i位时, 除数 最接近 被除数,且不小于被除数 时
res |= (1<<i); //商 在 i 位置肯定有1
a = a - (b << i); //用原来的数据减去 上一步移动后的 数,得到的结果 再重复
}
}
//异或:无符号相加(没有进位信息) 同0异1,即 运算两边相同则为0即false,两边不同则为1即true;
return ((x < 0) ^ (y < 0)) ? -res : res;
}
}
给定两个数,用位运算实现加减乘除(不适用加减乘除号)
最新推荐文章于 2024-07-22 17:06:26 发布