位运算应用
题目
题1:找出唯一成对的数。
题2:找出落单的那个数。
题3:二进制中1的个数。
题4:是不是2的整数次方。
题5:将整数的奇偶位互换。
题6:0~1间的浮点实数的二进制表示。
题7:出现k次与出现1次。
题目来源为蓝桥杯的一个培训课(主讲人:郑未)。
测试设计
c++程序中使用的产生随机数的测试头文件,如下:
1 /** 2 调用格式 3 a = GenerateDiffNumber(min,max,num); 4 a = Repeatedrandomnumber(min,max,num);//默认num为奇数 5 a = D_B(N);//默认为32位二进制 6 a = random_3();//1个随机数 7 a = decimal();//0~1的随机数 8 a = radixto(num,base)//十进制转换为其他进制(2<=base<=34) string radixto(int num, int base) 9 Stringflip(array, basin, base);//一组十进制数array[len],转换为base进制数,使用string类型的basin[len]存储 10 a = oonra(N); //注意使用vector<int>().swap(a);释放容器,vector<int> oonra(int N) 11 a = krepetition(k); //重复数为k只有1个不重复,输入k。 12 13 [author]:zlc,[date]:20204/19 14 15 **/ 16 #include<vector> 17 #include<iostream> 18 #include<cstdlib> 19 #include<ctime> 20 #include<cstring> 21 #include<algorithm> 22 using namespace std; 23 vector<int> GenerateDiffNumber(int min,int max,int num) 24 { 25 int rnd; 26 vector<int> diff; 27 vector<int> tmp;//存储剩余的数 28 //初始化 29 for(int i = min;i < max+1 ; i++ ) 30 tmp.push_back(i); 31 srand((unsigned)time(0)); //初始化随机数种子 32 for(int i = 0 ; i < num ; i++) 33 { 34 do{ 35 rnd = min+rand()%(max-min+1); 36 }while(tmp.at(rnd-min)==-1); 37 diff.push_back(rnd); 38 tmp.at(rnd-min) = -1; 39 } 40 return diff; 41 } 42 43 vector<int> Repeatedrandomnumber(int min,int max,int num) 44 { 45 int rnd, i, j; 46 vector<int> pack;//存储数据 47 vector<int> tank(num); 48 srand((unsigned)time(0)); //初始化随机数种子 49 50 pack = GenerateDiffNumber(min,max,num/2+1); 51 int len = pack.size(); 52 for(i=0; i<len-1; i++) 53 pack.push_back(pack[i]); 54 tank = GenerateDiffNumber(1,num,num); 55 for(i=0; i<num; i++){ 56 rnd = pack[tank[i]-1];//数组下标需要-1 57 tank[i] = rnd; 58 } 59 return tank; 60 } 61 62 vector<int> D_B(int N) 63 { 64 int i=0; 65 vector<int> can(32);//默认初始值为32个0 66 while(N){ 67 if(N%2 == 1) 68 can[31-i] = 1; 69 N /= 2; 70 i++; 71 } 72 return can; 73 } 74 75 int random_3()//产生一个随机数 76 { 77 int rnd; 78 srand((unsigned)time(0)); //初始化随机数种子 79 rnd = rand(); 80 return rnd; 81 //return rnd%10;//得到个位数 82 } 83 84 double decimal() 85 { 86 double x; 87 x = 1.0 / random_3(); 88 return x; 89 } 90 91 int base = 10;//默认为十进制 92 string radixto(int num, int base)//十进制转换为其他进制(2<=base<=34) 93 { 94 int i, cur; 95 string ch, st; 96 //思路为短除法,且一次性得到翻转的进制数 97 st = ""; 98 if(num==0) 99 return "0"; 100 while(num){ 101 cur = num%base; 102 if(cur <= 9) { 103 ch = cur+'0'; 104 st.append(ch); 105 } 106 else { 107 ch = cur-10+'A'; 108 st.append(ch); 109 } 110 num /= base; 111 } 112 113 return st; 114 } 115 116 void* Stringflip(int arr[], string basin[], int base) 117 { 118 string st; 119 int len = sizeof(arr)*2; 120 for(int i=0; i<len; i++){//遍历每一个要转换的数 121 st = radixto(arr[i], base); 122 reverse(st.begin(),st.end()); 123 basin[i].append(st); 124 } 125 } 126 127 128 vector<int> oonra(int N)//只有一个不重复数组 Only one non repeating array,没有控制的随机长度 129 { 130 int rnd, i, j, num, sum=0; 131 bool flag = true; 132 vector<int> pack;//存储重复数,清空后存储最终数据 133 vector<int> holder; //存储未乱序的原始数组 134 srand((unsigned)time(0)); //初始化随机数种子 135 num = rand()%6+1;//得到数组中的种类数,默认不大于6 136 137 pack = GenerateDiffNumber(2,10,num);//默认数据的重复个数不超过10个 138 139 int len; 140 for(j=0; j<num; j++){//得到需要的随机数组的总长度 141 sum += pack[j]; 142 if(pack[j]==1) 143 flag = false; 144 } 145 146 if(flag){ 147 sum = sum-pack[0]+1; 148 pack[0] = 1; 149 } 150 151 holder = GenerateDiffNumber(1,N,num);//得到未重复的原始数据,N远大于6 152 153 for(i=0; i<num; i++){ 154 len = pack[i]; 155 rnd = holder[i]; 156 for(j=1; j<len; j++) 157 holder.push_back(rnd); 158 } 159 160 vector<int> tank; //储存乱序因子 161 162 tank = GenerateDiffNumber(1,sum,sum);//乱序因子 163 164 pack.clear(); 165 for(int i=0; i<sum; i++){ 166 rnd = holder[tank[i]-1];//数组下标需要-1 167 pack.push_back(rnd); 168 } 169 170 //释放容器 171 vector<int>().swap(holder); 172 vector<int>().swap(tank); 173 return pack; 174 } 175 176 vector<int> krepetition(int k) 177 { 178 vector<int> pack;//存储未乱序的原始数组 179 vector<int> holder; //存储重复数,清空后存储最终数据 180 int num, i, j; 181 srand((unsigned)time(0)); //初始化随机数种子 182 num = rand()%6+1;//得到数组中的种类数,默认不大于6 183 pack = GenerateDiffNumber(1,100,num);//默认数据的重复个数不超过10个 184 int rnd; 185 for(i=0; i<num-1; i++){ 186 rnd = pack[i]; 187 for(j=1; j<k; j++) 188 pack.push_back(rnd); 189 } 190 int len = pack.size(); 191 192 vector<int> tank; //储存乱序因子 193 194 tank = GenerateDiffNumber(1,len,len);//乱序因子 195 196 for(int i=0; i<len; i++){ 197 rnd = pack[tank[i]-1];//数组下标需要-1 198 holder.push_back(rnd); 199 } 200 201 vector<int>().swap(pack); 202 vector<int>().swap(tank); 203 return holder; 204 } 205 206 207 /** 208 语句解释: 209 1、tmp.at(rnd-min)==-1 // vector的一个函数,查得的资料如下, 210 Access element.Returns a reference to the element at position n in the vector. 211 The function automatically checks whether n is within the bounds of valid elements in the vector, 212 throwing an out_of_range exception if it is not (i.e., if n is greater than, or equal to, its size). 213 This is in contrast with member operator[], that does not check against bounds. 214 (中译) 215 访问元素。返回对向量中位置n处元素的引用。 216 该函数自动检查n是否在向量中有效元素的范围内,如果n不在范围外, 217 则抛出一个异常(即,如果n大于或等于其大小)。这与不检查边界的成员运算符[]形成对比。 218 这便是一个使数组得到不重复数据的关键点。 219 2、 int min, max, num;//在[min,max]取num个数。 220 **/
笔记与代码
(代码主要为Java和C++)
1 import java.util.Random; 2 public class wei{ 3 public static void main(String[] args) { 4 int N = 1001; 5 int[] arr = new int[N]; 6 for (int i = 0; i < arr.length; i++) { 7 arr[i] = i + 1; 8 } 9 int index = new Random().nextInt(N); 10 arr[arr.length - 1] = arr[index]; 11 arr[index] = new Random().nextInt(N - 1) + 1; 12 int x = 0; 13 for (int i = 1; i < N; i++) { 14 x ^= i; 15 } 16 for (int i = 0; i < N; i++) { 17 x ^= arr[i]; 18 System.out.printf("%d ", arr[i]); 19 } 20 System.out.println(); 21 System.out.println(x); 22 System.out.println("=========="); 23 int[] helper = new int[N]; 24 for (int i = 0; i < N; i++) { 25 helper[arr[i]]++; 26 } 27 for (int i = 0; i < N; i++) { 28 if (helper[i] == 2) { 29 System.out.println(i); 30 break; 31 } 32 } 33 } 34 }
1 /** 2 题目数据只有一个重复,其他数据唯一 。 3 思路1:辅助空间计数法,开辟一个长度为1000的数组 ,扫描题目数组,并计数。 4 伪代码: 5 int helper[1001]; 6 memset(helper, 0, sizeof(helper));//全0数组 7 for(int i from 1 to 1001) 8 helper[array[i]]++; 9 for(int j from 1 to 1000) 10 if(helper[j] == 2) 11 cout<< j; 12 break; 13 时间复杂度为:O(n),但是需要开辟辅助空间 14 思路2:由异或的自反性 A^B^B=A^0=A可设计一个时间复杂度为O(n),且在原址上操作的算法 15 伪代码: 16 int x = 0 ; 17 for(int i from 1 to 1000) 18 x ^= i; 19 for(int j from 0 to 1000) 20 x ^= array[j] 21 cout<< x; 22 测试设计,设计一个能生成指定范围内的乱序数据的头文件 23 **/ 24 #include "randomnumber.h"//调用生成[a,b]内不重复n个数的头文件 25 #define N 1000 26 using namespace std; 27 28 int main() 29 { 30 int x = 0, i, j; 31 vector<int> array(N+1); 32 array = GenerateDiffNumber(1,N,N); 33 array[N] = rand()%N;//产生一个重复的数 34 for(i=1; i<=N; i++) 35 x ^= i; 36 /*测试可用N=10测方便观察 37 for(i=0; i<N; i++) 38 cout<< array[i] <<' '; 39 cout<< endl; 40 */ 41 for(j=0; j<N+1; j++) 42 x ^= array[j]; 43 cout<< x; 44 return 0; 45 }
1 /** 2 题2:找出落单的那个数。一个数组里除了某一个数字之外,其他的数字出现了 3 两次。请写程序找出这个出现一次的数字。 4 5 思路1:辅助空间,数组的长度取决于最大值,浪费空间。 6 伪代码: 7 int maxsize = array[0]; 8 for(i from 1 to n) 9 maxsize = maxsize > array[i] ? maxsize : array[i]; 10 int helper[maxsize]; 11 memset(helper, 0, sizeof(helper)); 12 for(int i from 1 to n) 13 helper[array[i]]++; 14 for(int j from 1 to n) 15 if(helper[j] == 1) 16 cout<< j; 17 break; 18 19 思路2:冒泡查找,最坏情况为O(n^2) 20 伪代码: 21 int index; 22 bool flag; 23 for(i from 0 to n) 24 index = array[i]; 25 flag = true; 26 for(j from i to n) 27 if(index == array[j]) 28 flag = flase; 29 break; 30 if(flag) 31 cout<< index; 32 break; 33 思路3:位运算的异或, A^B^B=A^0=A 34 伪代码: 35 int x = 0; 36 for(i from 0 to n) 37 x ^= array[i]; 38 cout<< x; 39 40 测试设计,设计一个头文件得到一组如题示的数据。 41 **/ 42 43 #include "randomnumber.h" 44 #define N 23 45 using namespace std; 46 int main() 47 { 48 int x = 0, i, j; 49 vector<int> array(N); 50 array = Repeatedrandomnumber(1,100,N); 51 52 for(i=0; i<N; i++) 53 cout<< array[i] <<' '; 54 cout<< endl; 55 56 for(j=0; j<N; j++) 57 x ^= array[j]; 58 cout<< x; 59 return 0; 60 }
1 import java.util.Scanner; 2 public class num1 { 3 public static void main(String[] args) { 4 Scanner sc = new Scanner(System.in); 5 int N = sc.nextInt(); 6 System.out.println(Integer.toString(N, 2)); 7 int count = 0; 8 for (int i = 0; i < 32; i++) { 9 if ((N & (1 << i)) == (1 << i)) { 10 count++; 11 } 12 } 13 System.out.println(count); 14 count = 0; 15 for (int i = 0; i < 32; i++) { 16 if (((N >>> i) & 1) == 1) 17 count++; 18 } 19 System.out.println(count); 20 count = 0; 21 while (N != 0) { 22 N = ((N - 1) & N); 23 count++; 24 } 25 System.out.println(count); 26 } 27 }
1 /** 2 题3:二进制中1的个数 3 请实现一个函数,输入一个整数,输出该数二进制表示中1的个数。 4 例:9的二进制表示1001,有2位是1。 5 6 思路1:位运算运用,1&1=1,1&0=0,0&0=0,0&1=0。搭配 <<。 7 伪代码: 8 int count = 0; 9 for(i from 0 to 32) 10 if((N&(1<<i)) == (1<<i)) 11 count++; 12 13 思路2:位运算应用,使用>>>运算符用0填充最高位,使原有的数据一个一个地退到低位,再与1做判断。 14 伪代码: 15 int count = 0; 16 for(i from 0 to 32) 17 if((N>>>i)&1) == 1) 18 count++; 19 20 思路3:位运算应用,n-1与n使得二者的1错位相与便能消掉该错位的1,直至把n的1全部去掉。 21 伪代码: 22 int count = 0; 23 while(N!=0){ 24 N = ((N-1)&N); 25 count++; 26 } 27 28 测试设计:不妨设计一个进制转换头文件。 29 **/ 30 #include "randomnumber.h" 31 int main() 32 { 33 int N = random_3(); 34 int count = 0; 35 vector<int> array; 36 cout<< N <<endl; 37 38 array = D_B(N); 39 while(N!=0){ 40 N = ((N-1)&N); 41 count++; 42 } 43 44 for(int i=0; i<32; i++) 45 cout<< array[i]; 46 cout<< endl; 47 cout<< count; 48 49 return 0; 50 }
1 /** 2 题4:是不是2的整数次方。 3 用一条语句判断一个整数是不是2的整数次方。 4 5 思路1:因为2的整数次幂的二进制数只有一个1,所以(N-1)&N就可以置0 。 6 伪代码: 7 if(((N-1)&N) == 0) is an integral multiple of 2. 8 else isn't an integral multiple of 2. 9 10 测试设计:得到一个随机数。 11 **/ 12 #include "randomnumber.h" 13 int main() 14 { 15 int N = random_3(); 16 cout<< N; 17 if(((N-1)&N) == 0) 18 cout<< " yes"; 19 else 20 cout<< " no"; 21 return 0; 22 }
1 import java.util.Scanner; 2 public class ji { 3 public static void main(String[] args) { 4 Scanner scanner=new Scanner(System.in); 5 int Number=scanner.nextInt(); 6 int ou=Number & 0xaaaaaaaa; 7 int ji=Number & 0x55555555; 8 int result=(ou>>1) ^ (ji<<1); 9 System.out.println("交换前:"+Integer.toString(Number, 2)); 10 System.out.println("交换后:"+Integer.toString(result, 2)); 11 } 12 13 }
1 /** 2 题目:将整数的奇偶位互换 3 思路1:时间复杂度为O(1)的方法是应用位运算的知识,101010...10&N得到奇数位的值,010101...01得到偶数位的值,再用异或运算把二者拼在一起。 4 (0xaaaaaaaa为十六进制表示32位的二进制数,0x十六进制标示,每4位二进制数表示一位十六进制数,a表示1010) 5 6 伪代码 : 7 int ou = N&0xaaaaaaaa; 8 int ji = N&0x55555555; 9 int result = (ou>>1) ^ (ji<<1);//奇偶位互换 10 11 测试设计:得到随机数与二进制数。 12 **/ 13 #include "randomnumber.h" 14 int main() 15 { 16 int N = random_3(); 17 vector<int> array1; 18 vector<int> array2; 19 20 array1 = D_B(N); 21 for(int i=0; i<32; i++) 22 cout<< array1[i]; 23 cout<< endl; 24 int ou = N&0xaaaaaaaa; 25 int ji = N&0x55555555; 26 int result = (ou>>1) ^ (ji<<1); 27 array2 = D_B(result); 28 29 for(int i=0; i<32; i++) 30 cout<< array2[i]; 31 32 return 0; 33 }
1 public class xiaoshu { 2 public static void main(String[] args) { 3 double num = 0.625; 4 StringBuilder sb = new StringBuilder("0."); 5 while (num > 0) { 6 double r = num * 2; 7 if (r >= 1) { 8 sb.append("1"); 9 num = r - 1; 10 } else { 11 sb.append("0"); 12 num = r; 13 } 14 if (sb.length() > 34) { 15 System.out.println("ERROR"); 16 return; 17 } 18 } 19 System.out.println(sb.toString()); 20 } 21 }
1 /** 2 题6:0~1间浮点实数的二进制表示。给定一个介于0和1之间的实数,(如0.625),类型为double,打印 3 它的二进制表示(0.101,因为小数点后的二进制分别表示0.5,0.25,0.123...)。如果该数字无法精确 4 地用32位以内的二进制表示,则打印"ERROR" 5 思路1:当小于1大于0时,r = N*2得到0则第一位为0则记忆该数且N=r,否则则记忆该数为1且N=r-1,依 6 次类推,直到N==0。得到的数组长度长于32则输出"ERROR"。 7 8 伪代码: 9 string sb = "0."; 10 while(N>0) 11 double r = N*2; 12 if(r>=1) sb.append("1"); N = r-1; 13 else sb.append("0"); N = r; 14 if(sb.length()>34) cout<< "ERROR"; 15 else cout<< sb; 16 17 测试设计:得到0~1的随机数。 18 **/ 19 #include<cstring> 20 #include "randomnumber.h" 21 int main() 22 { 23 string sb = "0."; 24 double r, N = decimal(); 25 26 while(N>0){ 27 r = N*2; 28 if(r>=1) { 29 sb.append("1"); 30 N = r-1; 31 } 32 else{ 33 sb.append("0"); 34 N = r; 35 } 36 } 37 38 if(sb.length()>34) 39 cout<< "ERROR"; 40 else 41 cout<< sb; 42 return 0; 43 }
1 public class _07_出现K次 { 2 public static void main(String[] args) { 3 int[] arr = {2, 2, 2, 9, 7, 7, 7, 3, 3, 3, 6, 6, 6, 0, 0, 0}; 4 int len = arr.length; 5 char[][] kRadix = new char[len][]; 6 int k = 3; 7 8 int maxLen = 0; 9 //转成k进制字符数组 10 //对于每个数字 11 for (int i = 0; i < len; i++) { 12 //求每个数字的三进制字符串并翻转,然后转为字符数组 13 kRadix[i] = new StringBuilder(Integer.toString(arr[i], k)).reverse().toString().toCharArray(); 14 if (kRadix[i].length > maxLen) 15 maxLen = kRadix[i].length; 16 } 17 //不进位加法 18 int[] resArr = new int[maxLen]; 19 for (int i = 0; i < len; i++) { 20 // 不进位加法 21 for (int j = 0; j < maxLen; j++) { 22 if (j >= kRadix[i].length) 23 resArr[j] += 0; 24 else 25 resArr[j] += (kRadix[i][j] - '0'); 26 } 27 } 28 29 int res = 0; 30 for (int i = 0; i < maxLen; i++) { 31 res += (resArr[i] % k) * (int) (Math.pow(k, i));// 8%3=2, 32 } 33 System.out.println(res); 34 } 35 }
1 /** 2 题7:出现k次与出现1次。数组中只有一个数出现了1次,其他的数都出现了k次, 3 请输出只出现了1次的数。 4 5 思路: k个相同的k进制数做不进位加法,结果为0(可以用模运算进行解释) 6 伪代码: 7 //转成k进制字符数组并翻转 8 int maxlen=0; 9 for(i from 0 to array.length()) 10 kRadix[i] = tocharArray(array[i]); 11 if(kRadix[i].length() > maxlen) 12 maxlen = kRadix[i].length(); 13 //不进位加法 14 int resArr[maxlen]; 15 for(i from 0 to array.length()) 16 for(j from 0 to maxlen) 17 if(j>=kRadix[i].length()) 18 resArr[j] += 0; 19 else 20 resArr[j] += (kRadix[i][j] - '0'); 21 int res = 0; 22 for(i from 0 to maxlen) 23 res += (resArr[i]%k)*(int)(pow(k,i)); 24 25 测试设计:设法得到题目所描述的数组,并设计k进制数的互换与字符串的翻转。 26 **/ 27 #include "randomnumber.h" 28 #include<cmath> 29 //int array[] = {2, 2, 2, 39, 7, 7, 7, 3, 3, 3, 6, 6, 6, 0, 0, 0};//例子 30 int k; 31 vector<int> array; 32 void init() 33 { 34 int res; 35 cin>> k; 36 array = krepetition(k); 37 for(int i=0; i<array.size(); i++){ 38 cout<< array[i] << ' '; 39 } 40 cout<< endl; 41 } 42 43 int main() 44 { 45 int maxlen=0, i, j, len; 46 init(); 47 len = array.size(); 48 49 string kRadix[len]; 50 51 for(i=0; i<len; i++){ 52 kRadix[i] = radixto(array[i], k);//求每个数字的三进制字符串并翻转,然后转为字符数组,默认k为3 53 if(kRadix[i].length() > maxlen) 54 maxlen = kRadix[i].length(); 55 } 56 57 int resArr[maxlen]; 58 memset(resArr, 0, sizeof(resArr));//全0数组 59 for(i=0; i<len; i++) { 60 for(j=0; j<maxlen; j++) 61 if(j>=kRadix[i].length()) 62 resArr[j] += 0; 63 else 64 resArr[j] += (kRadix[i][j]- '0'); 65 } 66 67 int res = 0; 68 for(i=0; i<maxlen; i++) 69 res += (resArr[i]%k)*(int)(pow(k,i)); 70 cout<< res; 71 vector<int>().swap(array); 72 return 0; 73 }
【zlc】