位运算的骚操作

位运算的骚操作(一)之四则运算

? 可以这样说,位运算是我们刚开始学计算机就会接触到的一种东西。那么位运算这么常见,我们是否可以使用它来做一些骚操作呢?

使用的运算符包括下面(java还有一个>>>无符号右移):

含义 运算符 例子
左移(后面补0) << 0011 => 0110
右移(正数前面补0,负数补1) >> 0110 => 0011
按位或 0011 ------- => 1011 1011
按位与 & 0011 ------- => 1011 1011
按位取反 ~ 0011 => 1100
按位异或 (相同为0不同为1) ^ 0011 ------- => 1000 1011

左移右移运算
右移相当于是除,左移相当于就是乘,左移一位乘以2,左移二位乘以4,依此类推.无论正数、负数,它们的右移、左移、无符号右移32位都是其本身,

位运算与四则运算(这里都是考虑整数【Java版本】)

加法

加法有两个操作:求和进位

求和:1+1=0 1+0=1 0+0=0

异或:1^1=0 1^0=1 0^0=0

进位:1+1=1 1+0=0 0+0=0

位与:1&1=1 1&0=0 0&0=0

那么这时候,我们使用位运算实现加法肯定是使用异或位与

  • 递归版本

    // 使用递归实现
    int add(int a,int b){
        // 假如进位为0
        if(b ==0){
            return a;
        }
        // 获得求和位
        int s = a^b;
        // 获得进位,然后需要向做移动一位表示进位
        int c = ((a&b)<<1);
        return add(s,c);
    }
  • 非递归版本

    非递归版本和递归版本没什么不一样的

     int add2(int a,int b){
    
        int s;
        int c;
        while(b != 0){
            // 获得求和位
            s = (a^b);
            // 获得进位,然后需要向做移动一位表示进位
            c = ((a&b)<<1);
            a = s;
            b = c;
        }
        return a;
    }

减法

减法,emm,简单点来说,就是使用加法来做的,只是说加了一个负数而已

