zoukankan      html  css  js  c++  java
  • 二进制中 1 的个数(C++ 和 Python 实现)

    (说明:本博客中的题目题目详细说明参考代码均摘自 “何海涛《剑指Offer:名企面试官精讲典型编程题》2012年”)

    题目

      请实现一个函数,输入一个整数,输出该数二进制表示中 1 的个数。例如把 9 表示成二进制是 1001,有 2 位是 1。因此如果输入 9,该函数输出是 2。

    算法设计思想

      计算一个整数的二进制表示中 1 的个数有多种算法。本文主要介绍两种算法,按位与运算算法和快速算法,更多算法,可以查看网友 zdd 的博文 “算法-求二进制数中1的个数”。

      按位与运算算法思想很简单,即对整数的二进制表示的每一位与 1 求与,所得结果为非 0 的个数,即为一个整数二进制表示中 1 的个数,这种算法所需的移位次数至少为整数的二进制表示中,数字 1 所在的最高位的位次(例如 0b0101,高位和低位所在的位次分别为 2 和 0),不够高效;

      快速算法,则不采用移位操作,而是用整数 i 与这个整数减 1 的值 i - 1,按位求与,如此可以消除,整数的二进制表示中,最低位的 1 。整数的二进制表示有几个 1,则只需计算几次。在 C/C++ 实现时,负整数溢出后为最大整数,但 Python 数值类型(Numeric Type)不会出现溢出的情况,所以,此时,还需要对边界值进行限定。

    注,整数的二进制表示方式和移位操作处理方式:
    1)整数的二进制表示方式:在计算机中,整数的二进制表示中,最高位为符号位。最高位为 0 时,表示正数; 最高位为 1 时表示负数。
    2)对整数的移位操作:
    当整数是负数时,右移时,最低位丢弃最高位补 1;  左移时,最高位丢弃,最低位补 0。
    当整数是正数时,右移时,最低位丢弃,最高位补 0;  左移时,最高位丢弃,最低位补 0 。

     

    C++ 实现

    /*
    * Author: klchang
    * Date: 2017.12.16
    * Description: Compute the number of 1 in the binary representation of an integer.
    */
    
    #include <iostream>
    
    #define INT_BITS 32
    
    // Generic method: bitwise and operation with a number that has only one 1 in binary.
    int numberOf_1_InBinary_Generic(int i)
    {
        int count = 0;
        int shiftCount = 0;
    
        while (i && shiftCount < INT_BITS)
        {
            if (i & 1) {
                ++ count;
            }
            i = i >> 1;
            ++ shiftCount;
        }
    
        return count;
    }
    
    // Fast method: bitwise and operation between integer i and (i-1).
    int numberOf_1_InBinary_Fast(int i)
    {
        int count = 0;
        while (i)
        {
            std::cout << "iter " << count << ": " << i << std::endl;
            i = i & (i - 1);
            count ++;
        }
    
        return count;
    }
    
    void unitest()
    {
        int data[] = {-5, 0, 5};
    
        std::cout << "---------------------- Generic Method -----------------------"  << std::endl;
        for (int i = 0; i < 3; ++i)
            std::cout << "The number of 1 in the binary representation of " << data[i] << " is "
                      << numberOf_1_InBinary_Generic(data[i]) << ".
    " << std::endl;
    
        std::cout << "----------------------- Fast Method --------------------------"  << std::endl;
        for (int i = 0; i < 3; ++i)
            std::cout << "The number of 1 in the binary representation of " << data[i] << " is "
                      << numberOf_1_InBinary_Fast(data[i]) << ".
    " << std::endl;
    }
    
    int main()
    {
        unitest();
    
        return 0;
    }

     

    Python 实现

    原理

      为了更好的理解这个问题在 Python 中的实现,先简单介绍 Python 数值类型,需要注意 Python 2 和 Python 3 的数值类型是有些区别的。Python 2 的数值类型有 4 种类型,即 int, long, float 和 complex 。而在 Python 3 中,int 和 long 类型已经整合到一起,成为新的 int 类型,也就是说,Python 3 中,只有 3 种类型,即 int, float 和 complex。 对 bool 类型,在 Python 2 中,其是普通整型 int (at least 32 bits of precision)的子类型;在 Python 3 中,其是 int 类型(unlimited precision)的子类型。

      Python 2 的数值类型 int 是普通整型,是有范围的,可以通过 sys.maxint 获取其最大值,至少 32 bit。当 Python 2 程序中的整数值超出范围后,自动转换为 long 类型,而 long 类型是没有范围限制的,即 unlimited precision。在 Python 3 中,这两种类型被统一起来,表示为 int 类型,与 Python 2 的数值类型 long 相同,没有范围限定(unlimited precision)。也就是说,在 Python 中,整型数是没有溢出的(overflow)。在 Python 程序中,当对一个负整数与其减 1 后的值按位求与,若结果为 0 退出,循环执行此过程。由于整型数可以有无限的数值精度,其结果永远不会是 0,如此编程,在 Python 中,只会造成死循环。而在 C/C++ 中,整数(32 bit)的范围是 [ - 2147483648, 2147483647 ],与此相对, Python 2 中的 long 类型和 Python 3 中 int 类型,如果不指定整型数的位数,是没有范围限制的。

    注:数值精度(numeric precision)是指数值中的数字位数(the number of digits);数值尺度(numeric scale)是指数值中的小数位数(the number of digits after the decimal point)。例如 123.45 可表示为 decimal(p = 5, s = 2),即 10进制,数值精度为 5, 数值尺度为 2。

    实现

    #!/usr/bin/python
    # -*- coding: utf8 -*-
    """
    # Author: klchang
    # Date: 2017.12.16
    # Description: Compute the number of 1 in the binary representation of an integer.
    """
    
    INT_BITS = 32 
    MAX_INT = (1 << (INT_BITS - 1)) - 1  # Maximum Integer for INT_BITS
    
    
    # Generic method: bitwise and operation with a number that has only one 1 in binary.
    def number_of_1_in_binary_generic(num):
        
        count, bit = 0, 1
        while num and bit <= MAX_INT + 1:
            if bit & num:
                count += 1
                num -= bit
            bit = bit << 1
    
        return count
            
    
    # Fast method: bitwise and operation between integer num and (num-1).
    def number_of_1_in_binary_fast(num):
        count = 0
        while num:
            if num < - MAX_INT - 1 or num > MAX_INT:
                break
            print("iter %d: %d" % (count, num))
            count += 1
            num = num & (num-1)
            
        return count
    
    
    def unitest():
        nums = [-5, 0, 5]
        # Generic Method
        print("-" * 30 + " Generic Method " + "-" * 30)
        for n in nums:
            print("The number of 1 in the binary representation of %d is %d.
    " % (n, number_of_1_in_binary_generic(n)))
        # Fast Method
        print('
    ' + "-" * 30 + " Fast Method " + "-" * 30)
        for n in nums:
            print("The number of 1 in the binary representation of %d is %d.
    " % (n, number_of_1_in_binary_fast(n)))
    
    if __name__ == '__main__':
        unitest()

    参考代码

     1. targetver.h

    #pragma once
    
    // The following macros define the minimum required platform.  The minimum required platform
    // is the earliest version of Windows, Internet Explorer etc. that has the necessary features to run 
    // your application.  The macros work by enabling all features available on platform versions up to and 
    // including the version specified.
    
    // Modify the following defines if you have to target a platform prior to the ones specified below.
    // Refer to MSDN for the latest info on corresponding values for different platforms.
    #ifndef _WIN32_WINNT            // Specifies that the minimum required platform is Windows Vista.
    #define _WIN32_WINNT 0x0600     // Change this to the appropriate value to target other versions of Windows.
    #endif

    2. stdafx.h

    // stdafx.h : include file for standard system include files,
    // or project specific include files that are used frequently, but
    // are changed infrequently
    //
    
    #pragma once
    
    #include "targetver.h"
    
    #include <stdio.h>
    #include <tchar.h>
    
    
    
    // TODO: reference additional headers your program requires here

    3. stdafx.cpp

    // stdafx.cpp : source file that includes just the standard includes
    // NumberOf1.pch will be the pre-compiled header
    // stdafx.obj will contain the pre-compiled type information
    
    #include "stdafx.h"
    
    // TODO: reference any additional headers you need in STDAFX.H
    // and not in this file

    4. NumberOf1.cpp

    // NumberOf1.cpp : Defines the entry point for the console application.
    //
    
    // 《剑指Offer——名企面试官精讲典型编程题》代码
    // 著作权所有者:何海涛
    
    #include "stdafx.h"
    #include <string.h>
    #include <stdlib.h>
    
    // ====================方法一====================
    int NumberOf1(unsigned int n);
    
    int NumberOf1Between1AndN_Solution1(unsigned int n)
    {
        int number = 0;
    
        for(unsigned int i = 1; i <= n; ++ i)
            number += NumberOf1(i);
    
        return number;
    }
    
    int NumberOf1(unsigned int n)
    {
        int number = 0;
        while(n)
        {
            if(n % 10 == 1)
                number ++;
    
            n = n / 10;
        }
    
        return number;
    }
    
    // ====================方法二====================
    int NumberOf1(const char* strN);
    int PowerBase10(unsigned int n);
    
    int NumberOf1Between1AndN_Solution2(int n)
    {
        if(n <= 0)
            return 0;
    
        char strN[50];
        sprintf(strN, "%d", n);
    
        return NumberOf1(strN);
    }
    
    int NumberOf1(const char* strN)
    {
        if(!strN || *strN < '0' || *strN > '9' || *strN == '')
            return 0;
    
        int first = *strN - '0';
        unsigned int length = static_cast<unsigned int>(strlen(strN));
    
        if(length == 1 && first == 0)
            return 0;
    
        if(length == 1 && first > 0)
            return 1;
    
        // 假设strN是"21345"
        // numFirstDigit是数字10000-19999的第一个位中1的数目
        int numFirstDigit = 0;
        if(first > 1)
            numFirstDigit = PowerBase10(length - 1);
        else if(first == 1)
            numFirstDigit = atoi(strN + 1) + 1;
    
        // numOtherDigits是01346-21345除了第一位之外的数位中1的数目
        int numOtherDigits = first * (length - 1) * PowerBase10(length - 2);
        // numRecursive是1-1345中1的数目
        int numRecursive = NumberOf1(strN + 1);
    
        return numFirstDigit + numOtherDigits + numRecursive;
    }
    
    int PowerBase10(unsigned int n)
    {
        int result = 1;
        for(unsigned int i = 0; i < n; ++ i)
            result *= 10;
    
        return result;
    }
    
    // ====================测试代码====================
    void Test(char* testName, int n, int expected)
    {
        if(testName != NULL)
            printf("%s begins: 
    ", testName);
        
        if(NumberOf1Between1AndN_Solution1(n) == expected)
            printf("Solution1 passed.
    ");
        else
            printf("Solution1 failed.
    "); 
        
        if(NumberOf1Between1AndN_Solution2(n) == expected)
            printf("Solution2 passed.
    ");
        else
            printf("Solution2 failed.
    "); 
    
        printf("
    ");
    }
    
    void Test()
    {
        Test("Test1", 1, 1);
        Test("Test2", 5, 1);
        Test("Test3", 10, 2);
        Test("Test4", 55, 16);
        Test("Test5", 99, 20);
        Test("Test6", 10000, 4001);
        Test("Test7", 21345, 18821);
        Test("Test8", 0, 0);
    }
    
    int _tmain(int argc, _TCHAR* argv[])
    {
        Test();
    
        return 0;
    }

    5. 参考代码下载

    项目 10_NumberOf1 下载: 百度网盘

    何海涛《剑指Offer:名企面试官精讲典型编程题》 所有参考代码下载:百度网盘

    参考资料

    [1]  何海涛. 剑指 Offer:名企面试官精讲典型编程题 [M]. 北京:电子工业出版社,2012. 77-82.

    [2] Python Software Foundation. Python 2.7.14 Documentation, The Python Standard Library, 5.4. Numeric Types — int, float, long, complex [OL]. https://docs.python.org/2/library/stdtypes.html#numeric-types-int-float-long-complex. 2017.

    [3] Python Software Foundation. Python 3.6.4rc1 Documentation, The Python Standard Library, 4.4. Numeric Types — int, float, complex [OL]. https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex. 2017.

    [4] Stack Overflow Users. How do I interpret precision and scale of a number in a database [OL]. https://stackoverflow.com/questions/2377174/how-do-i-interpret-precision-and-scale-of-a-number-in-a-database.

  • 相关阅读:
    转:10分钟掌握XML、JSON及其解析
    转:关于C++14:你需要知道的新特性
    2014/11/4~2014/12/20阶段性目标
    转:快速掌握一个语言最常用的50%
    推荐!国外程序员整理的 C++ 资源大全
    数据库面试宝典
    sqlite学习
    android
    转:c的回归-云风
    原创: 开题报告中摘要部分快速将一段文字插入到word的表格中
  • 原文地址:https://www.cnblogs.com/klchang/p/8017627.html
Copyright © 2011-2022 走看看