Java利用位运算实现比较两个数的大小

目录
  • 题目要求
  • 主要思路
    • 方法1(不考虑溢出)
    • 方法2(考虑溢出情况)

题目要求

如何不要用任何比较判断符(>,==,<),返回两个数( 32 位整数)中较大的数。

主要思路

方法1(不考虑溢出)

要比较 a 和 b 的大小,因为不能用比较符号,我们可以通过 a - b 的符号位来判断,如果 a - b 的符号位是 1,说明 a - b < 0,则 a 小,否则 a 大或者 a 和 b 相等。

如何判断一个数的符号位是 0 还是 1 ?

由于是 32 位整数,所以如果将一个数右移 31 位,然后和 1 相与(&),如果得到 1,则这个数是负数,如果得到 0,则这个数是正数。

举个具体例子,如果要求 a 和 b 谁大,我们可以先通过

((a - b) >> 31) & 1 得到一个值,如果这个值是 1 ,说明 a 小,否则 a 大或者 a 和 b 相等。

由于不能出现比较符号,所以无法使用如下代码

return ((a - b) >> 31) & 1 == 1?b:a;

也无法使用如下代码

if (((a - b) >> 31) & 1 == 1){
    return b;
}
return a;

但是我们可以巧妙利用((a - b) >> 31) & 1结果去构造一个公式,这个公式可以在((a - b) >> 31) & 1 == 1情况下得到 b, 在((a - b) >> 31) & 1 == 0情况下得到 a。

我们可以利用一个反转函数

public int flip(int n) {
    return n ^ 1;
}

这个函数的作用就是,当n == 1时,返回 0,当n == 0时,返回 1,我们将判断符号的结果flip一次,如下代码

    public static int sign(int n) {
        return flip((n >> 31) & 1);
    }

这个方法的作用就是

当符号位是 1 的时候,返回 0,符号位是 0 的时候,返回 1。

这样flip后,

sign(a - b)如果得到 1, 则:a - b > 0,则返回 a。

sign(a - b)如果得到 0, 则:a - b <= 0,则返回 b。

公式可以定义成

sign(a - b) * a + flip(sign(a - b)) * b

主函数直接做如下调用

public static int getMax1(int a, int b) {
    int c = a - b;
    //当符号位是 1 的时候,scA = 0,符号位是 0 的时候,scA = 1。
    int scA = sign(c);
    // scA = 1 时,scB = 0,scA = 0时,scB = 1
    int scB = flip(scA);
    // 如果 scA = 0,说明 b 大,直接返回b
    // 如果 scA = 1,说明 a 大,直接返回a
    return a * scA + b * scB;
}

这个方法没有考虑溢出的情况,比如

a = 2147483647;
b = -2147480000;

a - b直接就溢出了,后面的算法就都不适用了。

方法2(考虑溢出情况)

那我们可以先比较 a 与 b 两个数的符号,

会有如下几种情况:

情况1:符号不同,则直接返回符号为正的那个数。

情况2:如果符号相同,则这种情况下,a - b的值绝对不会溢出,那么就看 c 的符号(c为正返回a,c为负返回b)

方法2的核心代码如下

int c = a - b;
int sa = sign(a);
int sb = sign(b);
int sc = sign(c);
int difSab = sa ^ sb;
int sameSab = flip(difSab);
int returnA = difSab * sa + sameSab * sc;
int returnB = flip(returnA);
return a * returnA + b * returnB;

其中: int difSab = sa ^ sb就是判断 a 和 b 的符号是否一样,如果 difSab == 1,则 a 和 b 符号一样,如果difSab == 0,则a 和 b符号不一样。

只有当difSab == 0的时候,要考虑 c 的符号。因为difSab == 0,所以int returnA = 0 * sa + 1 * sc;,即int returnA = sc,如果 sc 为 1,说明 c 的符号是 0,则a - b > 0,返回 a 即可,否则返回 b。

方法1和方法2的完整代码和测试代码如下:

// 不要用任何比较判断,返回两个数中较大的数
public class Code_GetMax {

    public static int flip(int n) {
        return n ^ 1;
    }

