大數據項目(僅自我複習所用,無參考意義)
題目:利用500M的內存,計算10G的數據的中位數。
- 首先我們需要生成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;
}