第一章 开篇(2)
2011-11-14 21:34
260 查看
习题 5. 那个程序员说他有1MB的可用存储空间,但是我们概要描述的代码需要1.25MB的空间。他可以不费力气的索取到额外的空间。如果1MB空间是严格的边界,你会推荐如何处理呢?你的算法的运行时间又是多少?
用位向量表示10 000 000个整数需要10 000 000个位,10000000 bits = 1.192MB.所以程序员可利用的1MB空间是显然不够的,可以采用两趟算法,第一趟利用5000000个位来排序0~4999999之间的整数,然后在第二趟中排序排序5000000~9999999之间的整数。代码如下:
排序计算时间为(m = n = 1000 0000):0.859s
k趟算法可以在kn的时间开销和n/k的空间开销内完成对最多n个小于n的无重复正整数的排序。
习题 6. 如果那个程序员说的不是每个整数做多出现一次,而是每个整数最多出现10次,你又如何建议他呢?你的解决方案如何随着可用存储空间总量的变化而变化呢?
因为10等于二进制的1010, 2^3<10<2^4,所以可以用4位的半字节来统计它的出现次数,即需要4*1000 0000个位来表示1000 0000个正整数。利用习题五,可以使用4000 0000/k个位在k趟内完成对整个文件的排序。
习题 7. 本书1.4节中描述的程序存在一些缺陷。首先是假设在输入中没有出现两次的整数。如果是假定在输入中没有出现两次的整数。如果某个数出现超过一次的话,会发生什么?在这种情况下,如何修改程序来调用错误处理函数?当输入整数小于零或者大于等于n时,又会发生什么?如果某个输入不是数值又如何?在这些情况下,程序该如何处理?程序还应该包含哪些明智的检查?描述一些用以测试程序的小型数据集合,并说明如何正确处理上述以及其他不良情况。
1) 如果某个数出现超过一次的话,那么该数在排序后的文件中只会出现一次,因为该数对应的位被重复置位为1.在调用setBits()函数时,首先检查该数在位向量中对应的的位是否为0,如果是0表示该数第一次出现,那么将其置为1;否则则表示该数是重复出现的,可抛出异常进行错误处理。
2) 因为负数对于是个有符号数,其二进制的最高位为1,但计算机存储负数并不是按照其二进制形式直接存储的,而是对其取反加1再存储的,所以当输入小于零时会导致移位运算符和位运算符产生错误的值,从而导致访问数组越界等问题。如:
输出结果为:0
10
-1
22
对于任何一个整数A,若A>0,则其补码等于其本身;若A<0,则其补码为其符号位不变,其他位按位取反再加1。
b = 10000000000000000 00000000 00001010 //b的原码
b' = 11111111 11111111 11111111 11110110 //b的补码
所以,b>>5 = 1111111111111111 11111111 11111111 (算术右移,即对有符号数右移时,左边移入该数的符号位,原来为1就移入1,原来为0就移入0),也就等于-1. b & 31 = 00000000 0000000000000000 00010110 = 22
若输入整数大于或小于n时,会发生位向量数据访问越界。对于输入不是数值时,应首先检查输入数据是否为整数。
用位向量表示10 000 000个整数需要10 000 000个位,10000000 bits = 1.192MB.所以程序员可利用的1MB空间是显然不够的,可以采用两趟算法,第一趟利用5000000个位来排序0~4999999之间的整数,然后在第二趟中排序排序5000000~9999999之间的整数。代码如下:
#include <stdlib.h> #include <time.h> #include <iostream> #include <boost/timer.hpp> #include <boost/progress.hpp> using namespace std; using namespace boost; #define BITSPERDWORD 32 //表示一个整形变量具有32个位 #define SHIFT 5 //单次位移量,右移一位相当于除以2,左移一位相当于乘以2 #define MASK 0x1f //掩码,其十进制即为31 #define N 10000000 //表示10000000个7为非负整数 //将位数组从0开始的第i个位置为0,即将整数i在位向量中对应位置的位置为false void resetBits(int * arry, int i) { arry[i >> SHIFT] &= ~(1 << (i & MASK)); //m%n,n = 2^x时,m%n = m & (n-1) } //将位数组从0开始的第i个位置为1,即将整数i在位向量中对应位置的位置为true void setBits(int * arry, int i) { arry[i >> SHIFT] |= (1 << (i & MASK)); } //检测i是否在集合中 int testBits(int * arry, int i) { return arry[i >> SHIFT] & (1 << (i & MASK)); } //返回[l, u]范围内的一个随机整数 int randint(int l, int u) { return (l + rand() % (u - l + 1)); } int * geneRand2(int m ,int n) { int i, j; int *pArry = new int ; for (i = 0; i < n; i++) //生成数组 { pArry[i] = i; } //筛选随机数 srand((unsigned)time(NULL)); for (i = 0; i < m; i++) { j = randint(i, n - 1); int t = pArry[i]; pArry[i] = pArry[j]; pArry[j] = t ; } return pArry; } void mySort(int m ,int n) { int i, j = 0, k; int *pBitsArry = new int[1 +n / (2 * BITSPERDWORD)]; //使用空间减半 int * p = geneRand2(m ,n); int *pOutArry = new int[m]; //模拟输出文件 progress_timer t; for (k = 0; k < 2; k++) { for (i = 0; i < (n / 2); i++) { resetBits(pBitsArry, i); } if (0 == k) { for (i = 0; i < m; i++) { if (p[i] < (n / 2)) { setBits(pBitsArry, p[i]); } } for (i = 0; i < (n / 2); i++) { if (testBits(pBitsArry, i)) { pOutArry[j++] = i; } } } else { for (i = 0; i < m; i++) { if (p[i] >= (n / 2)) { setBits(pBitsArry, p[i]-(n / 2)); } } for (i = n / 2; i < n; i++) { if (testBits(pBitsArry, (i - n / 2))) { pOutArry[j++] = i; } } } } cout << t.elapsed()<<endl; // 生成位于0至n-1之间m个有序且不重复的随机整数所用的时间 /* cout<<"..........................."<<endl; for (i = 0; i < m; i++) { cout << pOutArry[i]<<endl; } cout<<"..........................."<<endl;*/ delete [] p; delete [] pBitsArry; delete [] pOutArry; } int _tmain(int argc, _TCHAR* argv[]) { mySort(10000000,10000000); return 0; }
排序计算时间为(m = n = 1000 0000):0.859s
k趟算法可以在kn的时间开销和n/k的空间开销内完成对最多n个小于n的无重复正整数的排序。
习题 6. 如果那个程序员说的不是每个整数做多出现一次,而是每个整数最多出现10次,你又如何建议他呢?你的解决方案如何随着可用存储空间总量的变化而变化呢?
因为10等于二进制的1010, 2^3<10<2^4,所以可以用4位的半字节来统计它的出现次数,即需要4*1000 0000个位来表示1000 0000个正整数。利用习题五,可以使用4000 0000/k个位在k趟内完成对整个文件的排序。
习题 7. 本书1.4节中描述的程序存在一些缺陷。首先是假设在输入中没有出现两次的整数。如果是假定在输入中没有出现两次的整数。如果某个数出现超过一次的话,会发生什么?在这种情况下,如何修改程序来调用错误处理函数?当输入整数小于零或者大于等于n时,又会发生什么?如果某个输入不是数值又如何?在这些情况下,程序该如何处理?程序还应该包含哪些明智的检查?描述一些用以测试程序的小型数据集合,并说明如何正确处理上述以及其他不良情况。
1) 如果某个数出现超过一次的话,那么该数在排序后的文件中只会出现一次,因为该数对应的位被重复置位为1.在调用setBits()函数时,首先检查该数在位向量中对应的的位是否为0,如果是0表示该数第一次出现,那么将其置为1;否则则表示该数是重复出现的,可抛出异常进行错误处理。
2) 因为负数对于是个有符号数,其二进制的最高位为1,但计算机存储负数并不是按照其二进制形式直接存储的,而是对其取反加1再存储的,所以当输入小于零时会导致移位运算符和位运算符产生错误的值,从而导致访问数组越界等问题。如:
#include <iostream> using namespace std; int _tmain(int argc, _TCHAR* argv[]) { int a = 10, b = -10; cout << (a >> 5) << endl; cout << (a & 31)<<endl; cout << (b >> 5) << endl; cout << (b & 31)<<endl; return 0; }
输出结果为:0
10
-1
22
对于任何一个整数A,若A>0,则其补码等于其本身;若A<0,则其补码为其符号位不变,其他位按位取反再加1。
b = 10000000000000000 00000000 00001010 //b的原码
b' = 11111111 11111111 11111111 11110110 //b的补码
所以,b>>5 = 1111111111111111 11111111 11111111 (算术右移,即对有符号数右移时,左边移入该数的符号位,原来为1就移入1,原来为0就移入0),也就等于-1. b & 31 = 00000000 0000000000000000 00010110 = 22
若输入整数大于或小于n时,会发生位向量数据访问越界。对于输入不是数值时,应首先检查输入数据是否为整数。
相关文章推荐
- 【读后感】编程珠玑(第二版) 第一章 开篇
- 【编程珠玑】第一章:开篇
- 编程珠玑_第一章_ 开篇
- 【编程珠玑】读书笔记 第一章 开篇
- 《编程珠玑》读书笔记——第一章.开篇
- #开篇第一章
- 第一章、jms介绍--开篇
- 第一章开篇,项目经理的准备,第一篇,为什么要有中国式的项目管理
- Oracle10g OCP学习笔记 第一章 开篇随笔
- 从零开始Desire HD刷机指南 —— 第一章:开篇
- 【编程珠玑】第一章 开篇
- 编程珠玑之第一章:开篇(习题)泛览
- 博客之开篇第一章
- 编程珠玑第一章开篇 总结
- 开篇导论(操作系统概念第七版 第一章)
- 《编程珠玑》习题练习In Python——第一章 开篇
- 【编程珠玑】第一章 开篇
- 编程珠玑(开篇)--第一章习题
- 第一章 开篇
- 第一部分 核心动画开篇---第一章 什么是核心动画