    public static int sign(int n) {
        return flip((n >> 31) & 1);
    }

    public static int getMax1(int a, int b) {
        int c = a - b;
        int scA = sign(c);
        int scB = flip(scA);
        return a * scA + b * scB;
    }

    public static int getMax2(int a, int b) {
        int c = a - b;
        int sa = sign(a);
        int sb = sign(b);
        int sc = sign(c);
        int difSab = sa ^ sb;
        int sameSab = flip(difSab);
        int returnA = difSab * sa + sameSab * sc;
        int returnB = flip(returnA);
        return a * returnA + b * returnB;
    }

    public static void main(String[] args) {
        int a = -16;
        int b = -19;
        System.out.println(getMax1(a, b));
        System.out.println(getMax2(a, b));
        a = 2147483647;
        b = -2147480000;
        System.out.println(getMax1(a, b)); // wrong answer because of overflow
        System.out.println(getMax2(a, b));

    }
}

到此这篇关于Java利用位运算实现比较两个数的大小的文章就介绍到这了,更多相关Java位运算内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Java 通过位运算求一个集合的所有子集方法

    Java没有自带的求一个集合的所有子集的方法,我们可以通过集合的子集规律来求. 一个集合的所有子集等于2^该集合的长度.比如{c,b,a}的长度为3,这个集合的子集就有8个. 这句话看起来很简单,但同时也隐含着高深的哲理.其实一个集合的所有集合,和2^该集合的长度这个数字有关.比如上面的例子,{c,b,a}的长度为3,则可以用0-7表示其所有子集.如下所示,改数字所对应的位置为1,则说明我需要这个数字形成子集.从0-7的二进制表示,刚好代表完,一个长度为3,子集个数为8的所有子集. 0(000)

  • 一定要让你搞懂Java位运算符

    目录 常用的运算符 位运算符 &.|.^ 运算符 ~ 运算符 << 和 >> 运算符 >>> 运算符 有<<<运算符吗 总结 常用的运算符 在Java语言中,官方给我们提供了极为丰富的运算操作,但是常用的,好像也就是算术运算符,例如加.减.乘.除.取余等.还有就是关系运算符大于.小于.等于.不等于等.再或者就是逻辑运算符&&.||.!等,至于位运算符,属实用的不多.用的不多今天为什么拿出来讲呢,首先这可以帮助你很好的去理解

  • Java利用三目运算符比较三个数字的大小

    易于理解版 package com.zhebie.ternary; public class ternary { public static void main(String[] args) { int a = 5, b = 8 , c = 9; a=a>b?a:b; //a与b相比较,将较大值赋值给a a=a>c?a:c; //已经获得较大值得a再与c相比较,将较大值再次赋值给a System.out.println(a); //输出a的值为9 } } 嵌套三目运算版 public clas

  • Java中的位运算符全解

    目录 1.&(按位与运算符) 2.|(按位或运算符) 3.^(异或运算符) 4.<<(左移运算符) 5.>>(右移移运算符) 6.~(取反运算符) 7.>>>(无符号右移运算符) (1)正数无符号右移 (2)负数无符号右移 总结 1. &(按位与运算符) &按位与的运算规则是将两边的数转换为二进制位,然后运算最终值,运算规则即(两个为真才为真):1&1=1 , 1&0=0 , 0&1=0 , 0&0=0 pu

  • Java利用位运算实现比较两个数的大小

    目录 题目要求 主要思路 方法1(不考虑溢出) 方法2(考虑溢出情况) 题目要求 如何不要用任何比较判断符(>,==,<),返回两个数( 32 位整数)中较大的数. 主要思路 方法1(不考虑溢出) 要比较 a 和 b 的大小,因为不能用比较符号,我们可以通过 a - b 的符号位来判断,如果 a - b 的符号位是 1,说明 a - b < 0,则 a 小,否则 a 大或者 a 和 b 相等. 如何判断一个数的符号位是 0 还是 1 ? 由于是 32 位整数,所以如果将一个数右移 31

  • Java利用位运算实现加减乘除的方法详解

    目录 前言 一.常见位运算 1. &运算 2. |运算 3. ^运算 4. ~运算 二.位运算实现加法 三.位运算实现减法 四.位运算实现乘法 五.位运算实现除法 前言 我们经常使用的加减乘除,我们所看到的只是表面的效果,那么加减乘除在底层究竟是怎么实现的?今天就让我们一探究竟.今天用位运算实现的加减乘除不使用任何的加减乘除符号. 一.常见位运算 1. &运算 &运算二进制每一位全1为1,否则为0 public static void main(String[] args) { i

  • Java利用位运算实现加减运算详解

    目录 前言 思路分析 示例 位运算进位 初步结果 去除加号 整体思路 加法代码实现 减法实现 减法分析 减法代码实现 总结 前言 本文主要介绍如何使用位运算来实现加减功能,也就是在整个运算过程中不能出现加减符号. 加减乘除运算在计算机中,实际上都是用位运算实现的,今天就用位运算来模拟下加法和减法的运算功能. 思路分析 先分析如何用位运算实现加法运算. 示例 假设a=23,b=36,使用位运算实现加法得到结果59. 首先来看下23.36.59的二进制信息. 从上面的图中可以看到,两个数相加的结果与

  • Java利用位运算实现乘法运算详解

    目录 前言 正文 十进制相乘 二进制相乘 思路分析 代码实现 总结 前言 在上一篇中,我们介绍了使用位运算实现加法和减法运算,接下来本文主要介绍如何用位运算实现乘法运算,在实现乘法时要用位运算实现,并且不能出现加减乘除任何符号. 之前介绍过一篇如何用位运算实现加法和减法: 如何用位运算实现加减运算? 正文 在用位运算实现之前,我们先来回忆一下小学时,学乘法时用的十字相乘法. 十进制相乘 例如,26 * 15,在进行乘法操作时,我们一般这样算,先用5乘以6得到30,把0写下把3记在一边,再用5乘以

  • PHP巧妙利用位运算实现网站权限管理的方法

    首先我们先定义4个常量来设定四种权限: ===================================== define(ADD,1);//增加数据库记录的权限 define(UPD,2);//修改数据库记录的权限 define(SEL,4);//查找数据库记录的权限 define(DEL,8);//删除数据库记录的权限 ===================================== 接下来假设有3个用户: A用户拥有ADD-UPD-SEL-DEL四个权限,用位或运算计算A的

  • Golang利用位运算实现为程序加速

    目录 前言 用位运算优化 其他奇淫巧技 总结 前言 最近在持续优化之前编写的 JSON 解析库 xjson,主要是两个方面的优化. 第一个是支持将一个 JSONObject 对象输出为 JSON 字符串. 这点在上个版本中只是利用自带的 Print 函数打印数据: func TestJson4(t *testing.T) { str := `{"people":{"name":{"first":"bob"}}}` first

  • C++使用异或运算实现交换两个数的值

    异或交换两个数的值是资源开销最小的方法,不需要中介数,原理简单的来说就是异或的负负得正 代码: #include <stdio.h> int main() { int a = 11, b = 22; printf("a=%d b=%d\n", a, b); a = a ^ b; b = a ^ b; a = a ^ b; printf("a=%d b=%d\n", a, b); } 编译: gcc test.c -o test 执行: a=11 b=22

  • Java利用移位运算将int型分解成四个byte型的方法

    package 移位运算; public class 移位运算 { public static void main(String[] args) { //00000111 01011011 11001101 00010101 int n=123456789; //n为需要进行移位处理的32位int型初始值 byte[] a =chai(n); for (int i = 0; i < a.length; i++) { byte b=a[i]; System.out.print(b+" &qu

  • Java中位运算(移位、位与、或、异或、非) 的简单实例

    复制代码 代码如下: public class Test {     public static void main(String[] args) {         // 1.左移( << )         // 0000 0000 0000 0000 0000 0000 0000 0101 然后左移2位后,低位补0://         // 0000 0000 0000 0000 0000 0000 0001 0100 换算成10进制为20         System.out.pri

随机推荐