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

● 交换律,结合律

● 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

  ■ 代码

 #include <iostream>
#include <vector>
#include <cassert> using namespace std; inline int getLastOneBit(int num) // 求 num 最低 "1" 位,返回值有且仅有一位为 1,特例输入 0 返回 0
{
return num & ~(num - );
} vector<int> findTwo(vector<int> & haha)
{
int ab = ;
for (int i = ; i < haha.size(); i++) // 第一轮得到 a ^ b,用 ab 来表示
ab ^= haha[i];
assert(ab != ); // 保证 a != b
int lastOneBit = getLastOneBit(ab); // 取 ab 的最低 "1" 位(定义为从低位到高位,首个至少有一个值为 1 的位),即 a 和 b 二进制表示中从低位到高位首个不同的位
// 如 ab = 01100100,则 lastOneBit = 00000100,可能的情况 a = ...100,b = ...000
int a = ;
for (int i = ; i < haha.size(); i++) // 不妨设 a 在 lastOneBit 位上为 1,第二轮取所有该位为 1 的元素作为子列进行异或运算,重复元素不影响结果
{
if (haha[i] & lastOneBit)
a ^= haha[i];
}
int b = ab ^ a;
cout << "findTwo -> a: " << a << ", b: " << b << endl;
return vector<int>({ a, b });
} int main()
{
vector<int> haha = { , , , , , , , };
vector<int> result = findTwo(haha); cout << "result -> a: " << result[] << ", b: " << result[] << endl;
return ;
}

  ■ 输出结果

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

●(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 ^ c) 和 (b ^ c) 该位为 1,(a ^ b) 的该位为 0,则 (a ^ c) 与 (b ^ c) 的最低 "1" 位相同,且 (a ^ b) 的最低 "1" 位不同,

   于是 getLastOneBit(a ^ c) ^ getLastOneBit(b ^ c) ^ getLastOneBit(a ^ b) == getLastOneBit(a ^ b),干掉了最低 "1" 位相同的那两个,

   类似问题(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)

  ■ 代码

 #include <iostream>
#include <vector>
#include <cassert> using namespace std; inline int getLastOneBit(int num)
{
return num & ~(num - );
} vector<int> findTwo(vector<int> & haha)
{
int ab = ;
for (int i = ; i < haha.size(); i++)
ab ^= haha[i];
assert(ab != );
int lastOneBit = getLastOneBit(ab); int a = ;
for (int i = ; i < haha.size(); ++i)
{
if (haha[i] & lastOneBit)
a ^= haha[i];
}
int b = ab ^ a;
cout << "findTwo -> a: " << a << ", b: " << b << endl;
return vector<int>({ a, b });
} int findOne(vector<int> & haha)
{
int abc = ;
for (int i = ; i < haha.size(); i++) // 第一轮得到 a ^ b ^ c,用 abc 表示
abc ^= haha[i]; int lastOneBit = ;
for (int i = ; i < haha.size(); i++) // 第二轮得到 getLastOneBit(a ^ b) ^ getLastOneBit(a ^ c) ^ getLastOneBit(b ^ c),成对元素不影响结果
lastOneBit ^= getLastOneBit(abc ^ haha[i]);
//lastOneBit = getLastOneBit(lastOneBit); // 原博客中有这句,实际不需要 int c = ;
for (int i = ; i < haha.size(); i++) // 第三轮选出所有最低 "1" 位与 lastOneBit 相同的元素(a 和 b 肯定不在里边),用异或找出 c
{
if (getLastOneBit(abc ^ haha[i]) == lastOneBit)
c ^= haha[i];
}
cout << "findOne -> c: " << c << endl;
return c;
}
int main()
{
vector<int> haha = { , , , , , , , , };
int c = findOne(haha); // 先找出一个非重复的元素
haha.push_back(c); // 将找到的元素添加到原数组末尾,相当于去掉了这个非重复元素
vector<int> result = findTwo(haha); // 寻找剩余两个非重复元素
result.push_back(c); cout << "result -> a: " << result[] << ", b: " << result[] << ", c: " << result[] << endl;
return ;
}

  ■ 输出结果

findOne -> c:
findTwo -> a: , b:
result -> a: , b: , c:

▶ 格雷码与普通二进制码相互转换。证明使用中括号记法,考虑结果每一位来源于 bin 的哪几位的计算

 gray = bin ^ (bin >> )
bin = gray ^ (gray >> )

