关于Python 位运算防坑指南

目录
  • 1、背景
  • 2、C# 语言
  • 3、Python 语言
  • 4、技术分析

1、背景

我们先看这个题目:

标题:137. 只出现一次的数字 II
难度:中等
https://leetcode-cn.com/problems/single-number-ii/

给定一个 非空 整数数组,除了某个元素只出现一次以外,其余每个元素均出现了三次。找出那个只出现了一次的元素。

说明:

你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?

示例 1:

输入: [2,2,3,2]
输出: 3

示例 2:

输入: [0,1,0,1,0,1,99]
输出: 99

思路:

初始result = 0,将每个数想象成 32 位的二进制,对于每一位的二进制的1累加起来必然是3N或者3N + 1(出现3次和1次);3N代表目标值在这一位没贡献,3N + 1代表目标值在这一位有贡献(=1),然后将所有有贡献的位记录到result中。这样做的好处是如果题目改成k个一样,只需要把代码改成count % k即可,很通用并列去找每一位。

2、C# 语言

  • 执行结果:通过
  • 执行用时:112 ms, 在所有 C# 提交中击败了 91.53% 的用户
  • 内存消耗:25.2 MB, 在所有 C# 提交中击败了 100.00% 的用户
public class Solution
{
    public int SingleNumber(int[] nums)
    {
        int result = 0;
        for (int i = 0; i < 32; i++)
        {
            int mask = 1 << i;
            int count = 0;
            for (int j = 0; j < nums.Length; j++)
            {
                if ((nums[j] & mask) != 0)
                {
                    count++;
                }
            }
            if (count % 3 != 0)
            {
                result |= mask;
            }
        }
        return result;
    }
}

3、Python 语言

class Solution:
    def singleNumber(self, nums: List[int]) -> int:
        result = 0
        for i in range(32):
            mask = 1 << i
            count = 0
            for num in nums:
                if num & mask != 0:
                    count += 1
            if count % 3 != 0:
                result |= mask
        return result

以上 Python 代码与 C# 代码逻辑完全一致,但提交时报错。错误信息如下:

输入:[-2,-2,1,1,-3,1,-3,-3,-4,-2]
输出:4294967292
预期结果:-4

我们发现:

-4 补码为 1111 1111 1111 1111 1111 1111 1111 1100

如果不考虑符号位

1111 1111 1111 1111 1111 1111 1111 1100 -> 4294967292

是不是很坑,C++,C#,Java等语言的整型是限制长度的,如:byte 8位,int 32位,long 64位,但 Python 的整型是不限制长度的(即不存在高位溢出),所以,当输出是负数的时候,会导致认为是正数!因为它把32位有符号整型认为成了无符号整型,真是坑。

我们对以上的代码进行修改,加入判断条件 if result > 2 ** 31-1: 超过32位整型的范围就表示负数了result -= 2 ** 32,即可得到对应的负数。

  • 执行结果:通过
  • 执行用时:96 ms, 在所有 Python3 提交中击败了 19.00% 的用户
  • 内存消耗:14.8 MB, 在所有 Python3 提交中击败了 25.00% 的用户
class Solution:
    def singleNumber(self, nums: List[int]) -> int:
        result = 0
        for i in range(32):
            mask = 1 << i
            count = 0
            for num in nums:
                if num & mask != 0:
                    count += 1
            if count % 3 != 0:
                result |= mask
            if result > 2 ** 31-1:
                result -= 2 ** 32
        return result

4、技术分析

上面的问题解决了,我们在深入的探讨一下。

整数在内存中是以补码的形式存在的,输出自然也是按照补码输出。

class Program
{
    static void Main(string[] args)
    {
        string s1 = Convert.ToString(-3, 2);
        Console.WriteLine(s1);
        // 11111111111111111111111111111101

        string s2 = Convert.ToString(-3, 16);
        Console.WriteLine(s2);
        // fffffffd
    }
}

但我们看一下 Python bin() 输出。

print(bin(3))  # 0b11
print(bin(-3))  # -0b11

print(bin(-3 & 0xffffffff))
# 0b11111111111111111111111111111101

print(bin(0xfffffffd))
# 0b11111111111111111111111111111101

print(0xfffffffd)  # 4294967293

是不是很颠覆认知,我们从结果可以看出:

  • Python中bin一个负数(十进制表示),输出的是它的原码的二进制表示加上个负号,巨坑。
  • Python中的整型是补码形式存储的。
  • Python中整型是不限制长度的不会超范围溢出。

