欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

一个自然数的二进制表示中有多少个1?

程序员文章站 2024-02-27 16:20:21
...

用C++写的,前两天看到一个博客上面写的idea,我感觉不错,于是实现了一下。

#include <iostream>
#include "CountOneNumbers.hpp"

int main(int argc, const char * argv[]) {
    // insert code here...
    int inputVariable = 1234;
    CountOneNumbers::countOneNumbers0(inputVariable);
    CountOneNumbers::printfBinary(inputVariable);
    return 0;
}
#ifndef CountOneNumbers_hpp
#define CountOneNumbers_hpp

#include <stdio.h>
#include <iostream>

class CountOneNumbers {
public:
    //打印该数的二进制有多少个1
    static void countOneNumbers0(int someNumber);
    //打印它的二进制形式
    static void printfBinary(int someNumber);
};

#endif /* CountOneNumbers_hpp */
#include "CountOneNumbers.hpp"

//在这里要计算的是一个数的二进制表示中有多少个1.
//先分析一下思路,你要想知道一个数中有多少个1就应该把该数中的1一个一个的剥离出来。
//那么如何剥离出来呢?
//现在不知道,但是现在可以知道的是,剥离之前和剥离之后这个数的二进制表示的区别所在。
//现在假设被计算的数字是11001100
//被剥离以后的数字应该是11001000
//即,从右往左把遇到的第一个1抽出来。
//怎么样达到这一目的呢?
//须知2进制是比较特殊的进制,特殊点在于它只要发生进位或者借位就会不断影响前面的位。
//具体为如果发生借位,那么它会一直影响直到遇到左边第一个1,
//如果发生进位,那么它会一直影响直到遇到左边第一个0位置。
//其实这一点在其他进制里面也会遇到的,不过不像2进制这样敏感而已,比如说十进制数
//9999999,此时只要加1,它就会影响左边的数字直到遇到不是9的数字。
//那么回到如何剥离1这个话题来,从11001100到11001000正好是要把从右到左遇到的第一个1变成0的操作,
//于是借位操作能够满足这个要求,于是11001100减1得到11001011.
//再观察11001011和11001000的关系发现,
//只要把11001100和11001011进行异或即可得到11001000从而剥离出来一个1.
//那么不断剥离的极限是什么?那就是0没有了呗。
//那又代表什么?代表变成0了呗。
//在这里仅以正整数作为输入来统计数字中的1的个数。
void CountOneNumbers::countOneNumbers0(int someNumber) {
    if (someNumber < 0) {
        std::cout<<"输入不合法"<<std::endl;
        return;
    }
    int oneCounter = 0;
    while (someNumber > 0) {
        someNumber = someNumber & someNumber - 1;
        oneCounter++;
    }
    std::cout<<"1的个数为:"<<oneCounter<<std::endl;
}

//基于你可以用该方法来打印一个数字的二进制表示。
//这里还是假设输入的数字是自然数。
void CountOneNumbers::printfBinary(int someNumber) {
    if (someNumber < 0) {
        std::cout<<"输入不合法"<<std::endl;
        return;
    }
    //因为上面的方法是从右到左,所以直接应用此法打印的是倒置的数列。
    //所以要把它正过来才行。所以现在要做的是如何剥离最高位的1.
    //首先获取int的位数,那么如何做到这一点呢?首先知道
    //该数字所属类型,于是可知该类型所占字节数,进而可知该类型所占位数
    //设置一个变量,这个变量的二进制形式是它只有一个1,并且这个1会从int类型的最高位
    //移动到int类型的最低位。每次移动一下就和输入的数字相与只要不为0就说明遇到1了。
    //否则跳过去。直到这个变量的幂指数变为负。或者把最大的2次方求出来然后不断除以2直到为0.
    //不然老是求幂指数运算代价也很大。
    //位移也可以实现但是高位补0还是补1并不统一,在类型正负变化的时候通用性不强。
    //最高位有可能是符号位,对于有符号类型来说,所以还要考虑这一点。
    //这个运算应该停留在应用层面而不是计算机底层的运算,因为每种语言或者计算机的底层实现有所差异
    //而面向应用层的接口则可以保持不变。
    //我所追求的就是通用。
    bool canPrint = false;
    std::cout<<someNumber<<"的二进制表示为:";
    int powerSign = sizeof(someNumber) * 8 - 1;
    //这里本来是用pow函数的,但是那需要用到math.h头文件
    //感觉不太完美,于是采用移位操作。
    unsigned int maxWeight = 1;
    maxWeight = maxWeight << powerSign;
    while (maxWeight > 0) {
        if ((maxWeight & someNumber) > 0) {
            std::cout<<"1";
            canPrint = true;
        } else {
            if (canPrint)std::cout<<"0";
        }
        maxWeight /= 2;
    }
    std::cout<<std::endl;
}

输出如下

1的个数为:5
1234的二进制表示为:10011010010
Program ended with exit code: 0