求一个整数的二进制表示中bit1的总数(C语言实现)

方法一:逐位判断法,这种方法最容易想到,效率也最差。时间复杂度与数据位宽(n = sizeof(unsigned int) * 8)有关O(n)。代码实现如下:

int fun1(unsigned int data)
{
    int cnt = 0;
    
    while(data) {
        if (data & 1) ++cnt;
        data >>= 1; 
    }

    return cnt;
}

方法二:循环消一法

        以一个unsigned char型十进制整数232为例,它的二进制表示为:11101000,十进制232减去1等于231,十进制整数231的二进制表示为11100111。十进制232减去1转换成二进制减法,计算过程如下:

                            11101000
                        —              1
                        ——————
                            11100111

        我们注意到,一个十进制整数减1的结果,如果用二进制表示的话,就是把该整数最后一位1后面的0全变成1,而最后一位1则变成0。利用上述特点,进一步将整数 N 与整数 (N -1)做“按位与”运算,即,N & (N-1),则能够将整数N的二进制表示中的最后一个1消除掉。因此,把一个整数不断与它减1后的结果做“按位与”,则可以逐一消除该整数最后面的1,最终得到0,统计消除次数就可得到该整数中bit1的个数。时间复杂度与bit1的个数n 有关O(n)。代码实现如下:

int fun2(unsigned int data)
{
    int cnt = 0;

    while (data) {
        data &= (data - 1);
        ++cnt;
    }

    return cnt;
}

方法三:查表法

        一个字节一共有256种状态,将每一个取值所对应的bit1的个数事先写在程序中(注意这些数值是有规律的),然后程序运行的时候,把整数的四个字节拆开逐字节查表并相加,这种可称为“查表法”。时间复杂度与入参数据类型含有的Byte数(n = sizeof(unsigned int))有关O(n)。代码实现如下:

int fun3(unsigned int num) 
{
    int cnt = 0;
    unsigned char* p = (unsigned char*)(&num);
    static const unsigned char table[256] = {
        0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,
        1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
        1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
        2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
        1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
        2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
        2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
        3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
        1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
        2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
        2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
        3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
        2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
        3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
        3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
        4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8
    };

    while(p != (unsigned char*)(&num + 1)) {
        cnt += table[*p++];
    }

    return cnt;
}

方法四:分组统计法

        我们注意到对于单个比特的二进制“数”而言,为0即表示bit1的个数是0,为1即表示bit1的个数是1(注意多比特二进制数值显然没有这个特点,比如一个字节有8个比特,当8个比特全为1时并不代表整数8,而代表255),所以:

(其中  i 表示x的第i个bit)

我们可以将所有bit两两一组分别相加计算,即:

并注意到每组最大值为2,而2个bit最大能表示的数为3。所以,我们可以将每组的和存入其原来的位置而不发生溢出。具体做法为:

  1. 将x的值右移一位后与二进制数0101...01(即0x55555555)按位取&。(x本身值不变)
  2. 将x的值与二进制数0101...01(即0x55555555)按位取&。(x本身值不变)
  3. 将第一步和第二步的结果相加,并赋值回给x。

即:

x = (x &  0x55555555) + ((x >> 1) & 0x55555555);

这样我们就得到了一个整数,其第0~1位的值为0~1位含有1的数量,第2~3位的值为2~3位含有1的数量,……。

以此类推,我们可以在此基础上,将上一步的结果按4 bits一组分组,每组的左边一半和右边一遍相加,并将和写入原来的4bit的位置。再按8bits,16bits,32bits一组重复以上过程,即得到最后的结果。时间复杂度最低O(1)。

代码实现如下:

int fun4(unsigned int data)
{
    data = (data & 0x55555555) + ((data >> 1) & 0x55555555);
    data = (data & 0x33333333) + ((data >> 2) & 0x33333333);
    data = (data & 0x0F0F0F0F) + ((data >> 4) & 0x0F0F0F0F);
    data = (data & 0x00FF00FF) + ((data >> 8) & 0x00FF00FF);
    data = (data & 0x0000FFFF) + ((data >>16) & 0x0000FFFF);

    return data;
}

上述方法效率对比:

        前置条件:将32K Byte内存区域全部初始化成0x5a,即,bit0的个数和bit1的个数相等,且都是0x20000个。

            效率对比


软件实现方法
搜寻范围(单位:Byte)耗时(单位:Us)
分组统计法32K73432
查表法32K96082
循环消一法32K109023
逐位判断法32K495765

分组统计法最快,逐位判断法最慢,速度相差了6.75倍。

参考:二进制中1的个数(分治法) - 知乎