首先我们得先将被减数取反(a取反就是~a+1

int adverse(int a){
    return add(~a,1);
}

减法的完全代码

// 取得相反数
int adverse(int a){
    return add(~a,1);
}
// 减法函数 其中add就是前面的加法函数
int subtract(int a,int b){
    return add(a,adverse(b));
}

乘法

说乘法的前面我们先考虑下符号问题。

首先呢,我们得知道一个数是正数还是负数

// 负数返回-1,正数返回0
int getsign(int i){
    return (i >> 31);
}

如果为负数,则进行取反

// 如果为负数,则进行取反
int toPositive(int a) {
    if (a >> 31 == -1)
        // 进行取反
        return add(~a, 1);
    else
        return a;
}
  • 解决方法一:

    乘法,我们小时候学习乘法,老师告诉我们,乘法就是累加,例如6*7就是7个6相加而已,so,我们肯定可以使用加法来实现乘法。时间复杂度O(n)。

int multiply(int a, int b){
    Boolean flag = true;
    // 如果相乘为正数,flag为false
    if (getsign(a) == getsign(b))
        flag = false;
    // 将a取正数
    a = toPositive(a);
    b = toPositive(b);
    int re = 0;

    while (b!=0) {
        // 相加
        re = add(re, a);
        // b进行次数减一
        b = subtract(b, 1);
    }
    // 假如结果是负数,则进行取反
    if (flag)
        re = adverse(re);
    return re;
}
  • 解决方法二

    首先先看一张图吧,这张图是我用笔记本的触摸板画的,难看了一点(en,我自己都受不了的难看),上面画的是6*2的二进制相乘。

    1

    上面我们可以发现,相乘有点类似与&,只不过它会进位而已。那么我们的问题就可以简单化了。相与,然后进位即可,然后再相加。换个方向想:a向左移动,b向右移动,如果b的最后一位为1,则可以将a加上。(时间复杂度O(logn))

    int multiply2(int a, int b) {
        Boolean flag = true;
        // 如果相乘为正数,flag为false
        if (getsign(a) == getsign(b))
            flag = false;
        // 将a取正数
        a = toPositive(a);
        b = toPositive(b);
        int re = 0;
        while (b!=0) {
            // 假如b的最后一位为1
            if((b&1) == 1){
                // 相加
                re = add(re, a);
            }
            b = (b>>1);
            a = (a<<1);
        }
        // 假如结果是负数
        if (flag)
            re = adverse(re);
        return re;
    }

除法

除法和乘法类似。

  • 解决方法一

    除法解决方法一和乘法的解决方法一类似,从被除数上面减去除数,直到不够减了,才罢休。时间复杂度O(n)。

  • 解决方法二
    解决方法二的思路是这样的:从b*(2**i)开始减(2**i代表2的i次方),然后一直减到b*(2**0)。这样的解决方法的时间复杂度是O(logn)。

    // a/b
    int divide(int a,int b){
       Boolean flag = true;
       // 如果相除为正数,flag为false
       if (getsign(a) == getsign(b))
           flag = false;
       // 将a取正数
       a = toPositive(a);
       b = toPositive(b);
       int re = 0;
       int i = 31;
       while(i>=0){
           // 如果够减
           // 不用(b<<i)<a是为了防止溢出
           if((a>>i)>=b){
               // re代表结果
               re = add(re, 1<<i);
               a = subtract(a, (b<<i));
           }
           // i减一
           i = subtract(i, 1);
       }
       // 假如结果是负数
       if (flag)
           re = adverse(re);
       return re;
    }

ok,今天的位运算四则运算就到这里了,在这里我都没有考虑到数据溢出的情况,因为重点不在这里,下次的博客我将会说一下位运算在算法中的一些骚操作。

原文地址:https://www.cnblogs.com/xiaohuiduan/p/10981210.html

时间: 2024-10-28 11:48:48

位运算的骚操作的相关文章

C#位运算实际作用之操作整型某一位

1.前言 前几天写了两篇关于c#位运算的文章 c#位运算基本概念与计算过程 C#位运算实际运用 在文中也提到了位运算的实际作用之一就是合并整型,当时引用了一个问题: C# 用两个short,一个int32拼成一个long型,高16位用short,中间32位用int,最低16位用另外一个short. 答案如下: 高16位shortA.中间32位intA.低16位shortB longResult=((long)shortA << 48 )+ ((long)intA << 16)+ s

位运算的另一种姿势

在蒟蒻Cydiater日常水题的过程中,忽然遇到了一道题.中间有一个过程是要求在很快的时间内求出$1500$大小的两个01串的与之后存在多少个1. 最坏的,扫一遍,整体复杂度$O(N)$,好像没有什么可以优化的空间了QAQ.我开始考虑用位运算的与操作优化,因为其有$1500$个元素,所以可以考虑把这个东西拆成$\frac{N}{32}$个01串. 但是这之后好像就又存在一个问题.如何快速的统计一个二进制的01串里有多少个1?如果不要求$O(1)$,可以不停的统计lowbit,那么这个复杂度就和有

位运算及其应用实例(1)

摘要 位运算是C/C++中的基本运算之一,即便是这样,它对大多数程序员来说是一个比较陌生的运算——大多数程序员很少使用位运算.本篇先简要介绍基本的位运算操作符及其用法(何时使用),然后介绍位运算符的几个典型应用: (1)      三种不用临时变量交换两个整数的实例,并分析每个实例的优缺点 (2)      进制转换,通过位运算实现将十进制数按二进制和十六进制输出,并得出一个通用的,用于将十进制按照2的n次方进制输出的程序. (3)      给出利用位运算实现的计算整数的二进制表示中有多少个1

计算机中的位运算

位运算是C/C++中的基本运算之一,即便是这样,它对大多数程序员来说是一个比较陌生的运算——大多数程序员很少使用位运算.本篇先简要介绍基本的位运算操作符及其用法(何时使用),然后介绍位运算符的几个典型应用: (1)      三种不用临时变量交换两个整数的实例,并分析每个实例的优缺点 (2)      进制转换,通过位运算实现将十进制数按二进制和十六进制输出,并得出一个通用的,用于将十进制按照2的n次方进制输出的程序. (3)      给出利用位运算实现的计算整数的二进制表示中有多少个1的实例

位运算的操作与算法

在上一次的博客中,我们实现了使用位操作去实现四则运算.实现整数的加减乘除.这次我们将讨论位运算在算法中的一些妙用. 位运算可以进行的骚操作 在这里我将使用题目进行示例 题1:找出唯一成对的数 1-1000这1000个数放在含有1001个元素的数组中,只有唯一的一个元素值重复,其它均只出现一次.每个数组元素只能访问一次,设计一个算法,将它找出来;不用辅助存储空间,能否设计一 个算法实现? 这个题目有两个要注意的点 数的范围是1-1000,这个是确定的 不能使用辅助储存空间 只有一个数字g重复 那么

位运算常用操作总结位运算应用口诀清零取反要用与,某位置一可用或若要取反和交换,轻轻松松用异或移位运

来源:http://www.educity.cn/wenda/381487.html 位运算常用操作总结位运算应用口诀 清零取反要用与,某位置一可用或 若要取反和交换,轻轻松松用异或 移位运算 要点 1 它们都是双目运算符,两个运算分量都是整形,结果也是整形.     2 " $amp;     3 "$amp;>amp;>quot;$右移:右边的位被挤掉.对于左边移出的空位,如果是正数则空位补0,若为负数,可能补0或补1,这取决于所用的计算机系统.     4 "

位运算之——按位与(&amp;)操作——(快速取模算法)

由于位运算直接对内存数据进行操作,不需要转成十进制,因此处理速度非常快. 按位与(Bitwise AND),运算符号为& a&b 的操作的结果:a.b中对应位同时为1,则对应结果位也为1. 例如: 10010001101000101011001111000 & 111111100000000 --------------------------------------------- 10101100000000 对10101100000000进行右移8位得到的是101011,这就得

数字位运算操作与算法简单示例

我们对于位运算可能既陌生又熟悉.知道其运算方法运算过程,但不能运用好它. 首先,我们还是回顾一下Java中位运算都包含那些操作: 一.与运算(&) 运算法则:将二进制数进行按位与运算.0&0=0:0&1=0:1&1=1 : 如:0011 & 0010 = 0010: 二.或运算(|) 运算法则:将二进制数进行按位或运算.0|0 =0:1|0 = 1;  1|1=1 如:0011 & 0010 = 0011: 三.异或运算(^) 运算法则:将二进制数进行按位异

位运算总结&amp;拾遗

JavaScript 位运算总结&拾遗 最近补充了一些位运算的知识,深感位运算的博大精深,此文作为这个系列的总结篇,在此回顾下所学的位运算知识和应用,同时也补充下前文中没有提到的一些位运算知识. 把一个数变为大于等于该数的最小的2的幂 一个数为2的幂,那么该数的二进制码只有最高位是1. 根据这个性质,我们来举个栗子,比如有数字10,转为二进制码后为: 1 0 1 0 我们只需把 0 bit的位置全部用1填充,然后再把该二进制码加1就ok了.而x | (x + 1)正好可以把最右边的0置为1,可是