大數據找中位數

                      大數據項目(僅自我複習所用,無參考意義)

題目:利用500M的內存,計算10G的數據的中位數。

  1. 首先我們需要生成10G的數據,這裏用到了一些文文件的操作。(fopen()打開文件,fwrite() 寫文件,rand()產生隨機數函數,fclose()關閉文件)。

             注意點:(1).打開文件必須要關閉文件。

                           (2)rand()是產生隨機數的函數,他的取值範圍是0~32767,他產生的是僞隨機數。srand()和時間參數                                       纔可以真正的生成隨機數。

2.創建100個文件,將這些數據利用類似於哈希的方式將他們填進去。

       (1). 這裏我們先用一個(char)類型二維數組,將這100個文件命名,用到sprintf()這個函數。

       (2)然後用這100個文件名去創建100個文件,就回到了文件操作。

       (3)將這些隨機數散列的放到這100個文件中去。(用每個值除以每個段的平均處理寬度)。

       (4)關閉文件。

3.比較這100個文件的大小,然後找到最中間的那個文件。

       (1)我們總共有10個G的數據,然後就一半就是5G,然後從第一個文件開始相加,當大於5G時證明,中位數就在這個文件中。

       (2)比較這個文件是否大於500M,若大於,繼續分裂,不大於就選擇合適的排序(堆排序和快排),找到中位數。

 4.驗證。

                    將所有的數據直接加載到內存去,和自己的方法是否一樣。

附代碼:

#define _CRT_SECURE_NO_WARNINGS  //消除fopen函數的警告 
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <time.h>
//利用500M的內存,計算10G的數據的中位數

#define NUM_SIZE (102411*1024*1024/4*10)  //10G的數據量
#define ITEM_SIZE (1024*1024/4*500)       //單次能使用的內存500M,操作的數據量
#define ITEM_RANGE 10736763               //每個段平均的數據寬度,32767*32767/100+1 取整

void QuickSort(int *arr, int len);         //快速排序
//void HeapSort(int *arr, int len);          //堆排序

//統計二進制文件path的字節數
long long FileSize(const char *path)
{
	FILE *fr = fopen(path, "rb");
	assert(fr != NULL);

	fseek(fr, 0, SEEK_END);
	long long count = ftell(fr);

	fclose(fr);
	return count;
}

//產生隨機數
void CreateBigFile(const char *path)
{
	FILE *fw = fopen(path, "wb");//二進制數據
	assert(fw != NULL);

	int tmp;

	for (long long i = 0; i<NUM_SIZE; i++)
	{
		tmp = rand()*rand();
		fwrite(&tmp, sizeof(int), 1, fw);
	}
	fclose(fw);
}

//需要找中位數,則將所有的數字按段分成100段//實測Windows 7打開文件超過509個之後不能再打開新的文件
//然後在某一個分段函數中找到中位數
int Division(char *path)
{
#define DIV_NUM 100
	char pathArr[DIV_NUM][10];//存放100個文件名
	int *buf = (int *)malloc(ITEM_SIZE*sizeof(int));
	FILE *fw[DIV_NUM];
	long long i;
	for (i = 0; i<DIV_NUM; i++)//生成文件名稱
	{
		sprintf(pathArr[i], "%d.txt", i);
	}

	for (i = 0; i<DIV_NUM; i++)
	{
		assert((fw[i] = fopen(pathArr[i], "wb")) != NULL);//打開成功
	}
	FILE *fr = fopen(path, "rb");
	assert(fr != NULL);
	int len = 0;//讀取數據的長度
	int index;
	while ((len = fread(buf, sizeof(int), ITEM_SIZE, fr))>0)//從原文件中讀取數據,然後保存到其它的100個文件中
	{
		for (int i = 0; i<len; i++)
		{
			index = buf[i] / ITEM_RANGE;
			fwrite(&buf[i], sizeof(int), 1, fw[index]);  //將數字保存到對應的文件中
		}
	}

	for (i = 0; i<DIV_NUM; i++)
	{
		fclose(fw[i]);
	}
	fclose(fr);


	long long mid = NUM_SIZE / 2;//中位數
	long long count = 0;//當前分段文件的字節數

	for (i = 0; i<DIV_NUM; i++)//先初步定位中位數所在的分段文件,然後在分段文件中找到該中位數
	{
		count += FileSize(pathArr[i]);
		if (count >= mid*sizeof(int))  //中位數在i號文件中
		{
			break;
		}
	}

	long long tmp = mid - (count - FileSize(pathArr[i])) / sizeof(int);//計算中位數在第i個文件中的第幾個(按大小算)數字

	//todo如果第i號分段文件的大小沒有超過內存限制則直接排序,如果超過限制還需要繼續分段。
	//這裏假設不超過
	fr = fopen(pathArr[i], "rb");
	assert(fr != NULL);
	len = fread(buf, sizeof(int), ITEM_SIZE, fr);
	fclose(fr);

	QuickSort(buf, len);

	int num = buf[tmp];

	free(buf);

	return num;
}

