位操作的趣味应用

位操作有很有趣的应用,下面列举出一些,欢迎读者补充。

1.  高低位交换

给出一个16位的无符号整数。称这个二进制数的前8位为“高位”,后8位为“低位”。现在写一程序将它的高低位交换。例如,数34520用二进制表示为:

10000110 11011000

将它的高低位进行交换,我们得到了一个新的二进制数:

11011000 10000110

它即是十进制的55430。

这个问题用位操作解决起来非常方便,设x=34520=10000110 11011000(二进制) 由于x为无符号数,右移时会执行逻辑右移即高位补0,因此x右移8位将得到0000000010000110。而x左移8位将得到11011000 00000000。可以发现只要将x>>8与x<<8这两个数相或就可以得到11011000 10000110。用代码实现非常简洁:

//高低位交换 by MoreWindows( http://blog.csdn.net/MoreWindows )
#include <stdio.h>
template <class T>
void PrintfBinary(T a)
{
    int i;
    for (i = sizeof(a) * 8 - 1; i >= 0; --i)
    {
        if ((a >> i) & 1)
            putchar(‘1‘);
        else
            putchar(‘0‘);
        if (i == 8)
            putchar(‘ ‘);
    }
    putchar(‘\n‘);
}
int main()
{
    printf("高低位交换 --- by MoreWindows( http://blog.csdn.net/MoreWindows )  ---\n\n");

    printf("交换前:    ");
    unsigned short a = 3344520;
    PrintfBinary(a);

    printf("交换后:    ");
    a = (a >> 8) | (a << 8);
    PrintfBinary(a);
    return 0;
}

运行结果如下:

 

2.  二进制逆序

我们知道如何对字符串求逆序,现在要求计算二进制的逆序,如数34520用二进制表示为:

10000110 11011000

将它逆序,我们得到了一个新的二进制数:

00011011 01100001

它即是十进制的7009。

    回顾下字符串的逆序,可以从字符串的首尾开始,依次交换两端的数据。在二进制逆序我们也可以用这种方法,但运用位操作的高低位交换来处理二进制逆序将会得到更简洁的方法。类似于归并排序的分组处理,可以通过下面4步得到16位数据的二进制逆序:

第一步:每2位为一组,组内高低位交换

10 00 01 10  11 01 10 00

  -->01 00 10 01 11 10 01 00

第二步:每4位为一组,组内高低位交换

0100 1001 1110 0100

  -->0001 0110 1011 0001

第三步:每8位为一组,组内高低位交换

00010110 10110001

  -->01100001 00011011

第四步:每16位为一组,组内高低位交换

01100001 00011011

  -->00011011 01100001

对第一步,可以依次取出每2位作一组,再组内高低位交换,这样有点麻烦,下面介绍一种非常有技巧的方法。先分别取10000110 11011000的奇数位和偶数位,空位以下划线表示。

原 数    10000110 11011000

奇数位 1_0_0_1_ 1_0_1_0_

偶数位  _0_0_1_0 _1_1_0_0

将下划线用0填充,可得

原 数    10000110 11011000

奇数位 10000010 10001000

偶数位 00000100 01010000

再将奇数位右移一位,偶数位左移一位,此时将这两个数据相或即可以达到奇偶位上数据交换的效果了。

原 数           10000110 11011000

奇数位右移 01000001 01000100

偶数位左移 00001000 10100000

      相或得到      01001001 11100100

可以看出,结果完全达到了奇偶位的数据交换,再来考虑代码的实现——

取x的奇数位并将偶数位用0填充用代码实现就是x & 0xAAAA

取x的偶数位并将奇数位用0填充用代码实现就是x & 0x5555

因此,第一步就用代码实现就是:

       x = ((x & 0xAAAA) >> 1) | ((x & 0x5555) << 1);

类似可以得到后三步的代码。完整程序如下:

//二进制逆序 by MoreWindows( http://blog.csdn.net/MoreWindows )
#include <stdio.h>
template <class T>
void PrintfBinary(T a)
{
    int i;
    for (i = sizeof(a) * 8 - 1; i >= 0; --i)
    {
        if ((a >> i) & 1)
            putchar(‘1‘);
        else
            putchar(‘0‘);
        if (i == 8)
            putchar(‘ ‘);
    }
    putchar(‘\n‘);
}
int main()
{
    printf("二进制逆序 --- by MoreWindows( http://blog.csdn.net/MoreWindows )  ---\n\n");

    printf("逆序前:    ");
    unsigned short a = 34520;
    PrintfBinary(a);

    printf("逆序后:    ");
    a = ((a & 0xAAAA) >> 1) | ((a & 0x5555) << 1);
    a = ((a & 0xCCCC) >> 2) | ((a & 0x3333) << 2);
    a = ((a & 0xF0F0) >> 4) | ((a & 0x0F0F) << 4);
    a = ((a & 0xFF00) >> 8) | ((a & 0x00FF) << 8);
    PrintfBinary(a);
}