所以为了获得负数(十进制表示)的补码,需要手动将其和十六进制数0xffffffff进行按位与操作,再交给bin()进行输出,得到的才是负数的补码表示。

总结:
这篇图文从一道Leetcode题目开始说起,发现C#语言与Python语言在利用二进制处理整型数据时存在不同,Python语言不属于强类型语言所以不限制整型的位数,表面上看好像方便使用其实就是个坑。大家使用时多加小心。

到此这篇关于关于Python 位运算防坑指南的文章就介绍到这了,更多相关Python 位运算防坑指南内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 简单了解python的一些位运算技巧

    前言 位运算的性能大家想必是清楚的,效率绝对高.相信爱好源码的同学,在学习阅读源码的过程中会发现不少源码使用了位运算.但是为啥在实际编程过程中应用少呢?想必最大的原因,是较为难懂.不过,在面试的过程中,在手写代码过程中,写出一两个位运算的代码,还会让面试官眼前一亮的. 位运算常用的运算符包括&(按位与), | (按位或),~(按位非),^(按位异或),<< (有符号左移位) ,>>(有符号右移位). 下面用几个例子说明其应用,希望对你有所启发. 1.判断奇数还是偶数 通常判

  • 初步认识Python中的列表与位运算符

    Python列表 List(列表) 是 Python 中使用最频繁的数据类型. 列表可以完成大多数集合类的数据结构实现.它支持字符,数字,字符串甚至可以包含列表(所谓嵌套). 列表用[ ]标识.是python最通用的复合数据类型.看这段代码就明白. 列表中的值得分割也可以用到变量[头下标:尾下标],就可以截取相应的列表,从左到右索引默认0开始的,从右到左索引默认-1开始,下标可以为空表示取到头或尾. 加号(+)是列表连接运算符,星号(*)是重复操作.如下实例: #!/usr/bin/python

  • python移位运算的实现

    密码算法程序设计实践选的SHA-1. 在写的过程中遇到一丢丢关于python移位的问题,记录一下. SHA-1其中第一步需要填充消息.简单阐述一下sha1填充消息的过程: 如输入消息"123",先转成ascii码--313233,消息长度为3*8=24. 即00110001 00110010 00110011 然后填充一个1占1bit,再填充447-24bit个0. 10000000...00000000 最后64bit加上消息长度24的二进制0001 1000 二进制相当于是: 00

  • 基础的十进制按位运算总结与在Python中的计算示例

    与运算 & 举例: 3&5                        解法:3的二进制补码是 11,  5的是101, 3&5也就是011&101,先看百位(其实不是百位,这样做只是便于理解) 一个0一个1,根据(1&1=1,1&0=0,0&0=0,0&1=0)可知百位应该是1,同样十位上的数字1&0=0,个位上的数字1&1=1,因此最后的结果是1.(这之后本来应该还有一步,因为我们现在得到的数值只是所求答案的补码,但是因

  • 解析Python中的二进制位运算符

    下表列出了所有的Python语言的支持位运算符.假设变量a持有60和变量b持有13,则: 示例: 试试下面的例子就明白了所有的Python编程语言提供了位运算符: #!/usr/bin/python a = 60 # 60 = 0011 1100 b = 13 # 13 = 0000 1101 c = 0 c = a & b; # 12 = 0000 1100 print "Line 1 - Value of c is ", c c = a | b; # 61 = 0011 1

  • 详细介绍Python语言中的按位运算符

    按位运算符是把数字看作二进制来进行计算的.Python中的按位运算法则如下: 按位与   ( bitwise and of x and y ) &  举例: 5&3 = 1  解释: 101  11 相同位仅为个位1 ,故结果为 1 按位或   ( bitwise or of x and y ) |  举例: 5|3 = 7  解释: 101  11 出现1的位是 1 1 1,故结果为 111 按位异或 ( bitwise exclusive or of x and y ) ^  举例:

  • Python-OpenCV教程之图像的位运算详解

    1.按位取反bitwise_not() 按位取反就是将数值根据每个bit位1变0,0变1,比如0xf0按位取反就变成了0x0f,如果是uint8类型的数据,取反前后的数据相加结果为0xff(255).下面的例子将lena.jpg和opencv-logo.png分别按位取反: import cv2 print('cv2.__version__:',cv2.__version__) img1 = cv2.imread('..\\lena.jpg') img2 = cv2.imread('..\\op

  • 关于Python 位运算防坑指南

    目录 1.背景 2.C# 语言 3.Python 语言 4.技术分析 1.背景 我们先看这个题目: 标题:137. 只出现一次的数字 II 难度:中等 https://leetcode-cn.com/problems/single-number-ii/ 给定一个 非空 整数数组,除了某个元素只出现一次以外,其余每个元素均出现了三次.找出那个只出现了一次的元素. 说明: 你的算法应该具有线性时间复杂度. 你可以不使用额外空间来实现吗? 示例 1: 输入: [2,2,3,2] 输出: 3 示例 2:

  • 详解Python中位运算的简单实现

    目录 简介 应用场景 案例源码 简介 程序中的数在计算机内存中都是以二进制的形式存在的,位运算就是直接对整数在内存中对应的二进制位进行操作,一般是将数字化为二进制数后进行操作. 应用场景 在常规操作和位运算的操作中使用位运算,可以提升性能.但是会造成代码难以理解,建议合理利用. 1.统计奇数 2.统计偶数 3.统计不相同数等 4.求相反数 位运算分有6种: 1.按位与:两个位都为1时,结果才为1(统计奇数)即全1为1. 2.按位或:两个位都为0时,结果才为0(统计偶数)即全0为0. 3.按位异或

  • Linux下安装Python3.6及避坑指南

    Python3的安装 1.安装依赖环境 Python3在安装的过程中可能会用到各种依赖库,所以在正式安装Python3之前,需要将这些依赖库先行安装好. yum -y install zlib-devel bzip2-devel openssl-devel ncurses-devel sqlite-devel readline-devel tk-devel gdbm-devel db4-devel libpcap-devel xz-devel 2. 下载Python3源代码 下载Python3的

  • Python编码爬坑指南(必看)

    自己最近有在学习python,这实在是一门非常短小精悍的语言,很喜欢这种语言精悍背后又有强大函数库支撑的语言.可是刚接触不久就遇到了让人头疼的关于编码的问题,在网上查了很多资料现在在这里做一番总结,权当一个记录也为后来的兄弟姐妹们服务,如果可以让您少走一些弯路本人将倍感荣幸. 先来描述下现象吧: import os for i in os.listdir("E:\Torchlight II"): print i 代码很简单我们使用os的listdir函数遍历了E:\Torchlight

  • Python学习之异常处理的避坑指南

    目录 finally与return的执行顺序 else与return的执行顺序 总结 最终想了想,还是把这个章节单独拎出来,虽然字数不多. 在代码中,存在return也应当执行finally: 存在return时,else是不执行的: 无return时,else正常执行: 如果发生异常,则else也不执行 finally 与 return 的执行顺序 示例代码如下: class Test(object):     def division(self, num1, num2):         t

  • python函数默认参数使用避坑指南

    目录 引言 verify 炸弹 测试接口的数据 原因 改进方案 引言 阿刁是一个自动化测试用例,从一出生他就被赋予终生使命,去测试一个叫登录的过程是否合理.他一直就被关在一个小黑屋里面,从来也没有出去过,小黑屋里还被关着其他的同胞,他们身上都捆着两个小袋子. 小黑屋里很难受,他们都想跑出去,可怎么也跑不出去.Python 是他们的总司令,有一次,python 告诉他们,你们就不要想着跑出去了,你们已经够幸运了,只有 8 个人用这个屋子,别的屋子都挤着 30 多个人呢! “这里还有其他的屋子?”

  • php中and 和 &&出坑指南

    我原来以为PHP中的and和&&是一样的, 只是写法上为了可读性和美观, 事实上我错了. 这里面深藏了一个坑! 看以下代码: $bA = true; $bB = false; $b1 = $bA and $bB; $b2 = $bA && $bB; var_dump($b1); // $b1 = true var_dump($b2); // $b2 = false $bA = false; $bB = true; $b3 = $bA or $bB; $b4 = $bA ||

  • Python list运算操作代码实例解析

    这篇文章主要介绍了Python list运算操作代码实例解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 在操作list的时候,经常用到对列表的操作运算,比如说,列表添加,删除操作,其实,这里面经常回遇到这样一个问题,就是列表的操作容易被混淆了. 有人做了一个总结,这个很清晰,我就不多做阐述了: 1.append() 向列表尾部追加一个新元素,列表只占一个索引位,在原有列表上增加 2.extend() 向列表尾部追加一个列表,将列表中的每个元

随机推荐