对于在2.5亿个整数中找出不重复的数(内存不足以容纳这2.5亿个整数),已经有众多前辈给出了比较详尽的算法描述,但在具体实现上现有的例子则比较简单,规模偏小。昨天生成2.5亿个数之后,开始读文件统计,结果10个小时左右没出结果。所以今天用25万个数做实验,几分钟就得出了统计结果。在VS2010环境里新建C++控制台工程,完整的实现代码如下。程序算法实现部分参考http://blog.csdn.net/leeboy_wang/article/details/8980682,在此向转载者和原作者表示衷心谢意。
bitmap算法:https://blog.csdn.net/bandaoyu/article/details/109405244
//题目要求:在2.5亿个整数中找出不重复的整数,内存不足以容纳这2.5亿个整数。
//采用2-Bitmap方法,2bit标识一个数字状态,00表示未出现过,01表示出现1次,10表出现两次或更多
#include"StdAfx.h"
#include<stdlib.h>
#include<string.h>
#include<memory.h>
//2.5亿个整数所需存储空间为2.5*10^8*4=10^9 Byte=976562.5KB=953.6743MB<1G
//num=rand();用来生成随机数,每次都生成一个介于0-RAND_MAX范围内的数。
//RAND_MAX=32767
//用char数组存储2-Bitmap,不用考虑大小端内存的问题
//2^15=32768,用两个bit标识一个随机数的状态,共需要2^16bit=2^13Bytes=8192Byte
//char数组的大小不低于8192,这里定义为8200
unsigned char flags[8200]; //数组大小自定义
//获取数字出现的次数,参数为要查看的数字,首先计算该数字的两个标识位所在的位置,由i,j确定
unsigned get_val(int idx)
{
int i = idx/4;
int j = idx%4;
unsigned ret = (flags[i]&(0x3<<(2*j)))>>(2*j);
return ret;
}
//标识位的置位函数,参数为遍历时对应的数字idx,val对应的是该数字的出现次数,第一次出现就为1
unsigned set_val(int idx, unsigned int val)
{
int i = idx/4;
int j = idx%4;
unsigned tmp = (flags[i]&~((0x3<<(2*j))&0xff)) | (((val%4)<<(2*j))&0xff);
flags[i] = tmp;
return 0;
}
//对遍历到的每个数字要执行的操作
//首先根据数字值获得该数字出现的次数,如果>=2次就不执行任何操作,返回。
//如果数字出现过0次或1次,就在该数字对应的标识位上加1.
unsigned add_one(int idx)
{
if (get_val(idx)>=2) {
return 1;
}
else {
set_val(idx, get_val(idx)+1);
return 0;
}
}
//生成2.5亿个随机数的函数
void CreateNumFiles()
{
int i,j,k;//标识三重循环,生成2.5亿个数
int randnumber;//存储生成的随机数
FILE *fp;
char str[10];//存储序号,用于生成文本文件的命名
char ext[5]=".txt";//存储文本文件的扩展名
for(i=0;i<2500;i++)
{
sprintf(str,"%d",i+1);//数字转字符串的函数,最后结果存储到字符串str里
strcat(str,ext);//字符串拼接,用于生成最终的文件名,如"1.txt""2.txt"
fp=fopen(str,"w+");//创建文件,准备写入数字
char randnum[10];//存储转换成字符串后的数字,用于拼接换行符后写入文件,直接写入数字会造成各数字间无间距,不能区分。
for(j=0;j<100;j++)
{
for(k=0;k<1000;k++)
{
randnumber=rand();//生成随机数
sprintf(randnum,"%d",randnumber);//随机数转成字符串
strcat(randnum,"\n");//拼接换行符
fprintf(fp,randnum);//完成写入一行的操作
}
}
fclose(fp);//写完一个文件后,关闭文件指针
}
}
//生成25万个随机数的函数,与上个函数一样,2.5亿个数的运行时间过长,本次测试将规模缩小到25万个随机数
void CreateNumFiles_Less()
{
int i,j,k;
int randnumber;
FILE *fp;
char str[10];
char ext[5]=".txt";
for(i=0;i<25;i++)
{
sprintf(str,"%d",i+1);
strcat(str,ext);
fp=fopen(str,"w+");
char randnum[10];
for(j=0;j<100;j++)
{
for(k=0;k<100;k++)
{
randnumber=rand();
sprintf(randnum,"%d",randnumber);
strcat(randnum,"\n");
fprintf(fp,randnum);
}
}
fclose(fp);
}
}
//只测试非负数的情况;
//假如考虑负数的话,需增加一个2-Bitmap数组.
//int a[]={1, 3, 5, 7, 9, 1, 3, 5, 7, 1, 3, 5,1, 3, 1,10,2,4,6,8,0};网上程序自带的测试数组,有兴趣的可以自己先测测
//使用25万个数测试,运行时间不超5分钟,运行结果记录到Results的文本文件中。
int main()
{
int i;
memset(flags, 0, sizeof(flags));
/*printf("原数组为:");
for(i=0;i < sizeof(a)/sizeof(int); ++i) {
printf("%d ", a[i]);
add_one(a[i]);
}
printf("\r\n");*/
//FILE *fp_read;
int filecount=0;//标识文件序号
FILE *fp_read;//统计操作时用于打开存数字的文本文件
char str[10];//用于存储数字,作为文件名的前半部分
char ext[5]=".txt";//扩展名txt,用于拼接字符串生成相应的文件名
char buf[10];//缓冲区,用于读取数字文件时存储读到的数字
int tempint;//对于文本里读出的每个数,tempint用于存储该数字
//遍历25个文件,首先拼接文件名,再读取每行字符,转成整数后进行置位操作
for(filecount=0;filecount<25;filecount++)
{
sprintf(str,"%d",filecount+1);
strcat(str,ext);
fp_read=fopen(str,"r");
if(filecount==0)
{
if(fp_read==NULL)
CreateNumFiles_Less();
}
while(!feof(fp_read))
{
fgets(buf,10,fp_read);
tempint=atoi(buf);
//printf("%d\n",tempint);
add_one(tempint);
}
fclose(fp_read);
printf("第%d个文件读取完成!\n",filecount+1);
}
//首先新建文本文件,再遍历标识数组,把出现一次的数字写入到Result.txt文件中
FILE *fp_result;
fp_result=fopen("Result.txt","w+");
fprintf(fp_result,"只出现过一次的数:");
for(i=0;i < 32768; ++i) {
if(get_val(i) == 1)
fprintf(fp_result,"%d\n",i);
}
fclose(fp_result);
printf("执行完毕\n");
return 0;
}
方法二:https://blog.csdn.net/kingyuan666/article/details/84584017