iis服务器助手广告广告
返回顶部
首页 > 资讯 > 后端开发 > Python >Java使用位运算实现加减乘除详解
  • 117
分享到

Java使用位运算实现加减乘除详解

Java位运算实现加减乘除Java位运算 加减乘除Java位运算 2023-05-19 08:05:54 117人浏览 薄情痞子

Python 官方文档:入门教程 => 点击学习

摘要

目录1 . 实现加法2 . 实现减法3 . 实现乘法4 . 实现除法在线OJ: LeetCode 29. 两数相除 原题目的要求是不能使用乘法, 除法和取余运算符实现除法. 在本篇博

在线OJ: LeetCode 29. 两数相除

原题目的要求是不能使用乘法, 除法和取余运算符实现除法.

在本篇博客中把题目要求提高一点, 这里只使用位运算来实现, 顺便的也就把只使用位运算实现加减乘除实现了.

1 . 实现加法

首先我们需要知道两数之和可以是两个数位相加和不进位相加之和, 而两数进行异或(^)运算其实就是两个数对应二进制值的无进位相加.

我们可以把思路可以转换一下, 把加法用异或替换, 如果我们能够得到两个数相加的二进制进位信息为0, 那么此时的无进位结果就是两数相加的最终结果了.

只有二进制无进位信息, 相加的结果; 然后把这个结果加上进位信息, 就是两个数相加的最终结果.

抽象一下:

我们要计算 a + b

先算 a ^ b = a' 得到的结果就是无进位相加的结果, 然后我们再得到 a 和 b 相加的进位信息 b’, 那么此时 a + b = a' + b' 就是两数之和, 但我们这里是不能用加号, 所以我们需要逐个把进位信息再继续叠加 (即让a’ 和 b’ 再继续运算, 得到进位信息和不进位信息…), 直到进位信息为 0 结束.

那么进位信息如何计算?

只有当 a 和 b 的二进制对应位置上都是1, 才会产生进位, 只需要 通过 (a & b) << 1 就可得到进位结果.

所以, 只使用位运算实现加法的代码如下:

// 不使用算数运算实现两数相加
public static int add (int a, int b) {
    // 两个数的和等于两个数不进位相加和进位相加的和
    // a ^ b 得到的就是两数不进位相加的和
    // (a & b) << 1 得到的就是两数只相加进位的值
    // 一直循环至进位值为0计算结束
    int sum = a;
    while (b != 0) {
        sum = a ^ b;
        b = (a & b) << 1;
        a = sum;
    }
    return sum;
}

2 . 实现减法

两数相减, 等价于一个数加上另一个数的相反数, 即 a - b = a + (-b), 但这里是不能不能出现减号的, 所以, 可以用加法来模拟一个数的相反数, 因为 x 的相反数等于 x 取反再加 1 (~x + 1), 即 add(~x, 1).

所以, 只使用位运算实现减法可以在加法代码的基础上进行:

// 计算一个数字的相反数
public static int oppNum (int num) {
    return add(~num, 1);
}
// 不使用算数运算实现两数相减
public static int minus(int a, int b) {
    // a - b 就相当于 a + (-b)
    // 一个数的相反数等于这个数取反再加1
    return add(a, oppNum(b));
}

3 . 实现乘法

看下面计算两个十进制数的乘法用的方法是不是很熟悉, 以 a = 12, b = 22 为例, a * b 通过如下方式计算;

  19
x 22
------
  38
 38
------
 418

这样的方法也适用于二进制, 19 的二进制是 10011, 22 的二进制是 10110, 计算过程如下;

     10011
x    10110
-------------
     00000
    10011
   10011
  00000
 10011
------------
 110100010

得到的计算结果 110100010 就是 418.

其实这里的二进制计算就对应着这样一个过程, 要求 a * b 的结果, 就从右往左开始遍历 b 的每一位二进制值, 如果 b 的第 i 位是 1, 则把 a 左移 i 位的累值加到结果中; 如果 b 的第 i 位是 0, 不需要处理, 最后累加完的结果就是 a * b 的答案.

只使用位运算实现乘法的代码如下:

// 不使用算数运算实现两数相乘
public static int mulit (int a, int b) {
    // 就小学手算十进制类似
    // 让 a 的每一位去乘 b 的每一位
    int res = 0;
    while (b != 0) {
        if ((b & 1) != 0) {
            res = add(res, a);
        }
        a <<= 1;
        // 要注意 b 必须是无符号右移
        b >>>= 1;
    }
    return res;
}

4 . 实现除法

