这里实现了交换一个数字的奇数位与偶数位,其交换规律是二进制序列中的第0位与第1位交换,第2位与第3位交换,第4位与第5位交换,……
1、算法思想
a.得到该数字的奇数位;
b.得到该数字的偶数位;
c.所有的奇数位右移一位变成偶数位;
d.所有的偶数位左移一位变成奇数位;
e.新的偶数位 按位或 新的奇数位 即可得到交换奇偶顺序后的数字。
不妨假设最右边的位为第0位,也就是一个偶数位。那么如何得到一个数字的奇数位呢?
int num = 7; // 0 1 1 1 // 奇数位 0 1 只想要得到0 1,就要保证偶数位上的数字为0(清0), // 奇数位上的数字不发生变化(保留原值),这里考虑按位与, // 1.奇数位上的数字按位与1则不会发生变化, // 2.偶数位上的数字按位与0实现清0, // 于是可以写出这个按位与的数字 0XAAAAAAAA, // 其二进制序列为:1010 1010 1010 1010 1010 1010 1010 1010 // A A A A A A A A // // 1010 1010 1010 1010 1010 1010 1010 1010 // & 0000 0000 0000 0000 0000 0000 0000 0111 // 0000 0000 0000 0000 0000 0000 0000 0010
如何得到一个数字的偶数位呢?
// 0 1 1 1 // 偶数位 1 1 只想要得到1 1,就要保证奇数位上的数字为0(清0), // 偶数位上的数字不发生变化(保留原值),这里考虑按位与, // 1.偶数位上的数字按位与1则不会发生变化, // 2.奇数位上的数字按位与0实现清0, // 于是可以写出这个按位与的数字 0X55555555, // 其二进制序列为:0101 0101 0101 0101 0101 0101 0101 0101 // 5 5 5 5 5 5 5 5 // // 0101 0101 0101 0101 0101 0101 0101 0101 // & 0000 0000 0000 0000 0000 0000 0000 0111 // 0000 0000 0000 0000 0000 0000 0000 0101
所有的奇数位右移一位变成偶数位:
// 奇数位 0000 0000 0000 0000 0000 0000 0000 0010 // >> 1 0000 0000 0000 0000 0000 0000 0000 0001
所有的偶数位左移一位变成奇数位:
扫描二维码关注公众号,回复:
122160 查看本文章
// 偶数位 0000 0000 0000 0000 0000 0000 0000 0101 // << 1 0000 0000 0000 0000 0000 0000 0000 1010
新的偶数位 按位或 新的奇数位:
// 新的偶数位 0000 0000 0000 0000 0000 0000 0000 0001 // 新的奇数位 |或 0000 0000 0000 0000 0000 0000 0000 1010 // 交换后的数字 0000 0000 0000 0000 0000 0000 0000 1011 // 11
从本质上来说,按位或运算可以理解为 相同位(同为0或者同为1)的和的一半 加上 不同位(一个为1,一个为0)的和。接下来通过下面的示例来剖析下按位或运算:
int num1 = 7; // 0 1 1 1 int num2 = 3; // + 0 0 1 1 // 1 0 1 0 //相同位 0 1 1 //相同位+ 0 1 1 // 0 1 1 0 // 0 4 2 0 //相同位的一半也就是(4+2)/2=3 //相同位 0 1 1 //相同位& 0 1 1 // 0 0 1 1 // 0 0 2 1 2+1=3 //相同位 0 1 1 //相同位| 0 1 1 // 0 0 1 1 // 0 0 2 1 2+1=3 //不同位 1 // + 0 // 1 2*2=4 //不同位 1 // | 0 // 1 2*2=4
加法运算可以这样理解:
//num1+num2的运算可以理解为 相同位的和(6) 加上 不同位的和(4),得到的结果也是10, //其本质是 相同位的按位与的和(ps:由于不同位(1或者0)时按位与是0,所以这里用相同位的按位与) //加上 所有位的按位或的和。
按位或运算可以这样理解:
int num1 = 7; // 0 1 1 1 int num2 = 3; // | 0 0 1 1 // 0 1 1 1 按位或得到的值为7 //相同位 0 1 1 //相同位& 0 1 1 // 0 0 1 1 // 0 0 2 1 相同位与得到2+1=3 //不同位 1 //不同位& 0 // 0 0 0 0 不同位与得到0 //相同位 0 1 1 //相同位| 0 1 1 // 0 0 1 1 // 0 0 2 1 相同位或得到2+1=3 //不同位 1 // | 0 // 1 不同位或得到2*2=4 //num1 | num2的运算就相当于是 相同位的和的一半 加上 不同位的和。
这里定义一个宏来实现交换奇数位与偶数位的算法:
# define SWAP_ODD_EVEN(NUM) ((((NUM) & (0X55555555)) << 1) | (((NUM) & (0XAAAAAAAA)) >> 1))
2、源代码
a.输出二进制序列
对num模2除2即可。
/* * 函数名称:OutputBinary * * 函数功能:输出一个数字的二进制位 * 从右往左读,表示从高位到低位, * 不足32位的,右边补0。 * * 入口参数:num * * 出口参数:void * * 返回类型:void */ void OutputBinary(unsigned int num) { unsigned int one_bit_num = num; if (0 == num) { printf("%d", one_bit_num); } while (num) { one_bit_num = num % 2; printf("%d ", one_bit_num); num >>= 1; } printf("\n"); return; }
不足:首先,每次需要补齐二进制序列至32位,其次,数字必须为无符号整型,并且需要对0进行判断,最后从右往左看每一位的数字,很不人性化。
改进:遍历变量one_bit_num从31到0,然后移位one_bit_num再与1。
/* * 函数名称:OutputBinary * * 函数功能:输出一个数字的二进制位 * * 入口参数:num * * 出口参数:void * * 返回类型:void */ void OutputBinary(int num) { int one_bit_num = 0; for (one_bit_num=31; one_bit_num>=0; one_bit_num--) { printf("%d ", ((num >> one_bit_num) & 1)); } printf("\n"); return; }
b.主函数
#define _CRT_SECURE_NO_WARNINGS 1 /* * Copyright (c) 2018, code farmer from sust * All rights reserved. * * 文件名称:SwapOddEven.c * 功能:写一个宏可以将一个数字的奇数位和偶数位交换。 * * 当前版本:V1.0 * 作者:sustzc * 完成日期:2018年4月11日21:48:09 */ # include <stdio.h> # include <assert.h> # define SWAP_ODD_EVEN(NUM) ((((NUM) & (0X55555555)) << 1) | (((NUM) & (0XAAAAAAAA)) >> 1)) int main(void) { unsigned int num = 0; unsigned int ret = 0; printf("input one num:"); assert(1 == scanf("%d", &num)); printf("swap before:"); OutputBinary(num); ret = SWAP_ODD_EVEN(num); printf("swap after: "); OutputBinary(ret); printf("now,num is %d\n", ret); return 0; }
3、输出结果