窥探算法之美妙——统计整数二进制中1的个数

原文发表在个人博客主页,转载请注明出处python

前言

我一直是一个比较喜欢算法的人,以为算法真的是至关美妙和神奇!!!趁春节有时间看看算法书,体会思想和技术沉淀下来的美妙,今天看到了统计二进制中1的个数这个本来很简单的题目,以前也看过,不过此次看书加深刻思考以后发现里面的水仍是很深的,特别是用python的程序猿更应该明白,闲话少说,开始正题。程序员

题目

实现一个函数,输入一个整数,输出该数二进制表示中1的个数算法

题目分析

拿到这个题目,全部人确定立马有了思路,几个关键词立马在脑海中显现:“循环”,“右移”,“与”。而后很快写出了函数,随便输入几个数验证,没问题。。。可是这个题目须要注意的首先是整数,包括正整数和负整数,其次在python中,数据位数是一个比较模糊的概念,在程序中基本不存在,由于越位以后他会自动将int转为为long类型,因此对python程序员来讲,须要提早搞明白整数的位数,或者在python语言中调用C语言,下面来列举其中的集中解法。api

技巧:python中的左移和右移与其余C/C++等的定义和结果都是不同的,你们能够自行作实验,python中的定义:右移n位定义为除以pow(2,n),左移n位定义为乘以pow(2,n),并且没有溢出(移除的int会升级为long类型)函数

解法

上面题目分析的时候说过每一个人看见题目心中都会涌现出最naive可是不对的解法(不论用什么语言):编码

def count(num):
    cnt = 0
    while num:
        if num & 1 == 1:
            cnt += 1
        num = num >> 1
    return cnt

为何说他不对呢,你们能够输入一个负整数看看,程序会陷入死循环,由于在其余语言中,负数在计算机中是用补码表示的,最高位是1,在右移的过程当中,高位都是用1来填补的,因此while num这个条件一直为真;在python中,根据右移的定义就能够自行推断出来。既然如今右移num不行,那咱们能够左移1,在32的整数中,最多左移32位,1就会变为零,因此这能够做为判断条件,这在C语言中能够写出和上面相似的代码,可是在python中,咱们一块儿能够左移下去(到虚拟内存大小的位数),因此这里我用到了python中的库ctypes,在python中使用C语言,代码以下:code

from ctypes import *
def count(num):
    cnt = 0
    flag = 1
    while c_int(flag).value:
        if c_int(num & flag).value:
            cnt += 1
        flag = flag << 1
    return cnt

上面的代码不论输入正数负数仍是零,均可以获得正确的答案,可是对于全部的整数都须要循环32次才能获得结果,继续改进。看一个简单的例子,整数12的二进制表示为1100,将其减一变为1011,将获得的结果和原树进行按位与,获得1000,因此发现规律没有?把一个整数减去1以后再和原来的整数作按位与,获得的结果至关因而把整数的二进制表示中最右边的一个1变成0,按照这个规律进行遍历,则函数的循环次数为二进制中一的个数次。代码以下:blog

from ctypes import *
def count(num):
    cnt = 0
    while c_int(num).value:
        cnt += 1
        num = (num -1) & num
    return cnt

技巧:把一个整数减去1以后再和原来的整数作按位与,获得的结果至关因而把整数的二进制表示中最右边的一个1变成0内存

以上都是各类语言通用的方法,只不过是以python做为例子而已,那么在python中语言中能够利用他的库函数很容易的解决这个问题,代码以下:get

def num_of_one(num):
    '''
    count the num of "one" in num n
    bin():convert the num to binary string
    :param num: num num
    :return: the num of "one" in num
    '''
    if num >= 0:
        nbin = bin(num)
        return nbin.count('1')
    else:
        num = abs(num)
        nbin = bin(num-1)
        return 32 - nbin.count('1')

上面的代码是为了更加详细的区分正数和负数,固然利用python的一些特性,简化代码以下:

def num_of_one(num):
    nbin = bin(n & 0xffffffff)
    return nbin.count('1')

技巧:对于二进制来讲,先减一后取反和先去反后加一结果同样

博主John Rambo在我博客下面提供了另一种方法,这个方法也是十分巧妙,先用一个列表存储下来0到15的二进制中1的个数,在计算的时候每四位进行查表,代码以下:

#Add up the number of 1 bits in every 4 bits.
#number of 1 bits in 0x0 to 0xF.
counts = [0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4]

def num_of_one(num):
    result = 0
    for i in range(0,32,4):
        result += counts[num >> i & 0xf]
    return result

技巧:在python中,负数和0xffffffff按位与以后变成一个无符号数,二进制表示为编码形式

网友PowerShell免费软件提供了一种方法,也比较巧妙:

$num=Read-Host -Prompt "请输入一个整数(能够是负数)"
([System.Convert]::ToString($num,2)).replace("0","").length
#一句话的事儿

总结

本篇blog中的题目对于不少程序员来讲一点也不陌生,可是要完美的解决这些问题仍是须要一些思考的,同时技巧也是必不可少的,算法如此美妙,计算机的世界使人神往~

相关文章
相关标签/搜索