在实现除法的时候, 为了防止溢出, 我们可以先把两数转换成正数来进行计算; 最后在计算末尾再判断两个数的符号决定是否把由正数计算出来的结果取其相反数.

假设 a / b = c, 则 a = b * c, 使用位运算就需要结合二进制来思考, 这里假设:

a = b * (2^7) + b * (2^4) + b * (2^1), 则 c 的二进制一定是10010010.

抽象一下, 如果a = b * (2 ^ m1) + b * (2 ^ m2) + b * (2 ^ m3), 则 c 的 m1 位置, m2 位置, m3 位置一定是 1, 其他位置都是 0.

所以, 我们实现除法的思路可以转换成 a 是由几个 ( b * 2 的某次方) 的结果组成.

所以, 只使用位运算实现除法的核心代码如下:

// 判断是不是负数
public static boolean isNegavit(int num) {
    return num < 0;
}
// 这个适用于a和b都不是系统最小值的情况下
public static int div(int a, int b) {
    // 这里的除法一定要保证两个数都是正数, 如果有负数在计算逻辑最后加上即可
    int x = isNegavit(a) ? oppNum(a) : a;
    int y = isNegavit(b) ? oppNum(b) : b;
    int res = 0;
    // 计算的是 x / y
    // 每次循环 y 向左移动到和 x 最接近的值, 但要满足 y <= x, 如果是这个条件下, 也就是让 y 左移, 可能会溢出到符号位, 就可能会出错
    // 实际上将 x 向右移动到和 y 最接近的值, 移动的位数和上面也是一样的, 不过要满足 x >= y;
    for (int i = 30; i >= 0; i = minus(i, 1)) {
        if ((x >> i) >= y) {
            // 这个比特位一定是1
            res |= (1 << i);
            // x 减去 y << i;
            x = minus(x, y << i);
        }
    }
    // isNegavit(a) != isNegavit(b) 这个也可以用 isNegavit(a) ^ isNegavit(b) 来实现效果
    return isNegavit(a) != isNegavit(b) ? oppNum(res) : res;
}

其中

if ((x >> i) >= y) {
    // 这个比特位一定是1
    res |= (1 << i);
    // x 减去 y << i;
    x = minus(x, y << i);
}

就是让 a 不断尝试其值是否由 (b * 2的某个次方) 相加得到.

但只有上面的实现还不够, 这里是有一些特殊情况需要考虑的, 比如在 Java 中, int 类型的系统最小值Integer.MIN_VALUE取相反数的结果依然是Integer.MIN_VALUE.

所以, 除法的两个操作数如果有系统最小值的话需要单独的进行计算处理.

1.如果两操作数都是系统最小值, 结果就是 1;

2.如果只有除数 (b) 为系统最小值, 结果就是 0;

3.如果被除数 (a) 为系统最小值, 除数为 -1 时, 根据 LeetCode 题目要求, 结果就是Integer.MIN_VALUE / (-1) == Integer.MAX_VALUE;

4.如果被除数 (a) 为系统最小值, 除数为 -1 和 Integer.MIN_VALUE时 (即a = Integer.MIN_VALUE且b != -1 && b != Integer.MIN_VALUE), a / b应该通过如下方式来计算;

  • 可以先让先让系统最小值 +1 (a + 1), 此时就可以按照正常上面的正常流程去计算除法了, 即(a + 1) / b = c.
  • 接着计算a - (b * c) = d, 得到由于 a + 1 少/多算的.
  • 然后d / b = e
  • 最后将 c 和 e 相加就得到了 a / b 的值, c + e = ((a + 1)/b) + ((a - (b * c)) / b) = a / b

除了这些特殊, 就是正常的流程了, 所以, 加上针对系统最小值的特殊判断的代码如下:

// 除法的计算如果有系统最小值需要进行特殊判断(因为Integer.MAX_VALUE取反再+1得到的还是原来值), 也就是没办法计算相反数
public static int divide(int a, int b) {
    if (a == Integer.MIN_VALUE && b == Integer.MIN_VALUE) {
        // 如果两数都是系统最小值
        return 1;
    } else if (b == Integer.MIN_VALUE){
        // 如果除数为系统最小值
        return 0;
    } else if (a == Integer.MIN_VALUE) {
        // 被除数为系统最小值
        // 且除数为-1
        if (b == oppNum(1)) {
            // 答案应该是 Integer.MAX_VALUE + 1 的这个值的, 但力扣系统返回 Integer.MAX_VALUE 就行了
            return Integer.MAX_VALUE;
        } else {
            // 系统最小值没法取相反数计算
            // 1. c = (Integer.MAX_VALUE + 1) / b , 先让系统最小值 +1 后再除以 b
            // 2. (Integer.MAX_VALUE - c * b) / b
            // 3. 再将 1 和 2 的结果相加节课
            int c = div(add(a, 1), b);
            return add(c, div(minus(a, mulit(c, b)), b));
        }
    } else {
        // 两数都不是系统最小值
        return div(a, b);
    }
}