位运算骚操作 Part 3的更多相关文章

  1. 位运算骚操作 Part 1

    ▶ 原文标题<Bit Twiddling Hacks>,地址:https://graphics.stanford.edu/~seander/bithacks.html ▶ 额外参考资料:h ...

  2. 位运算骚操作 Part 2

    ▶ 计算 unsigned int v 的以 2 为底的对数,结果放入 unsigned int r . // 方法零 #pragma unroll ;v; r++, v >>= ); / ...

  3. java位运算(操作)的使用

    位操作是程序设计中对位模式按位或二进制数的一元和二元操作. 在许多古老的微处理器上, 位运算比加减运算略快, 通常位运算比乘除法运算要快很多. 在现代架构中, 情况并非如此:位运算的运算速度通常与加法 ...

  4. C 碎片九 预处理&位运算&文件操作

    一.预处理 预处理语句:#开头的语句,在预处理阶段处理预处理语句.包括宏定义.文件包含处理.条件编译 1, 宏定义 1. 不带参数宏定义:#define 标识符  字符串 #define PI 3.1 ...

  5. LeetCode | 289. 生命游戏(原地算法/位运算)

    记录dalao的位运算骚操作 根据百度百科 ,生命游戏,简称为生命,是英国数学家约翰·何顿·康威在 1970 年发明的细胞自动机. 给定一个包含 m × n 个格子的面板,每一个格子都可以看成是一个细 ...

  6. 位运算之——按位与(&)操作——(快速取模算法)

    学习redis 字典结构,hash找槽位 求槽位的索引值时,用到了 hash值 & sizemask操作, 其后的scan操作涉及扫描顺序逻辑,对同模的槽位 按一定规则扫描! 其中涉及位运算 ...

  7. 【Java基础】14、位运算之——按位与(&)操作——(快速取模算法)

    学习redis 字典结构,hash找槽位 求槽位的索引值时,用到了 hash值 & sizemask操作, 其后的scan操作涉及扫描顺序逻辑,对同模的槽位 按一定规则扫描! 其中涉及位运算 ...

  8. 全国计算机等级考试二级教程-C语言程序设计_第15章_位运算

    位运算,不适用于实数,仅仅适用于整数.字符. C语言的位运算只能操作整数.字符,实数是指数方式表示的,不适用于位运算. #define _CRT_SECURE_NO_WARNINGS #include ...

  9. LeetCode解题中位运算的运用

    位运算是我最近才开始重视的东西,因为在LeetCode上面刷题的时候发现很多题目使用位运算会快很多.位运算的使用包含着许多技巧(详细可以参考http://blog.csdn.net/zmazon/ar ...

随机推荐

  1. oracle data type

    NUMBER ( precision, scale) precision表示数字中的有效位.如果没有指定precision的话,Oracle将使用38作为精度. scale表示数字小数点右边的位数,s ...

  2. uva 202

    #include <iostream> #include<cstdio> #include<cstring> #include<algorithm> # ...

  3. jsp(待改)

    ##JSP 1.指令 作用:用于配置JSP页面,导入资源文件 *书写格式 <%@ 指令名称 属性1=值1,属性2=值2  ...%> *分类: *page :配置JSP页面的 #属性: c ...

  4. 20164318 毛瀚逸 Exp3 免杀原理与实践

    1实验要求 1.1 正确使用msf编码器(0.5分),msfvenom生成如jar之类的其他文件(0.5分),veil-evasion(0.5分),加壳工具(0.5分),使用shellcode编程(1 ...

  5. XPATH 要想获取的东西里不分段,不变成列表就用STRING(),不用TEXT()

    简单说一说: requests配合xpath来抓网站数据的时候,不像selenium+xpath. selenium有  find_element  find_elements,区别是带S ,查找第一 ...

  6. Ubuntu 16.10的root默认密码设置

    1.终端输入sudo passwd 2.输入当前用户密码,回车 3.按照终端提示输入新的root密码并确认 4.su root 输入新的密码 5.修改root密码成功

  7. Scrapy 代理IP

    Scrapy 代理IP 一.Scarpy使用代理IP 1.在setting.py 配置 代理服务器IP 2.在middlermares.py 配置 downloadmiddlermare(下载中间件) ...

  8. Game Development Patterns and Best Practices (John P. Doran / Matt Casanova 著)

    https://github.com/PacktPublishing/Game-Development-Patterns-and-Best-Practices https://github.com/m ...

  9. Java之Java7新特性之try资源句式

    一.原来写法: static String readFirstLineFromFile(String path) throws IOException { BufferedReader br = nu ...

  10. vue中直接修改props中的值并未给出警告,为啥?

    问:vue中直接修改props中的值并未给出警告,为啥? 答:如果props传入的值是引用类型,在子组件中改变其元素,不改变引用,那么不报错: 如果是基本类型,那么在修改时浏览器控制台会有报错信息. ...