如有侵权请联系本人!

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
目 录 第1章 C语言 8 1.1 什么是局部程序块(local block)? 8 1.2 可以把变量保存在局部程序块吗? 9 1.3 什么时候用一条switch语句比用多条if语句更好? 9 1.4 switch语句必须包含default分支吗? 10 1.5 switch语句的最后一个分支可以不要break语句吗? 11 1.6 除了在for语句之外,在哪些情况下还要使用逗号运算符? 11 1.7 怎样才能知道循环是否提前结束了? 13 1.8 goto,longjmp()和setjmp()之间有什么区别? 13 1.9 什么是左值(lvaule)? 15 1.10 数组(array)可以是左值吗? 15 1.11 什么是右值(rvaule)? 16 1.12 运算符的优先级总能保证是“自左至右”或“自右至左”的顺序吗? 17 1.13 ++var和var++有什么区别? 17 1.14 取模运算符(modulus operator)“%”的作用是什么? 17 第2章 变量和数据存储 18 2.1. 变量存储在内存(memory)的什么地方? 18 2.2. 变量必须初始化吗? 19 2.3. 什么是页抖动(pagethrashing)? 19 2.4. 什么是const指针? 20 2.5. 什么时候应该使用register修饰符?它真的有用吗? 21 2.6. 什么时候应该使用volatile修饰符? 21 2.7. 一个变量可以同时被说明为const和volatile吗? 22 2.8. 什么时候应该使用const修饰符? 23 2.9. 浮点数比较(floating-point comparisons)的可靠性如何? 23 2.10. 怎样判断一个数字型变量可以容纳的最大值? 24 2.11. 对不同类型的变量进行算术运算会有问题吗? 25 2.12. 什么是运算符升级(operatorpromotion)? 25 2.13. 什么时候应该使用类型强制转换(typecast)? 26 2.14. 什么时候不应该使用类型强制转换(typecast)? 27 2.15. 可以在头文件说明或定义变量吗? 27 2.16. 说明一个变量和定义一个变量有什么区别? 27 2.17. 可以在头文件说明static变量吗? 28 2.18. 用const说明常量有什么好处? 28 第3章 排序与查找 28 排序 28 查找 29 排序或查找性能? 30 3.1. 哪一种排序方法最方便? 32 3.2. 哪一种排序方法最快? 33 3.3. 对外存(磁盘或磁带)而不是内存数据进行排序称为外部排序。 39 3.4. 1哪一种查找方法最方便? 44 3.5. 1哪一种查找方法最快? 46 3.6. 1什么是哈希查找? 51 3.7. 1怎样对链表进行排序? 53 3.8. 1怎样查找链表数据? 53 第4章 数据文件 59 4.1. 当errno为一个非零值时,是否有错误发生? 59 4.2. 什么是流(stream)? 59 4.3. 怎样重定向一个标准流? 60 4.4. 怎样恢复一个重定向了的标准流? 60 4.5. stdout能被强制打印到非屏幕设备上吗? 61 4.6. 文本模式(textmode)和二进制模式(binarymode)有什么区别? 61 4.7. 怎样判断是使用流函数还是使用低级函数? 62 4.8. 怎样列出某个目录下的文件? 62 4.9. 怎样列出一个文件的日期和时间? 63 4.10. 怎样对某个目录下的文件名进行排序? 66 4.11. 怎样判断一个文件的属性? 67 4.12. 怎样查看PATH环境变量? 69 4.13. 怎样打开一个同时能被其它程序修改的文件? 69 4.14. 怎样确保只有你的程序能存取一个文件? 71 4.15. 怎样防止其它程序修改你正在修改的那部分文件内容? 71 4.16. 怎样一次打开20个以上的文件? 72 4.17. 怎样避开"Abort,Retry,Fail”消息? 72 4.18. 怎样读写以逗号分界的本? 74 第5章 编译预处理 76 5.1. 什么是宏(macro)?怎样使用宏? 76 5.2. 预处理程序(preprocessor)有什么作用? 77 5.3. 怎样避免多次包含同一个头文件? 79 5.4. 可以用#include指令包含类型名不是".h"的文件吗? 80 5.5. 用#define指令说明常量有什么好处? 80 5.6. 用enum关键字说明常量有什么好处? 81 5.7. 与用#define指令说明常量相比,用enum关键字说明常量有什么好处? 81 5.8. 如何使部分程序在
C语言实现的128位AES加密算法,可以运行在JAVA的JNI AIS加密算法c语言实现代码 nt cnt for(ent =0: cnt< 8: cnt++) BvtcToBit(*(ch+cnt), bit+(ent<<3)) return /将二进制位串转为长度为8的字符串水 int Bit64ToChar8 (ElemType bitL64, ElemType ch18) int cnt memset(ch, 0, 8) for(ent-0: cnt<8: cnt++i BitToByte(bit+(cnt<<3), ch+cnt) return 0 /*生成子密钥 int DES Make Subkeys(ElemType key _64, ElemType subkeys [16][48]) ElemType temp 56 int cnt DES PCI Transform(key,temp):/*PCI置换* for(cnt=0;cnt<16;cnt+-){*16轮跌代,产生16个子密钥米 DES ROL(tenp, MOVE TIMES[cnt]);循坏左移* DES PC2 Transform(temp, subkeys cnt]);/PC2置换,产生子密钥体 return o /*密钥置換1*/ int DES PCI Transform(ElemType key [64, ElemType tempts[56])t int cnt for(cnt=0: cnt( 56 cnt++) )empts[cnt]= key[ Ilant] r巳turn /*密钥置換2* int DES PC2 Transform(Elem Type key [56], ElemType tempts[48])i t cnt for(ent =0: cnt< 48: cnt+)I )pbts [cnt]= key [PC 2[cnt]] return /*循环左移*/ int DES ROL (Elem Type data[56], int time)t Elem l'ype temp _56 /*保存将要循环栘动到右边的位* memcpy(temp, data, time) memcpy(temg-time, data+28, time) /*前28位移动 (data-28-time, temp, time) /*后28位移动* memcpy(data 28, data+28+time, 28-time memcpy (data-56-time, temp+time, time) return o /*P置换*/ int DES IP) Iransform(Elemlype data[64)[ ElemType temp _64]: for(cnt templet- datalIP Tablelcnt」」 memcpy(data, temp, 64) return o 第3页 AIS加密算法c语言实现代码 /*IP逆置換* int DES IP 1 Transform(ElemType data[64)( int cnt ElemType temp _64 for(cnt =0: cnt 64: cnt+-)i templet」- dataLIP1 Tablelcrt]」 memcpy(data, temp, 64) return o /*扩展置换*/ int DES E Transform(ElemType data[48])( Int cn ElemType temp48」 for(ent-0: cnt 48: cnt-) temp lent= datale Tablelent memcpy( data, temp, 48 return o P置换 int DES P Transform(ElemType data[32])( t ElemType temp_32] for(ent =0; cnt 32; cnt+-) temp ent-datalP Tablel 11 me.mcpy(data, temp, 32) return 0 /水异或* int DES XOR(Elem Type R[48, Elem Type L[48], int count)I int cnt for(cnt-0: cnt< count: cnt++)i RIant]= lent] return 0 /*S盒置换*/ int DES SBOX (Elem Type data[48]) int cnt int line, row, output int curl, cur for(ent=0; cnt( 8: cnt++i curl cnt:6 cur2= cnt<<2 /*计算在S盒的行与列来 line=(data cur1<<1)+ data[ cur1+5 row=(data[cur1+1]<<3)+(data[cur1+2]<<2) +(data「cur1-31<<1)+data「cur1+41 output s[cnt][line]trow] /*化为2进制*/ data[cur2]=(output&0X08)>>3 data[cur2+1]=(output&0X04)>>2 data (output&0X02)>1 datalcur 2+3= output&0x01 return o 交换 int DES Swap(ElemType left[32], ElemType right [32]) memcpy(temp, left, 32 memcpy(left, right, 32 memcpy (right, temp, 32 return o 第4页 AIS加密算法c语言实现代码 /*加密单个分组 int DES EncryptBlockElem Type plainBlock[8, ElemType subKeys[l6][48, ElemType cipherBlock[8])I ElemT'ype plainTs [54] ElemType copyRight[48] int cnt Char8ToBit64(plainBlock, plairBits) /米初始置换(IP置换)* DES IP Transform(plainBits /*16轮迭代* for(cnt=0: cnt< 16: cnt+-) memcpy(copyRight, plainBits- 32, 32 /*将右半部分进行扩展置换,从32位扩展到48位*/ DES E Trans form(copyRight) /*将右半部分与子密钥进行异或操作 DES XOR (copy Righ 48) /*异或结果进入S盒,输出32位结果*/ DES SBOX (copyRight) /P置换 DES P Transform(copyRight) /*将明文左半部分与右半部分进行异或* DES XOR (plainBits, copyRight, 32) 最终完成左右部的交换* DES Swap(plainBits, plainBits-32) /逆初始置换(IPI置换)* DES IP 1 Transform (plainBits) Bit64ToChar8(plainBits, cipherBlock) turn o /*解密单个分组 int DES DecryptBlock(ElemType cipherBlock[8, ElemType subKeys[16] 18], ElemType plainBlock [81) ElemType cipherBits[ 641 Elem Type copy Right [48] int cnt Char8ToBit64(cipherBlock, cipherBits) /初始置换(IP置换)* DES IP Transform(cipherBits /*16轮迭代*/ for(cnt-15: cnt >-0: cnt--)i memcpy(copyRight, cipherBits+32, 32 /*将右半部分进行扩展置换,从32位扩展到48位 DES T Trans form(copyright) /*将右半部分与子密钥进行异或操作 DES XOR(copy Right, subKeys [ent], 48) /*异或结果进入S盒,输出32位结果* DES SBOX(copyRight) /米P置换* DES P Transform(copyright) /*将明文h半部分与右半部分进行异或* DES XOR (cipherBits, copy Right, 32) f(cnt /米最终完成左右部的交换* DES Swap(cipherBits, cipherBits+32) /*逆初始置换(IP1置换)* DES IP 1 Transform(cipherBits) Bit64ToChar8(cipherBits, plainBlock) return 0: *加密文件 int DES Encrypt (char xplainFile, char *keyStr, char *cipherFile)t FILE xplain, i*cipher; int count ElcmType plainBlock[81, ciphcrBlock [8, keyBlock 8 第5页 AIS加密算法c语言实现代码 Elem Type bEy 64] ElemType subKeys[16][18] if((plain- fopen(plainFilc, " rb"))--NULL) return Plain FILe OPEN ERROR return CIPHER FILE OPEN ERROR: ))== NULL)( if ((cipher fopen(cipherFile, wb /设置密钥 memcpy (keyBlock, key Str, 8) 将密钥转换为二进制流* Char8ToBit64(keyBlock, bKcy /牛成子密钥* DES Make SubKeys(bEy, subKeys while(!feof plain))( /*每次读8个字节,并返回成功读取的字节数* if((count- fread(plainBlock, sizeof(char),8, plain)) 8){ DES EncryptBlock (plainBlock, subKeys, cipherBlock f(count)[ /*填充*/ memset(plainBlock ount, \0, 7- count) /*最后一个字符休存包括最后一个字符在内的所填充的字符数量水 plainblockl7-8-count DES EncryptBlock (plainBlock, subkeys, cipherBlock fwrite(cipherBlock, sizeof (char), 8, cipher) fclose (plain) f'c. lose(cipher return oK /*解密文件* int DES Decrypt(char *cipherFile, char *key Str, char xplainFile)i FILE* plain,米 cipher int count, times 0 long fileLen Eleml'ype plainBlock [8], cipherBlock[8], keyBlock[8 ElemType bEy _6 ElemType subKeys[16][48] if ((cipher fopen(cipherFile, rb ))= NULL)[ return CIPHEr FILe OPEN ERROR if((plain= fopen(plainFile, wb" ))= NULL) rcturn plain FIle OPEN ERROR /*设置密钥* memcpy(key Block, keyStr, 8) /将密钥转换为二进制流* Char8ToBit64 (keyBlock, bKey) /水生成子密钥* ES Make SubKeys(bKey subKeys) /取文什长度*/ fseek( cipher,0, SEEK END);/将文件指针置尾 fi lelen= ftel l( cipher);/*取文件指针当前位置*/ rewind( CIpher);/*将文件指针重指向文件头* while(1)i 密文的字节数一定是8的整数倍* fread(cipherBlock, sizeof(char), 8, cipher DES DecryptBlock(cipherBlock, subKeys, plainBlock) times +-8 if(times< fileLen fwrite(plainBlock, sizeof(char), 8, plain) /*判断末尾是否被填充米 if(plainBlock 71< 8)i 第6页 AIS加密算法c语言实现代码 for(count=8- plainBlock[7]; count< 7; count++)( if(plainBlock[ count!='\0i break if( count==7){/*有填充* fwrite(plairBlock, sizeof (char), 8- plainBlockL7, plain) else{/*无填充 fwrite(plainBlock, sizeof(char), 8, plain) t'close ( plain) fclose(cipher) return OK int main() clock t a, b a= clockO DES Encrypt( 1. txt, key. txt, 2. txt b=clock o printf("加密消耗%毫秒Ⅶn",b-a); system("pause") a= clock( DES Decrypt( 2. txt, key. txt", 3. txt") printf("解密消耗%毫秒、n",o-a) getcharo return 第7页

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值