完整实现的代码如下:

class Solution {
    // 不使用算数运算实现两数相加
    public static int add (int a, int b) {
        // 两个数的和等于两个数不进位相加和进位相加的和
        // a ^ b 得到的就是两数不进位相加的和
        // (a & b) << 1 得到的就是两数只相加进位的值
        // 一直循环至进位值为0计算结束
        int sum = a;
        while (b != 0) {
            sum = a ^ b;
            b = (a & b) << 1;
            a = sum;
        }
        return sum;
    }
    // 计算一个数字的相反数
    public static int oppNum (int num) {
        return add(~num, 1);
    }
    // 不使用算数运算实现两数相减
    public static int minus(int a, int b) {
        // a - b 就相当于 a + (-b)
        // 一个数的相反数等于这个数取反再加1
        return add(a, oppNum(b));
    }
    // 不使用算数运算实现两数相乘
    public static int mulit (int a, int b) {
        // 就和小学手算十进制类似
        // 让 a 的每一位去乘 b 的每一位
        int res = 0;
        while (b != 0) {
            if ((b & 1) != 0) {
                res = add(res, a);
            }
            a <<= 1;
            // 要注意 b 必须是无符号右移
            b >>>= 1;
        }
        return res;
    }
    // 不使用算数运算实现除法
    // 判断是不是负数
    public static boolean isNegavit(int num) {
        return num < 0;
    }
    // 这个适用于a和b都不是系统最小值的情况下
    public static int div(int a, int b) {
        // 这里的除法一定要保证两个数都是正数, 如果有负数在计算逻辑最后加上即可
        int x = isNegavit(a) ? oppNum(a) : a;
        int y = isNegavit(b) ? oppNum(b) : b;
        int res = 0;
        // 计算的是 x / y
        // 每次循环 y 向左移动到和 x 最接近的值, 但要满足 y <= x, 如果是这个条件下, 也就是让 y 左移, 可能会溢出到符号位, 就可能会出错
        // 实际上将 x 向右移动到和 y 最接近的值, 移动的位数和上面也是一样的, 不过要满足 x >= y;
        for (int i = 30; i >= 0; i = minus(i, 1)) {
            if ((x >> i) >= y) {
                // 这个比特位一定是1
                res |= (1 << i);
                // x 减去 y << i;
                x = minus(x, y << i);
            }
        }
        // isNegavit(a) != isNegavit(b) 这个也可以用 isNegavit(a) ^ isNegavit(b) 来实现效果
        return isNegavit(a) != isNegavit(b) ? oppNum(res) : res;
    }
    // 除法的计算如果有系统最小值需要进行特殊判断(因为Integer.MAX_VALUE取反再+1得到的还是原来值), 也就是没办法计算相反数
    public static int divide(int a, int b) {
        if (a == Integer.MIN_VALUE && b == Integer.MIN_VALUE) {
            // 如果两数都是系统最小值
            return 1;
        } else if (b == Integer.MIN_VALUE){
            // 如果除数为系统最小值
            return 0;
        } else if (a == Integer.MIN_VALUE) {
            // 被除数为系统最小值
            // 且除数为-1
            if (b == oppNum(1)) {
                // 答案应该是 Integer.MAX_VALUE + 1 的这个值的, 但力扣系统返回 Integer.MAX_VALUE 就行了
                return Integer.MAX_VALUE;
            } else {
                // 系统最小值没法取相反数计算
                // 1. c = (Integer.MAX_VALUE + 1) / b , 先让系统最小值 +1 后再除以 b
                // 2. (Integer.MAX_VALUE - c * b) / b
                // 3. 再将 1 和 2 的结果相加节课
                int c = div(add(a, 1), b);
                return add(c, div(minus(a, mulit(c, b)), b));
            }
        } else {
            // 两数都不是系统最小值
            return div(a, b);
        }
    }
}

当然, 按照原本的题意, 只是不能使用乘法, 除法和取余运算, 其他可以正常使用, 代码就简单了不少, 思路是一样的, 代码实现如下:

