编程珠玑 Column 1

本文介绍了如何在有限内存(约1M)内对1到9,999,999之间的不重复随机数进行排序。通过位数组和随机数生成策略解决此问题,探讨了生成随机数文件的算法,以及当随机数可能重复时的解决方案。文章还讨论了如何在内存限制下优化文件操作,以减少对性能的影响。" 133056534,20038709,STM32微控制器实现MQTT通信教程,"['STM32开发', '嵌入式硬件', '单片机', '物联网通信', 'MQTT库']
摘要由CSDN通过智能技术生成

概述

Column 1由一个问题展开讨论:如何给一个包含0到9,999,999编号的记录文件排序并输出到另一个文件,其中编号不重复,只允许1M左右的内存空间。

最优的解法自然是采用位数组,1.25×106B大小的位数组含10,000,000位,可以保存规定范围内数是否存在。这种方法很好的利用了编号不重复的特性。


生成随机数文件

要对算法进行实现,面临的第一个问题就是,如何生成这么一个记录文件?这也练习4提出的问题:如何从范围0到n-1范围内生成k个不重复的随机数。以下是我的一个解法(Column 12对这个问题有详细的分析并提出了一些解法):

首先生成一个大小为n的整型数组,从第0位到第n-1位数组内容分别填对应的数组下标,这样数组就包含了不重复的从0到n-1的所有数字。那么接下来的工作就是每次从该数组未被选择的数中随机选择出一个数直到选出k个数。对此我们可以将每次选出的数和当前剩余的未选出的数的最后一个交换,这样选择结束后数组的最后k个数就是我们“生成”的随机数。以下是生成随机数文件的程序代码

 

使用这个程序后生成了一个随机数文件,发现生成的文件明显不随机!文件开始的一些数字很小,但是之后的数字几乎全部集中在9,000,000之上!这显然是存在问题的。试着减小了MAXN和SELECTK的规模到1000和100,就很正常了。查了一下rand()函数的说明,生成的伪随机数的范围是在0到RAND_MAX之间,我的第一感觉就是肯定RAND_MAX偏小!在程序中加入了打印RAND_MAX的语句后,可以看到RAND_MAX的值为32767。看来我的猜测是正确的。所以需要修改程序增大随机数生成范围,最简单的办法就是rand()生成的随机数再平方作为最终的随机数。所以上述代码进行以下修改

 

再次进行测试,发现这次生成的结果就比较正常了。这样,有了ranfile.txt文件,就可以接下来进行排序算法实现了。




排序算法

C++提供了bit vector的模板类bitset,我们可以很方便的像使用普通数组那用利用bitset而不必关心位操作的细节。当然也可以自己实现位数组,书中也进行了一些讨论并给出了示例。实现的代码如下

 

bitset模板的默认是全部被初始化为0的。注意的是我在这里给位赋值的时候并没有用true或者false,因为true的定义不一定是1,有可能是除0以外的某个整数,所以使用0和1可以保证最大的兼容性。

还有一个问题是大变量的定义,我们应该尽量不定义在栈上而是全局变量区或者堆上,这样可以避免栈溢出。虽然这种情况在用户空间的应用程序中几乎不可能发生因为应用程序的堆是可以动态增长的,但放在栈资源紧缺的内核空间下,这样的做法就显得危险了。所以没有必要我认为还是不要在栈上定义大变量。


问题讨论

问题5:在解决这个问题时我们使用的实际内存是1.25M。如果内存的限制不是粗略的1M而是精确的,那么怎么解决呢?

这个问题可以结合书中之前给出的解法综合一下解决。我们可以将给出的大文件先分成两份,一份只含有0-4,999,999而另一份只含5,000,000-9,999,999。这样按照之前的方法进行两次排序,最后将两个排序结果文件合并。这样的话我们和之前相比多出了若干次文件操作,文件操作相对内存操作是要费时很多的,所以我们应尽量减少文件操作,可以两次读取ranfile.txt文件,一次只处理0-4,999,999将结果写入输出文件,然后再次读取ranfile.txt并只处理5,000,000-9,999,999并将结果继续写入输出文件。这样我们就只多进行了一次文件读取操作,加上文件系统的缓存策略,这种方法的效率和1.25M内存的解法差不了太多。

问题6:如果每个数不是最多出现一次,而是最多10次,那么该怎么解决呢?

我们可以对这个bitset进行改进,没个单元不是1位,而是4位,每一单元存储这个数出现的次数,问题就得到解决了。当然,如果空间还是1M限制的话,呵呵,参见问题5~

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值