刚开始看《编程珠玑》,没想到第一章开头作者提到的问题就是有一次在与一位学长聊天的过程中学长问到我的问题——磁盘排序,我一下便来了精神,立即把此书奉为“有缘书”,便决定一定要看下去。一直以来每当我决定要夯实自己的算法基础时,总是会因为一个小问题需要花费自己多个小时的时间而觉得“很不划算”。虽然自己觉得这样是不对的,但是当初的心总是显得很浮躁。当然,这并不表示我现在境界变高了,不浮躁了。而是意识到要想使编程变得有兴趣,就得去搞搞算法,多折腾。时机 ~
本文主要是自己在解答《编程珠玑》第一章习题3的过程中碰到的一些问题的总结。简单分为两部分:第一部分为位图排序的实现(没有参照答案)及程序调试过程中的问题;第二部分插入了这段时间内同学问过我的统计一个整数在二进制形式下1的个数的算法,因为觉得与位有关,所以也添加进来了。
一
题目大意,输入文件包含1 000 000个整数,范围(0, 10 000 000)整数不重复,请排序。
实现代码:
- #include <iostream>
- #include <fstream>
- #include <cstdlib>
- #include <ctime>
- using namespace std;
- #define BIT_SIZE 1250000
- #define NUM_SIZE 1000000
- #define MAX_NUM 10000000
- void gen_random_num( unsigned int *num )
- {
- ofstream out( "rand_num_file.txt" );
- /*
- srand( (unsigned)time(0) );
- for (int i=0; i<NUM_SIZE; ++i)
- num[i] = rand() % MAX_NUM;
- */
- for (int i=0; i<NUM_SIZE; ++i)
- num[i] = MAX_NUM - i;
- for (int i=0; i<NUM_SIZE; ++i) //35
- out <<num[i] <<"/n";
- out.close();
- }
- void zero_bit( unsigned char *bit)
- {
- for (int i=0; i<BIT_SIZE; ++i)
- bit[i] &= 0x00;
- /*
- printf ("In initialization: ");
- for (int i=0; i<BIT_SIZE; ++i)
- printf( "%x ", bit[i] );
- printf( "/n" );
- */
- }
- void pro_bit( unsigned char *bit, unsigned int *num )
- {
- unsigned int j, k;
- unsigned char s = 0x80;
- for (int i=0; i<NUM_SIZE; ++i) //58
- {
- j = num[i] / 8;
- k = num[i] % 8;
- if (k == 0)
- bit[j-1] |= 0x01;
- else
- bit[j] |= ( s>>(k-1) );
- }
- /*
- printf ("In processing: ");
- for (int i=0; i<BIT_SIZE; ++i)
- printf( "%x ", bit[i] );
- */
- }
- void output_to_file( unsigned char *bit )
- {
- unsigned char s = 0x80;
- ofstream fp( "sorted_num.txt" );
- for ( int i=0; i<BIT_SIZE; ++i )
- {
- for ( int j=1; j<=8; ++j )
- {
- if ( bit[i] & (s>>(j-1)) )
- fp <<8*i+j <<"/n";
- }
- }
- fp.close();
- }
- int main()
- {
- //char bit[ BIT_SIZE ];
- unsigned char *bit = ( unsigned char * )malloc( BIT_SIZE * sizeof(char) );
- //int num[ NUM_SIZE ];
- unsigned int *num = ( unsigned int * )malloc( NUM_SIZE * sizeof(int) );
- // Initialization
- gen_random_num( num );
- zero_bit( bit );
- // processing
- pro_bit( bit, num );
- // output to file
- output_to_file( bit );
- return 0;
- }
题目可以有两种解法,一种为分批排序再合并;另一种就是位图排序,算法的基本思想为将整数与位图建立一个直接的映射:即对于整数n,将位图中第n位置为1,最后从位图的第一位开始以此输出。不怕显笨啦,这段小程序让我断断续续地调了两天。在过程中主要收获二点:
1)随机数的问题,
因为需要自己产生1 000 000个随机整数,很自然的想到了使用C库中的rand(),及时间种子,自以为万无一失。我之所以在随机数的产生上比较自信是因为我之前稍稍研究了下随机数的产生产生,通常的做法便是直接调用rand(),但是这产生的是伪随机数,其每次调用产生的值序列会一样,因为其种子在默认情况下为1。而使用时间作为种子之后边可以在一定程度上避免这种伪随机数。不过令我没有想到的是,在调试过程中我产生的随机数个数总是大于排序过后的整数个数(通过比较存放这些整数的文件大小而得),之后再仔细查看rand()和srand()的源代码才知道直接调用是不行的,因为rand()产生的随机数大小最大为32767,在这里是不能够满足要求的!所以,如果需要可以仿照其源代码来产生满足(0, 10 000 000)的随机数。后来因为对这样产生的随机数是否重复没有把握,便直接用了个逆序数。
- RAND() 及 SRAND()源码
- static unsigned long int next = 1;
- int rand(void) // RAND_MAX assumed to be 32767
- {
- next = next * 1103515245 + 12345;
- return (unsigned int)(next/65536) % 32768;
- }
- void srand(unsigned int seed)
- {
- next = seed;
- }
2)堆与栈的区别
因为想当初我为了存储生成的1 000 000个整数,我很傻地在程序中定义了一个整形数组,企图申请大小为4M的内存空间!结果可想而知啦 ~幸好自己较早的意识到了这个失误并加以改正。之后才了解到栈的默认空间大小最大为2M(系统相关),而堆则能够较好的满足大内存需求。预知堆与栈的区别,请查看其他资料,不详细介绍啦。
二
题目大意:统计一个整数二进制形式下1的个数。
实现代码:
- //
- #include <iostream>
- using namespace std;
- int main()
- {
- int var;
- int cnt;
- int a;
- while ( 1 )
- {
- cout <<"value input: (enter 0 to quit!)/n";
- if ( !(cin>>var && var) )
- break;
- cnt = 0;
- /*
- a = (a^a | 1);
- for ( int i=0; i<32; ++i )
- {
- if (var & (a<<i))
- ++cnt;
- }
- */
- while (var)
- {
- if ( var % 2 )
- ++cnt;
- var /= 2;
- }
- cout <<"the integer contains " <<cnt <<" bit 1./n";
- }
- return 0;
- }
题目也有两种方法:一种为辗转相除并施加逻辑判断;另一种便是位运算,对每一位进行位操作。
本还想把一个有关归并排序的算法假如进来,因为这也是在这个过程中想到的,不过看这个比较费时间,还是空时再另总结,呵呵,似乎又犯急躁的毛病啦 ~