class Solution29 {
    public static boolean isNegavit(int num) {
        return num < 0;
    }
    public static int oppNum (int num) {
        return (~num) + 1;
    }
    public static int mulit (int a, int b) {
        // 就和小学手算十进制类似
        // 让 a 的每一位去乘 b 的每一位
        int res = 0;
        while (b != 0) {
            if ((b & 1) != 0) {
                res += a;
            }
            a <<= 1;
            // 要注意 b 必须是无符号右移
            b >>>= 1;
        }
        return res;
    }
    public static int div(int a, int b) {
        // 这里的除法一定要保证两个数都是正数, 如果有负数在计算逻辑最后加上即可
        int x = isNegavit(a) ? oppNum(a) : a;
        int y = isNegavit(b) ? oppNum(b) : b;
        int res = 0;
        // 计算的是 x / y
        // 每次循环 y 向左移动到和 x 最接近的值, 但要满足 y <= x, 如果是这个条件下, 也就是让 y 左移, 可能会溢出到符号位, 就可能会出错
        // 实际上将 x 向右移动到和 y 最接近的值, 移动的位数和上面也是一样的, 不过要满足 x >= y;
        for (int i = 30; i >= 0; i--) {
            if ((x >> i) >= y) {
                // 这个比特位一定是1
                res |= (1 << i);
                // x 减去 y << i;
                x -= (y << i);
            }
        }
        // isNegavit(a) != isNegavit(b) 这个也可以用 isNegavit(a) ^ isNegavit(b) 来实现效果
        return isNegavit(a) != isNegavit(b) ? oppNum(res) : res;
    }
    // 除法的计算如果有系统最小值需要进行特殊判断(因为Integer.MAX_VALUE取反再+1得到的还是原来值), 也就是没办法计算相反数
    public static int divide(int a, int b) {
        if (a == Integer.MIN_VALUE && b == Integer.MIN_VALUE) {
            // 如果两数都是系统最小值
            return 1;
        } else if (b == Integer.MIN_VALUE) {
            // 如果除数为系统最小值
            return 0;
        } else if (a == Integer.MIN_VALUE) {
            // 被除数为系统最小值
            // 且除数为-1
            if (b == -1) {
                // 答案应该是 Integer.MAX_VALUE + 1 的这个值的, 但力扣系统返回 Integer.MAX_VALUE 就行了
                return Integer.MAX_VALUE;
            } else {
                // 系统最小值没法取相反数计算
                // 1. c = (Integer.MAX_VALUE + 1) / b , 先让系统最小值 +1 后再除以 b
                // 2. (Integer.MAX_VALUE - c * b) / b
                // 3. 再将 1 和 2 的结果相加节课
                int c = div(a + 1, b);
                return c + ((a - mulit(c, b)) / b);
            }
        } else {
            // 两数都不是系统最小值
            return div(a, b);
        }
    }
}

上述代码都是可以通过OJ的

以上就是Java使用位运算实现加减乘除详解的详细内容,更多关于Java位运算的资料请关注编程网其它相关文章!

--结束END--

本文标题: Java使用位运算实现加减乘除详解

