求二进制数中1的个数


要求:对于一个字节(8bit)的变量,求其二进制表示中“1的个数,要求算法的执行效率尽可能地高。

大多数的读者都会有这样的反应:这个题目也太简单了吧,解法似乎也相当地单一,不会有太多的曲折分析或者峰回路转之处。那么面试者到底能用这个题目考察我们什么呢?事实上,在编写程序的过程中,根据实际应用的不同,对存储空间或效率的要求也不一样。比如在PC上的程序编写与在嵌入式设备上的程序编写就有很大的差别。我们可以仔细思索一下如何才能使效率尽可能地“高”。

【解法一】

可以举一个八位的二进制例子来进行分析。对于二进制操作,我们知道,除以一个2,原来的数字将会减少一个0。如果除的过程中有余,那么就表示当前位置有一个1。

以10 100 010为例;

第一次除以2时,商为1 010 001,余为0。

第二次除以2时,商为101 000,余为1。

因此,可以考虑利用整型数据除法的特点,通过相除和判断余数的值来进行分析。于是有了如下的代码。

版本1

int count(int v)
{
    
int num = 0;
    
while(v)
    {
        
if(v % 2 == 1)
        {
            num
++;
        }
        v 
= v/ 2;
    }
    
return num;
}

 

【解法二】使用位操作

前面的代码看起来比较复杂。我们知道,向右移位操作同样也可以达到相除的目的。唯一不同之处在于,移位之后如何来判断是否有1存在。对于这个问题,再来看看一个八位的数字:10 100 001。

在向右移位的过程中,我们会把最后一位直接丢弃。因此,需要判断最后一位是否为1,而“与”操作可以达到目的。可以把这个八位的数字与00000001进行“与”操作。如果结果为1,则表示当前八位数的最后一位为1,否则为0。代码如下:

版本2

int count(int v)
{
    
int num = 0;
    
while(v)
    {
        num 
+= v &0x01;
        v 
>>= 1;
    }
    
return num;
}

 

【解法三】

位操作比除、余操作的效率高了很多。但是,即使采用位操作,时间复杂度仍为O(log2v),log2v为二进制数的位数。那么,还能不能再降低一些复杂度呢?如果有办法让算法的复杂度只与“1”的个数有关,复杂度不就能进一步降低了吗?

同样用10 100 001来举例。如果只考虑和1的个数相关,那么,我们是否能够在每次判断中,仅与1来进行判断呢?

为了简化这个问题,我们考虑只有一个1的情况。例如:01 000 000。

如何判断给定的二进制数里面有且仅有一个1呢?可以通过判断这个数是否是2的整数次幂来实现。另外,如果只和这一个“1”进行判断,如何设计操作呢?我们知道的是,如果进行这个操作,结果为0或为1,就可以得到结论。

如果希望操作后的结果为0,01 000 000可以和00 111 111进行“与”操作。

这样,要进行的操作就是 01 000 000 &(01 000 000 – 00 000 001)= 01 000 000 &
00 111 111 = 0。

因此就有了解法三的代码:

版本3

int count(int v)
{
    
int num = 0;
    
while(v)
    {
        v 
&= (v-1);
        num
++;
    }
    
return num;
}

 

【解法四】使用分支操作

解法三的复杂度降低到OM),其中Mv中1的个数,可能会有人已经很满足了,只用计算1的位数,这样应该够快了吧。然而我们说既然只有八位数据,索性直接把0~255的情况都罗列出来,并使用分支操作,可以得到答案,代码如下:

版本4

int count(int v)
{
    
int num = 0;
    
switch (v)
    {
    
case 0x0:
        num 
= 0;
        
break;
    
case 0x1:
    
case 0x2:
    
case 0x4:
    
case 0x8:
    
case 0x10:
    
case 0x20:
    
case 0x40:
    
case 0x80:
        num 
= 1;
        
break;
    
case 0x3:
    
case 0x6:
    
case 0xc:
    
case 0x18:
    
case 0x30:
    
case 0x60:
    
case 0xc0:
        num 
= 2;
        
break;
        
//
    }
    
return num;
}


