編程珠璣筆記第一章 a[i>>SHIFT]

問題描述:

問題:一個最多包含n個正整數的文件,每個數都小於n,n = 100 00000。文件中的正整數沒有重複的,請按升序排列這些整數。可用內存空間只有1MB左右,運行時間在10秒左右。

選擇方案:

1:歸併排序,32爲整數,1M的內存空間,每次可以讀入約250 000(25萬)個數(注意估計方法,1M 10的6次方,1百萬字節,1G,10的9次方,10億字節)

那就需要40次讀入,第一次遍歷文件,對0-249 999的樹進行排序,第二次對250 000到499 999之間的數排序,瓶頸在於需要40次讀入文件

2:利用位圖和位向量解決,該排序的特徵爲:數據沒有重複,數據限制在較小的範圍內,除了單一整數外沒有其他的相關數據了

習題:庫語言實現排序

產生10 000 000文件

#include <iostream>  
#include <ctime>  
using namespace std;
#define RAND_MAX 10000

//rand 產生2^15次方的隨機數,bigrand產生2^30次方的隨機數,見編程珠璣12章如果用rand做產生的數據隨機性不太好,所以用bigrand
int bigrand()
{
	return (RAND_MAX)*rand()+rand();
}
int randint(int l,int u)
{
	return l+ bigrand()%(u-l+1);
}
#define SOURCENUM 10000000
int testdata[SOURCENUM];//這麼大的數組不能在main內定義,因爲棧的大小有限制,定義爲全局變量,在靜態區
int main()
{

	cout<<"shangglag"<<endl;
	srand((unsigned)time(NULL));
	for (int i = 0,j = 1; i < SOURCENUM; i++,j++)testdata[i] = j;
	for(i = 0; i <  SOURCENUM; i++)swap(testdata[i],testdata[randint(i,SOURCENUM-1)]);//rand()%(SOURCENUM-1 - i + 1)+i]
	FILE* fp1 = fopen("D:\\file3.txt","w+");
	if(!fp1)return 0;
	for( i = 0; i <SOURCENUM; i++)
	{
		fprintf(fp1,"%d\n",testdata[i]);
		if(i%10000)cout<<"-";
	}
	fclose(fp1);

}

採用位圖的方法進行排序 參考http://www.cnblogs.com/shuaiwhu/archive/2011/05/29/2065039.html

首先弄懂i>>SHIFT相當於i/32,i&MASK相當於i%32.那麼就採用這個,把a數組中的元素都設置爲0.

整個程序的思想就是:

1.每個整數有32位,那麼它就可以表示32個數,分別對應每bit位爲1. 
2.然後把10000000個數分爲1+N/BITSPERWORD組(相當於有這麼多個桶),每組包含接近32個數。
上面的解釋可能仍不到位,那我們來看具體的函數:
對於set函數,我們可以這樣理解arr[i>>SHIFT] |= (1<<(i&MASK))可以轉化爲arr[i/32] = arr[i/32] | (1<<(i%32))i%32必然處於區間[0, 31],那麼1<<(i%32)就是將bit位1向前移動(i%32)位,然後和arr[i/32]相或,因而arr[i/32]的第(i%32)位就爲1.
對於test函數,就是上面過程的反過程了。它是用來判斷i個這個數是否存在,即arr[i/32]的相應bit位是否爲1.
如下圖:
最後,就做排序了,      for (int i = 0; i < N; i++)由於[0, N)已經是從小到大排序好的,那麼我們只需判斷每個數是否存在,若存在,就輸出,所以輸出結果也就是排序的了。
#include <iostream>
#include <ctime>
#include <set>
#include <bitset>
using namespace std;
#define BITSPERWORD 32
#define SHIFT 5
#define MASK 0x1f
#define N 10000000

int a[1+N/BITSPERWORD];

void set_i(int i)
{
	a[i>>SHIFT] |= (1<<(i&MASK)); //a[i>>SHIFT] 每1個int32位爲一個桶,a[i>>SHIFT]相當於找那個32位,
								   //1<<i(i&MASK),32位中中的那位爲1
}

int test_i(int i)
{
	return a[i>>SHIFT] & (1 << (i&MASK));//跟1與
}

void clr_i(int i)
{
	a[i >> SHIFT] &= ~(1<<(i&MASK));//跟0與
}
int main()
{       
        for (int i = 0; i < (1+N/BITSPERWORD); i++ )
	{
		a[i] = 0;
	}
        FILE *fpin = fopen("D:\\file2.txt","r");
	FILE *fpout = fopen("D:\\fpout.txt","w+");
	int m = 0;
	for (int i = 1; i <= N;i++)
	{
		if(fscanf(fpin,"%d",&m)==1)
		{
			//cout << m<<endl;
			set_i(m);
		}
	}
	for (int i = 1; i <= N;i++)//排序輸出到文件
	{
		if (test_i(i))
		{
			fprintf(fpout,"%d\n",i);
		}
	}
	fclose(fpin);
	fclose(fpout);
        system("pause");
	return 0;
}
}

使用qsort函數進行排序,空間需求大30s

#include <iostream>
#include <ctime>
using namespace std;
int intcomp(const void  *x,const void *y)
{
	return *(int *)x - *(int *)y;//注意轉型
}
#define N 10000000
int testdata[N];
int main()
{
	clock_t beginTime,endTime;
	beginTime = clock();
	FILE *fpin = fopen("D:\\file4.txt","r");
	FILE *fpout = fopen("D:\\fpout4.txt","w+");
	int m = 0;
	for (int i = 1; i <=N;i++)
	{
		if(fscanf(fpin,"%d",&m)==1)
		{
			testdata[i - 1] = m;
		}
	}
	//void qsort(void *base, int nelem, int width, int (*fcmp)(const void *,const void *));   
	//參數:1、待排序數組首地址; 2、數組中待排序元素數量; 3、各元素的佔用空間大小; 4、指向函數的指針,用於確定排序的順序
	qsort(testdata,10000000,sizeof(int),intcomp);
	for (int i = 1; i <= N;i++)
	{
		fprintf(fpout,"%d\n",testdata[i-1]);
	}
	fclose(fpin);
	fclose(fpout);
	endTime = clock();
	cout<<(double)(endTime - beginTime)/CLOCKS_PER_SEC<<endl;
}

用STLset進行排序

clock_t beginTime,endTime;
	beginTime = clock();
	FILE *fpin = fopen("D:\\file4.txt","r");
	FILE *fpout = fopen("D:\\fpout5.txt","w+");
	int m = 0;
	set<int> S;
	for (int i = 1; i <=N;i++)
	{
		if(fscanf(fpin,"%d",&m)==1)
		{
			S.insert(m);
		}
	}
	set<int>::iterator siterator;
	for (siterator = S.begin();siterator!=S.end();++siterator)
	{
		fprintf(fpout,"%d\n",*siterator);
	}

	fclose(fpin);
	fclose(fpout);
	endTime = clock();
	cout<<(double)(endTime - beginTime)/CLOCKS_PER_SEC<<endl;



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章