本文链接: https://www.lsjlt.com/news/212645.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Java使用位运算实现加减乘除详解
    目录1 . 实现加法2 . 实现减法3 . 实现乘法4 . 实现除法在线OJ: LeetCode 29. 两数相除 原题目的要求是不能使用乘法, 除法和取余运算符实现除法. 在本篇博...
    99+
    2023-05-19
    Java位运算实现加减乘除 Java位运算 加减乘除 Java位运算
  • Java利用位运算实现加减乘除的方法详解
    目录前言一、常见位运算1. &运算2. |运算3. ^运算4. ~运算二、位运算实现加法三、位运算实现减法四、位运算实现乘法五、位运算实现除法前言 我们经常使用的加减乘除,我...
    99+
    2024-04-02
  • PHP中怎么使用位运算实现加减乘除运算
    这篇文章主要介绍了PHP中怎么使用位运算实现加减乘除运算,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。计算机最基本的操作单元是字节,一个字节由8个位组成,一个位只能存储一个0...
    99+
    2023-06-20
  • Java利用位运算实现加减运算详解
    目录前言思路分析示例位运算进位初步结果去除加号整体思路加法代码实现减法实现减法分析减法代码实现总结前言 本文主要介绍如何使用位运算来实现加减功能,也就是在整个运算过程中不能出现加减符...
    99+
    2022-12-31
    Java位运算实现加减运算 Java 加减运算 Java位运算
  • javascript如何实现加减乘除运算
    本篇内容主要讲解“javascript如何实现加减乘除运算”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“javascript如何实现加减乘除运算”吧! ...
    99+
    2024-04-02
  • php怎么实现加减乘除的运算
    php实现加减乘除运算的方法:1、创建一个php示例文件;2、定义两个变量为“$x”和“$y”;3、通过“$x+$y”,“$x-$y”,“$x*$y”及“$x/$y”公式实现加减乘除运算;4、使用“echo”输出运算结果即可。本教程操作系统...
    99+
    2023-05-14
    php
  • MongoDB中怎么实现加减乘除运算
    本篇文章给大家分享的是有关MongoDB中怎么实现加减乘除运算,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。1.$add操作符(+)1.1 语...
    99+
    2024-04-02
  • Java利用位运算实现乘法运算详解
    目录前言正文十进制相乘二进制相乘思路分析代码实现总结前言 在上一篇中,我们介绍了使用位运算实现加法和减法运算,接下来本文主要介绍如何用位运算实现乘法运算,在实现乘法时要用位运算实现,...
    99+
    2023-05-15
    Java位运算实现乘法运算 Java位运算 乘法运算 Java位运算
  • Java怎么用位运算实现加减运算
    这篇文章主要讲解了“Java怎么用位运算实现加减运算”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Java怎么用位运算实现加减运算”吧!思路分析先分析如何用位运算实现加法运算。示例假设a=2...
    99+
    2023-07-04
  • C++重载运算符实现分数加减乘除
    本文实例为大家分享了C++重载运算符实现分数加减乘除的具体代码,供大家参考,具体内容如下 实现结果如下图所示: 代码如下所示: #include <iostream>...
    99+
    2024-04-02
  • C++实现加减乘除计算器
    本文实例为大家分享了C++实现加减乘除计算器的具体代码,供大家参考,具体内容如下 #include <iostream> #include <conio.h>...
    99+
    2024-04-02
  • TypeScript类型实现加减乘除详解
    目录引言分析DivideSmallerThanTupleSubtract最后加法乘法坑点总结引言 在网上看到这道题目:请用TS类型实现整除? type A = Divide<...
    99+
    2023-05-16
    TypeScript类型加减乘除 TypeScript 加减乘除
  • java实现简单的加减乘除计算器
    本文实例为大家分享了java实现加减乘除计算器的具体代码,供大家参考,具体内容如下 代码 import java.awt.*; import java.awt.event.*;...
    99+
    2024-04-02
  • BigDecimal的加减乘除计算方法详解
    目录BigDecimal的运算——加减乘除 首先是bigdecimal的初始化加法 add()函数     减法subtract()...
    99+
    2024-04-02
  • Java怎么用位运算实现乘法运算
    这篇文章主要介绍了Java怎么用位运算实现乘法运算的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Java怎么用位运算实现乘法运算文章都会有所收获,下面我们一起来看看吧。十进制相乘例如,26 * 15,在进行乘法...
    99+
    2023-07-06
  • java如何使用位运算代替乘除法
    这篇文章主要介绍了java如何使用位运算代替乘除法,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。位运算代替乘除法在所有的运算中,位运算是最为高效的。因此,可以尝试使用位运算代...
    99+
    2023-06-27
  • 详解C/C++高精度(加减乘除)算法中的压位优化
    目录前言一、基本原理1、存储方式2、计算方式二、完整代码三、性能对比总结附录 1、性能测试代码前言 由于上一章《C/C++ 高精度(加减乘除)算法简单实现》实现了基本的高精度计算,数...
    99+
    2023-01-31
    C++压位优化原理 C++压位优化 C++ 优化
  • PHP如何在不使用加减乘除运算符号的情况下实现加法
    这篇文章主要讲解了“PHP如何在不使用加减乘除运算符号的情况下实现加法”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“PHP如何在不使用加减乘除运算符号的情况下实现加法”吧!写一个函数,求两个...
    99+
    2023-06-20
  • C语言如何使用移位实现乘除法运算
    这篇文章主要为大家展示了“C语言如何使用移位实现乘除法运算”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“C语言如何使用移位实现乘除法运算”这篇文章吧。移位实现乘...
    99+
    2024-04-02
  • C/C++高精度(加减乘除)算法的实现
    目录前言一、必要的参数二、辅助函数三、实现加减乘除1、加法2、减法3、乘法4、除法四、使用例子1、加法例子2、减法例子3、乘法例子4、除法例子前言 C/C++基本类型做算术运算时长度...
    99+
    2022-12-15
    C++实现高精度算法 C++高精度算法 C语言 高精度算法
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作