首页 > 其他 > 详细

位运算骚操作 Part 3

时间:2019-03-05 15:08:01      阅读:221      评论:0      收藏:0      [点我收藏+]

? 异或运算 "^" 具有的部分性质:

● 交换律,结合律

● a ^ b == (!a & b) | (a & !b),a ^ 1 == !a,a ^ 0 == a,a ^ a == 0,a ^ !a == 1,

● RAID 5 的理解:写入时,数据 A 写入硬盘 0, 数据 B 写入硬盘 1,数据 A^B 写入硬盘 2;读取时,数据 A 可用硬盘 1 和硬盘 2 的数据 B^C 进行验校或恢复

 

? 使用异或运算找非重复元素的方法,参考【https://blog.csdn.net/u013609078/article/details/51622077】,但是原文写的像屎一样,很多说法连良定义都没有

●(1)整型数组中有 1 个元素只出现一次,其他数字都出现两次,要求找出这个数字

  ■ 把所有元素进行规约异或即可

●(2)整型数组中有 2 个元素只出现一次,其他数字都出现两次,要求找出这个数字

  ■ 核心思想是利用两元素的差异(二进制表示中从低位到高位首个不同的位)作为条件来构造子列,把所有与 b 具有相同条件的元素去掉,在该子列中找 a 就等价于问题(1)

  ■ 找出 a 后 b = (a ^ b) ^ a

  ■ 代码

 1 #include <iostream>
 2 #include <vector>
 3 #include <cassert>
 4 
 5 using namespace std;
 6 
 7 inline int getLastOneBit(int num)           // 求 num 最低 "1" 位,返回值有且仅有一位为 1,特例输入 0 返回 0
 8 {
 9     return num & ~(num - 1);
10 }
11 
12 vector<int> findTwo(vector<int> & haha)
13 {
14     int ab = 0;
15     for (int i = 0; i < haha.size(); i++)   // 第一轮得到 a ^ b,用 ab 来表示
16         ab ^= haha[i];
17     assert(ab != 0);                        // 保证 a !=b
18     int lastOneBit = getLastOneBit(ab);     // 取 ab 最低 "1" 位(定义为从低位到高位,首个至少有一个数为 1 的位),即 a 和 b 二进制表示中从低位到高位首个不同的位,如 ab = 01100100,则 lastOneBit = 00000100,可能的情况 a = ...100,b = ...000
19 
20     int a = 0;
21     for (int i = 0; i < haha.size(); i++)   // 不妨设 a 在 lastOneBit 位上为 1,第二轮取所有该位为 1 的元素作为子列进行异或运算,重复元素不影响结果
22     {
23         if (haha[i] & lastOneBit)
24             a ^= haha[i];
25     }
26     int b = ab ^ a;
27     cout << "findTwo -> a: " << a << ", b: " << b << endl;
28     return vector<int>({ a, b });
29 }
30 
31 int main()
32 {
33     vector<int> haha = { 2, 3, 5, 2, 3, 5, 7, 11 };
34     vector<int> result = findTwo(haha);
35 
36     cout << "result -> a: " << result[0] << ", b: " << result[1] << endl;
37     return 0;
38 }

  ■ 输出结果

findTwo -> a: 7, b: 11
result -> a: 7, b: 11

 