运行结果如下:

 

3.  二进制中1的个数

统计二进制中1的个数可以直接移位再判断,当然像《编程之美》书中用循环移位计数或先打一个表再计算都可以。本文详细讲解一种高效的方法。以34520为例,可以通过下面四步来计算其二进制中1的个数二进制中1的个数。

第一步:每2位为一组,组内高低位相加

      10 00 01 10  11 01 10 00

  -->01 00 01 01  10 01 01 00

第二步:每4位为一组,组内高低位相加

      0100 0101 1001 0100

  -->0001 0010 0011 0001

第三步:每8位为一组,组内高低位相加

00010010 00110001

  -->00000011 00000100

第四步:每16位为一组,组内高低位相加

00000011 00000100

  -->00000000 00000111

这样最后得到的00000000 00000111即7即34520二进制中1的个数。类似上文中对二进制逆序的做法不难实现第一步的代码:

       x = ((x & 0xAAAA) >> 1) + (x & 0x5555);

好的,有了第一步,后面几步就请读者完成下吧,先动动笔再看下面的完整代码:

//二进制中1的个数  by MoreWindows( http://blog.csdn.net/MoreWindows )
#include <stdio.h>
template <class T>
void PrintfBinary(T a)
{
    int i;
    for (i = sizeof(a) * 8 - 1; i >= 0; --i)
    {
        if ((a >> i) & 1)
            putchar(‘1‘);
        else
            putchar(‘0‘);
        if (i == 8)
            putchar(‘ ‘);
    }
    putchar(‘\n‘);
}
int main()
{
    printf("二进制中1的个数 --- by MoreWindows( http://blog.csdn.net/MoreWindows )  ---\n\n");

    unsigned short a = 34520;
    printf("原数    %6d的二进制为:  ", a);
    PrintfBinary(a);

    a = ((a & 0xAAAA) >> 1) + (a & 0x5555);
    a = ((a & 0xCCCC) >> 2) + (a & 0x3333);
    a = ((a & 0xF0F0) >> 4) + (a & 0x0F0F);
    a = ((a & 0xFF00) >> 8) + (a & 0x00FF);
    printf("计算结果%6d的二进制为:  ", a);
    PrintfBinary(a);
    return 0;
}

4.  缺失的数字

很多成对出现数字保存在磁盘文件中,注意成对的数字不一定是相邻的,如2, 3, 4, 3, 4, 2……,由于意外有一个数字消失了,如何尽快的找到是哪个数字消失了?

由于有一个数字消失了,那必定有一个数只出现一次而且其它数字都出现了偶数次。用搜索来做就没必要了,利用异或运算的两个特性——1.自己与自己异或结果为0,2.异或满足交换律。因此我们将这些数字全异或一遍,结果就一定是那个仅出现一个的那个数。 示例代码如下:

//缺失的数字  by MoreWindows( http://blog.csdn.net/MoreWindows )
#include <stdio.h>
int main()
{
    printf("缺失的数字 --- by MoreWindows( http://blog.csdn.net/MoreWindows )  ---\n\n");

    const int MAXN = 15;
    int a[MAXN] = {1, 347, 6, 9, 13, 65, 889, 712, 889, 347, 1, 9, 65, 13, 712};
    int lostNum = 0;
    for (int i = 0; i < MAXN; i++)
        lostNum ^= a[i];
    printf("缺失的数字为:  %d\n", lostNum);
    return 0;
}

位操作是一种高效优美的方法,同时由于其高效的运算性能和掌握难度较大,位操作运算一直是笔试面试时的热门话题之一。本文详细总结了位操作的方法与技巧并列出4种位操作趣味应用,如果读者能亲自上机实现代码,相信必能更好应对笔试和面试时可能遇到的位操作问题。

另外,欢迎各位能提供笔试面试中的位操作相关的题目给我,我将会在提高篇中加入这些。谢谢大家。

时间: 2024-10-06 03:34:09

位操作的趣味应用的相关文章

C++:位操作基础篇之位操作全面总结

位操作篇共分为基础篇和提高篇,基础篇主要对位操作进行全面总结,帮助大家梳理知识.提高篇则针对各大IT公司如微软.腾讯.百度.360等公司的笔试面试题作详细的解答,使大家能熟练应对在笔试面试中位操作题目. 下面就先来对位操作作个全面总结,欢迎大家补充. 在计算机中所有数据都是以二进制的形式储存的.位运算其实就是直接对在内存中的二进制数据进行操作,因此处理数据的速度非常快. 在实际编程中,如果能巧妙运用位操作,完全可以达到四两拨千斤的效果,正因为位操作的这些优点,所以位操作在各大IT公司的笔试面试中