//快速排序的劃分函數
static int Partition(int *arr, int low, int high)
{
	int tmp = arr[low];

	while (low < high)
	{
		while (low<high && arr[high] >= tmp)//從後往前找比基準小的數據
		{
			high--;
		}
		if (low == high)
		{
			break;
		}
		else
		{
			arr[low] = arr[high];//************
		}

		while (low<high && arr[low] <= tmp)//從前往後找比基準大的數字
		{
			low++;
		}
		if (arr[low] > tmp)
		{
			arr[high] = arr[low];//***********
		}
		else
		{
			break;
		}
	}

	arr[low] = tmp;

	return low;
}

static void Quick(int *arr, int low, int high)
{
	int par = Partition(arr, low, high);

	if (low + 1 < par)//至少有兩個數據
	{
		Quick(arr, low, par - 1);
	}
	if (par + 1 < high)
	{
		Quick(arr, par + 1, high);
	}
}

//快速排序
void QuickSort(int *arr, int len)
{
	Quick(arr, 0, len - 1);
}

/* 堆排序
static void HeapAdjust(int *arr, int start, int end)
{//O(logn),O(1),
	int tmp = arr[start];
	int parent = start;

	for (int i = 2 * parent + 1; i <= end; i = 2 * i + 1)
	{
		if ((i + 1 <= end) && (arr[i]<arr[i + 1]))
		{
			++i;
		}//i一定是左右孩子較大值的下標

		if (tmp < arr[i])
		{
			arr[parent] = arr[i];
			parent = i;
		}
		else
		{
			//arr[parent] = tmp;
			//return ;
			break;
		}
	}
	arr[parent] = tmp;
}

void HeapSort(int *arr, int len)//O(nlogn),O(1),不穩定
{
	//第一個建大根堆
	int i;
	int tmp;
	for (i = (len - 1 - 1) / 2; i >= 0; i--)//O(nlogn)
	{
		HeapAdjust(arr, i, len - 1);
	}

	for (i = 0; i<len - 1; i++)//O(nlogn)
	{
		tmp = arr[0];
		arr[0] = arr[len - 1 - i];
		arr[len - 1 - i] = tmp;

		HeapAdjust(arr, 0, len - i - 1 - 1);//已經有序的數據不參與調整
	}
}
*/
//將全部數據讀到內存,計算中位數
//驗證上述分段的方法是否正確
int Mid1(const char *path)
{
	int *arr = (int *)malloc(NUM_SIZE*sizeof(int));
	//int *arr = NULL;
	assert(arr != NULL);
	FILE *fr = fopen(path, "rb");
	assert(fr != NULL);
	int len = fread(arr, sizeof(int), NUM_SIZE, fr);
	fclose(fr);

	QuickSort(arr, len);
	int tmp = arr[len / 2]; // 中位數
	free(arr);

	return tmp;
}

int main()
{
	char *path = "big.txt";
	printf("%d\n",Mid1(path));
	clock_t c1 = clock();
	CreateBigFile(path);//產生10億個數字大概需要300秒
	clock_t c2 = clock();
	printf("產生10億個數字的時間爲%d毫米\n", c2 - c1);//大概需要300秒
	int num = Division(path);//大概300秒左右
	printf("%d\n", num);
	c1 = clock();
	printf("獲取10億個數字的中位數時間爲%d毫米\n", c1 - c2);

	return 0;
}

 

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