解法四看似很直接,但实际执行效率可能会低于解法二和解法三,因为分支语句的执行情况要看具体字节的值,如果a =0,那自然在第1个case就得出了答案,但是如果a =255,则要在最后一个case才得出答案,即在进行了255次比较操作之后!

看来,解法四不可取!但是解法四提供了一个思路,就是采用空间换时间的方法,罗列并直接给出值。如果需要快速地得到结果,可以利用空间或利用已知结论。这就好比已经知道计算1+2+ … +N的公式,在程序实现中就可以利用公式得到结论。

最后,得到解法五:算法中不需要进行任何的比较便可直接返回答案,这个解法在时间复杂度上应该能够让人高山仰止了。

【解法五】查表法

/* 预定义的结果表 */

int countTable[256=
{
    
01121223122323341223233423
    
3434451223233423343445233
    
4344534454556122323342334
    
3445233434453445455623343
    
4453445455634454556455656
    
6712232334233434452334344
    
5344545562334344534454556
    
3445455645565667233434453
    
4454556344545564556566734
    
4545564556566745565667566
    
76778
};

int count(int v)
{
    
//check parameter
    return countTable[v];

}


这是个典型的空间换时间的算法,把0~255中“1的个数直接存储在数组中,v作为数组的下标,countTable[v]就是v中“1的个数。算法的时间复杂度仅为O(1)。

在一个需要频繁使用这个算法的应用中,通过“空间换时间”来获取高的时间效率是一个常用的方法,具体的算法还应针对不同应用进行优化。

扩展问题

1.   如果变量是32位的DWORD,你会使用上述的哪一个算法,或者改进哪一个算法?

2.   另一个相关的问题,给定两个正整数(二进制形式表示)AB,问把A变为B需要改变多少位(bit)?也就是说,整数A B 的二进制表示中有多少位是不同的?

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/268785.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

CentOS安装glibc-2.14

到http://ftp.gnu.org/gnu/glibc/下载glibc-2.14.tar.xz tar glibc-2.14.tar.gz cd glibc-2.14 mkdir build cd build ../configure --prefix/usr/local/glibc-2.14 make -j4 su xxxx make install 看看现在libc.so.6在哪个位置,然后修改软链接 ln -s /usr/local/gl…

cesium米转换经纬度_cesium 常见坐标系及坐标转换(工具篇)

/** Description: 坐标转换工具,下面的坐标转换默认都是基于WGS84 椭球体*/class Degrees {constructor(longitude, latitude, height 0) {this.longitude longitude;this.latitude latitude;this.height height;}}Cesium.Degrees Degrees;//不建议这样直接修改…

sql 2005 try catch

--删除卡的记录 delete from ObjTransportCard where CardIdCardId --删除交易的记录 delete from ObjTransaction where CardIdCardId 在Sql 2000中,为了成功执行这个存储过程,我需要在每一条语句后面去判断ERROR,如果有错,则执行回滚.上例只是我举的一个简单的例子,如果在一个…

selenium拖动元素java_【自动化测试】Java+Selenium操作页面元素(合集)

本文基于Java语言,依托于Eclipse工具,使用Selenium框架,主要介绍在Selenium中,如何操作Web页面中的各种元素。Eclipse 搭建1.1、Eclipse 配置1.2、引入依赖包修改pom.xml文件在dependencys节点下,添加如下内容&#xf…

面试题 锁消除是什么

锁消除是在编译器级别的事情。 在即时编译器时,如果发现不可能被共享的对象,则可以消除这些对象的锁操作。 也许你会觉得奇怪,既然有些对象不可能被多线程访问,那为什么要加锁呢?写代码时直接不加锁不就好了。 但是…

通过日志恢复SQL Server的历史数据

园子里前段时间发过一篇通过日志恢复MSSQL数据例子 ,我总结一下 通过日志还原,最重要的是: 1.必须有一个完整的备份,且这个备份必须是在修改、删除数据之前做的。 2.在更新、删除数据之后,做日志备份,该log…

JavaScript面向对象编程指南(五) 原型

第5章 原型 5.1 原型属性 function f(a,b){return a*b;};// length 属性f.length; //2// constructor 构造属性f.constructor; // function Function() { [native code] }// prototype 属性 初始值是空对象typeof f.prototype; //"object" 5.1.1 利用原型添加方法和…

java communal_平台用英语怎么说

平台通常指高于附近区域的平面;楼房的阳台。也指指计算机硬件或软件的操作环境。而在网络上所说的平台一般是指网站。那么你知道平台用英语怎么说吗?下面来学习一下吧。平台英语说法1:platform平台英语说法2:terrace平台英语说法3:flat roof…

面试题-- 什么是偏向锁

所谓的偏向,就是偏心,即锁会偏向于当前已经占有锁的线程 。 大部分情况是没有竞争的(某个同步块大多数情况都不会出现多线程同时竞争锁),所以可以通过偏向来提高性能。即在无竞争时,之前获得锁的线程再次获…

Ping, IPConfig, Format, Netstat, etc. Not Recognized as Commands

Can you use them when you point directly to them..> C:\Windows\System32\ping.exe 127.0.0.1? If so, you probably have a messed up PATH variable.> echo %PATH%Should have "C:\Windows\System32" in it, does it? 以上为google的结果,我…

Collections.min()和Collections.max()的使用

取集合中的最小值 Collections.min(); 取集合中的最大值 Collections.max(); public class TestCollectionMinMax {public static void main(String[] args) {List<Integer> list new ArrayList<>();list.add(3);list.add(7);list.add(9);list.add(1);list.add(…

java date 过时_过时date.toLocaleString()的解决方法

过时date.toLocaleString()的解决方法 Java代码 收藏代码 System.out.println(new java.util.Date()); 输出&#xff1a;Thu Jan 27 14:43:28 CST 2011 Java代码 收藏代码 System.out.println(new java.util.Date().toLocaleString()); 输出&#xff1a;2011-1-27 14:45:…

linux下查看隐藏的文件

在linux下以点开头命名的文件在系统中视为隐藏文件&#xff0c;例如 “.123 ”。用ls、ls -l或者ll是无法查看的。那么如何才能查看linux系统下的隐藏文件呢&#xff1f;下面介绍一下简单的查看方式。1、#在命令行下使用ls -a2、#在XWindow的KDE桌面中在"查看(View)"…

python学习之路(九)

这一节主要讲的是装饰器。装饰器是一个非常好用的&#xff0c;用于装饰已有函数的函数功能。 其优点是不用修改调用方式&#xff0c;还不用修改源代码。 他的思想是&#xff1a;函数既变量&#xff1b;高阶函数&#xff1b;嵌套函数。 现在来尝试写装饰器 有两个函数 各自实现自…

java http请求实现_java工程实现http请求接口

java工程实现http请求接口java工程实现http请求接口1.实现代码package com.home;import com.alibaba.fastjson.JSON;import com.alibaba.fastjson.JSONObject;import com.sun.net.httpserver.HttpExchange;import com.sun.net.httpserver.HttpHandler;import com.sun.net.https…

Java集合中removeIf的使用

使用removeIf方法从List中删除元素 在JDK1.8中&#xff0c;Collection以及其子类新加入了removeIf方法&#xff0c;作用是按照一定规则过滤集合中的元素。这里展示removeIf的用法。 需求是过滤掉学生中分数为为18以下的&#xff0c; 一个学生实体类 Data public class Studen…

Java中lombok @Builder注解使用详解

简介 Lombok大家都知道&#xff0c;在使用POJO过程中&#xff0c;它给我们带来了很多便利&#xff0c;省下大量写get、set方法、构造器、equal、toString方法的时间。除此之外&#xff0c;通过Builder注解&#xff0c;lombok还可以方便的实现建造者模式&#xff0c;创建对象 …

鼠标控制

一&#xff09;客户端 using System;using System.Net;using System.Net.Sockets;using System.Text;using System.IO;using System.Threading; namespace 鼠标控制_客户端{ //我们定义一个保存鼠标坐标的结构 public struct MousePosition { private int x; …

java resourcebundle_Java - Properties和ResourceBundle类学习

一、前言在项目的开发过程中&#xff0c;为了统一配置的管理&#xff0c;我们经常需要将一些配置信息根据环境的不同&#xff0c;配置在不同的properties中&#xff0c;然后从里面进行读取。而Properties类作为最基础也是最经常使用的类&#xff0c;通过本文我们来学习一下它的…