位操作基础篇之位操作全面总结

文章参考地址:http://blog.csdn.net/morewindows/article/details/7354571# 位操作篇共分为基础篇和提高篇,基础篇主要对位操作进行全面总结,帮助大家梳理知识.提高篇则针对各大IT公司如微软.腾讯.百度.360等公司的笔试面试题作详细的解答,使大家能熟练应对在笔试面试中位操作题目. 下面就先来对位操作作个全面总结,欢迎大家补充. 在计算机中所有数据都是以二进制的形式储存的.位运算其实就是直接对在内存中的二进制数据进行操作,因此处理数据的速度非常快

两个常见位操作面试题 不用加减乘除运算符计算两数之和及a b 3

分享一下我老师大神的人工智能教程吧.零基础!通俗易懂!风趣幽默!还带黄段子!希望你也加入到我们人工智能的队伍中来!http://www.captainbed.net <两个常见位操作面试题不用加减乘除运算符计算两数之和及a=b*3> 地址:http://blog.csdn.net/morewindows/article/details/8710737转载请标明出处,谢谢. 欢迎关注微博:http://weibo.com/MoreWindows 上一篇<位操作基础篇之位操作全面总结>

日志文件目录

一.Visio分类 以编程方式使用 Microsoft Office Visio 2003 ActiveX 控件 visio应用程序相关设置-选项-视图 visio应用程序相关设置-选项-常规 visio中相关设置-菜单视图 Visio中设置自定义属性的值 Visio中的Undo和Redo Visio中ShapeAdded和SelectionAdded 二.位分类 补码.原码.反码 不同进制之间的转换 位操作基础 常用位操作小技巧 位操作的趣味应用 位操作与空间压缩 位操作基础篇之位操作全面总结

位运算大集合

一.位运算常用的小技巧:判断奇偶.交换两数.变换符号及求绝对值等 1.判断奇偶 只要根据最未位是0还是1来决定,为0就是偶数,为1就是奇数.因此可以用if ((a & 1) == 0)代替if (a % 2 == 0)来判断a是不是偶数. 下面程序将输出0到100之间的所有奇数. 1 for (i = 0; i < 100; ++i) 2 if (i & 1) 3 printf("%d ", i); 4 putchar('\n'); 2.交换两数 一般的写法是:

c语言的位操作

c语言的位操作最常用的地方就是用在对寄存器的写值上. 一.基本的一些概念 1.位与:& 操作:1 & 1 = 1: 1 & 0 = 0: 0 & 0 = 0: 特点:只有全是1的时候才是1,其他情况都是0. 总结:任何数和0位与就是0,和1位与没有变化,所以位与常用在清零上(清零用位与). 2.位或:| 操作:1 | 1 = 1: 1 | 0 = 1: 0 | 0 = 0: 特点:只有全0的时候才是0,其他情况都是1. 总结:任何数和1位或就是1,和0位或没有变化,所以位或

位操作的技巧

一,基本概念认知 1,为啥要用补码 计算机中的符号数有三种表示方法,即原码.反码和补码.三种表示方法均有符号位和数值位两部分,符号位都是用0表示"正",用1表示"负",而数值位,三种表示方法各不相同. 在计算机系统中,数值一律用补码来表示和存储.原因在于,使用补码,可以将符号位和数值域统一处理:同时,加法和减法也可以统一处理.此外,补码与原码相互转换,其运算过程是相同的,不需要额外的硬件电路.(来自百度百科) 2,补码的求法: 正数的补码和原码相同: 负数是对应正数

C/C++位操作

异或:不同为1,相同为0.可以记为:"异"为不同,不同为1. C方式位操作总结: 设置特定的位用|; 清除特定的位用&; 取反特定的位用; 取反所有的位用~; 引用自

在32位、64位操作系统下各数据类型所占的字节数

点击打开链接 在32位.64位系统当中,唯一改变的是指针的长度;在32位系统当中是4个字节.64位则是8个字节.所谓的32位.64位,这个指的是寄存器的位宽. 32位平台下结果: 64位平台下结果: 一.下面几点是值得大家注意的: 1.关于 int 的取值范围,缺省的int数值范围是由编译器设计者决定的,通常都是机器最为自然高效的位数.甚至,我们在32位的机器上,前提没有什么指令可以高效的的处理更短的整型值,我们可以将short.int.long都设置成32位. 2.浮点数在缺省的情况下 表示的