●(3)整型数组中有 3 个元素只出现一次,其他数字都出现两次,要求找出这个数字

  ■ 引理:若x ^ y ^ z == 0,则 x,y,z 最低 "1" 位有且仅有两个数为 1,如 x == 00001000,y == 00000100,z == 00001100 满足该式

    证明:3 个数的最低 "1" 位四种情况:

      ① 全相同,该位 1 ^ 1 ^ 1 == 1;

      ② 全不同,该位 1 ^ 0 ^ 0 == 1;

      ③ 两个 0 一个 1,该位 1 ^ 0 ^ 0 == 1;

      ④ 两个 1 一个 0,该位 1 ^ 1 ^ 0 == 0,仅这种情况满足上式

  ■ 注意到恒等式 (a ^ b) ^ (a ^ c) ^ (b ^ c) == ((a^b^c) ^ c) ^ ((a^b^c) ^ b) ^ ((a^b^c) ^ a) == 0,所以 (a ^ b),(a ^ c),(b ^ c) 三个数的的最低 "1" 位有且仅有两个 1,

    不妨设 (a ^ b) 和 (a ^ c) 该位为 1,(b ^ c) 的该位为 0,则 (a ^ c) 与 (b ^ c) 的最低 "1" 位相同,且与 (a ^ b) 的最低 "1" 位不同

    于是 getLastOneBit(a ^ c) ^ getLastOneBit(b ^ c) ^ getLastOneBit(a ^ b) == getLastOneBit(a ^ b),(函数 getLastOneBit 的定义见问题(2)的代码)

    类似问题(2),我们构造原数组的一个子列,考虑原数组中满足 getLastOneBit((a^b^c) ^ x) == getLastOneBit(a ^ b) 的那些元素 x,

      ① 若 x 是重复元素,则 x 会在子列中出现零次或两次,不影响异或的结果;

      ② 若 x == a || x == b,则上述等式不成立,x 不在子列中;

      ③ 若 x == c,则上述等式成立,x 在子列中

    所以在该子列中找 c 就等价于问题(1),找到 c 后可以把 c 添加到原数组末尾,在该新数组中找 a 和 b 就等价于问题(2)   

  ■ 代码

 1 #include <iostream>
 2 #include <vector>
 3 #include <cassert>
 4 
 5 using namespace std;
 6 
 7 inline int getLastOneBit(int num)
 8 {
 9     return num & ~(num - 1);
10 }
11 
12 vector<int> findTwo(vector<int> & haha)
13 {
14     int ab = 0;
15     for (int i = 0; i < haha.size(); i++)
16         ab ^= haha[i];
17     assert(ab != 0);
18     int lastOneBit = getLastOneBit(ab);
19 
20     int a = 0;
21     for (int i = 0; i < haha.size(); ++i)
22     {
23         if (haha[i] & lastOneBit)
24             a ^= haha[i];
25     }
26     int b = ab ^ a;
27     cout << "findTwo -> a: " << a << ", b: " << b << endl;
28     return vector<int>({ a, b });
29 }
30 
31 int findOne(vector<int> & haha)
32 {
33     int abc = 0;
34     for (int i = 0; i < haha.size(); i++)               // 第一轮得到 a ^ b ^ c,用 abc 表示
35         abc ^= haha[i];
36 
37     int lastOneBit = 0;
38     for (int i = 0; i < haha.size(); i++)               // 第二轮得到 getLastOneBit(a ^ b) ^ getLastOneBit(a ^ c) ^ getLastOneBit(b ^ c),成对元素不影响结果
39         lastOneBit ^= getLastOneBit(abc ^ haha[i]);
40     //lastOneBit = getLastOneBit(lastOneBit);           // 原博客中有这句,但根据我的论证该句不需要
41 
42     int c = 0;                                          
43     for (int i = 0; i < haha.size(); i++)               // 第三轮选出所有最低 "1" 位与 lastOneBit 相同的元素(a 和 b 肯定不在里边),用异或找出 c
44     {
45         if (getLastOneBit(abc ^ haha[i]) == lastOneBit)
46             c ^= haha[i];
47     }
48     cout << "findOne -> c: " << c << endl;
49     return c;
50 }
51 int main()
52 {
53     vector<int> haha = { 2, 3, 5, 2, 3, 5, 7, 11, 13 };
54     int c = findOne(haha);                              // 先找出一个非重复的元素
55     haha.push_back(c);                                  // 将找到的元素添加到原数组末尾,相当于去掉了这个非重复元素
56     vector<int> result = findTwo(haha);                 // 寻找剩余两个非重复元素
57     result.push_back(c);
58 
59     cout << "result -> a: " << result[0] << ", b: " << result[1] << ", c: " << result[2] << endl;
60     return 0;
61 }

  ■ 输出结果

findOne -> c: 13
findTwo -> a: 7, b: 11
result -> a: 7, b: 11, c: 13

 

位运算骚操作 Part 3

原文:https://www.cnblogs.com/cuancuancuanhao/p/10476810.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!