編程練習(三)(Top-K選擇)

Top-K選擇

  • 說明
    random文件裏面含有一系列正整數。要求選擇其中最大的k(k=10)個數,並顯示。

  • 目的
    考察top-K選擇算法的效率,本次將k設置爲10。

  • 程序流程
    1、讀文件到內存數組A;
    2、記錄程序開始時間;
    3、循環1百次
    4、 執行top-K選擇算法;
    (要求不能改變數組A中的內容,對數組A只能讀,不能寫)
    5、循環1百次結束
    6、記錄程序結束時間;
    7、顯示1百萬次循環的執行時間;
    8、顯示選擇的top-K個數。
    9、程序編完之後,理論上分析一下算法的時間複雜性。


解決方法

(解決方法的實現代碼可能還存在着問題,但思路是正確的。)

  • 第一種
    對n個數由大到小排序,然後選擇前k個最大的數。

  • 排序算法有很多種,快速排序應該綜合看來是最優,本代碼使用最簡單但也是最慢的排序算法來解決,時間複雜度爲o(k*n^2)。

  • c語言實現如下

#include<stdio.h>
#include<time.h>

int Read();
int i=0,a[100000];

int main(){
	clock_t start,end;
	start=clock();
	float runtime;
	int j=0,k=0;
	Read();
	for(int n=0;n<10000;n++){
		while(a[j]){
			k=j;
			while(a[k]){
				if(a[j]<=a[k]){
					int x=0;
					x=a[j];
					a[j]=a[k];
					a[k]=x;
				}
				k++;
			}
			k=0;
			j++;
		}
		printf("第%d次Top-10=",n+1);
		for(j=0;j<10;j++){
			printf("%d ",a[j]);
		}
		printf("\n");
		j=0;
	}
	end=clock();
	runtime=(double)(end-start);
	printf("程序運行時間爲%f毫秒(ms)\n",runtime);
	return 0;
}

int Read(){
	FILE *data;
	data=fopen("random.txt","r");
	while(fscanf(data,"%d",&a[i])!=EOF){
		i++;
	}
	fclose(data);
}
  • 第二種
    (1)掃描數組A, 選擇最大的數,並做標記;
    (2) 掃描數組A, 選擇第二大的數,並做標記;
    ……
    (k) 掃描數組A, 選擇第K大的數,並做標記;

  • 代碼仍存在問題未能解決,在於每次將數組中的最大值置1後,無法恢復,如果每次使用Read()函數重讀只能運行十次,整體來看時間複雜度爲o(kn)。

  • c語言實現如下

#include<stdio.h>
#include<time.h>
#include<string.h>

int Read();
int i=0,a[100000];

int main(){
	clock_t start,end;
	start=clock();
	float runtime;
	int j=0,num=0,max=0,k=0,b[10]={0};
	Read();
	for(int n=0;n<10;n++){
		for(j=0;j<10;j++){
		    max=0,k=0;
			while(a[k]){
				if(a[k]>max){
					max=a[k];
					num=k;
				}
				k++;
			}
			b[j]=max;
			a[num]=1;
		}
		printf("第%d次Top-10=",n+1);
		for(j=0;j<10;j++){
			printf("%d ",b[j]);
		}
		printf("\n");
	}
	end=clock();
	runtime=(double)(end-start);
	printf("程序運行時間爲%f毫秒(ms)\n",runtime);
	return 0;
}

int Read(){
	FILE *data;
	data=fopen("random.txt","r");
	while(fscanf(data,"%d",&a[i])!=EOF){
		i++;
	}
	fclose(data);
}
  • 第三種
    把數組的前k個數先放入優先級隊列(底層實現是小根堆,STL模板庫有現成的庫函數), 從第k+1個數開始掃描。
    (1) 如果當前數比優先級隊列的隊列頭(小根堆的堆頂)大,說明當前數需要放入優先級隊列,這時先彈出優先級隊列的隊列頭,然後將當前數插入優先級隊列。
    (2) 如果當前數比優先級隊列的隊列頭(小根堆的堆頂)小,說明當前數不會是top-k中的數,直接濾過,處理下一個數。

  • 在放入優先級隊列比較時,小根堆是最優的比較方法,時間複雜度僅爲堆的高度h=log(2)k(即以2爲底k的對數),總體時間複雜度再乘以外界的n,即o(n*log(2)k)。
    本代碼使用隊列內逐個比較方法,所以時間複雜度爲o(kn)。

  • c語言實現如下

#include<stdio.h>
#include<time.h>
#include<string.h>

int Read();
int i=0,a[1000000];

int main(){
	clock_t start,end;
	start=clock();
	float runtime;
	int h[10]={0};
	int k=0,x=0,j=0;
	Read();
	for(int n=0;n<1000000;n++){
		j=0;
		while(a[j]){
			if(a[j]>h[0]){
				h[0]=a[j];
				for(k=0;k<9;k++){
					if(h[k]>h[k+1]){
						x=h[k];
						h[k]=h[k+1];
						h[k+1]=x;
					}
				}
			}
			j++;
		}
		printf("第%d次Top-10=",n+1);
		for(k=9;k>=0;k--){
			printf("%d ",h[k]);
			h[k]=0;
		}
		printf("\n");
	}
	end=clock();
	runtime=(double)(end-start);
	printf("程序運行時間爲%f毫秒(ms)\n",runtime);
	return 0;
}

int Read(){
	FILE *data;
	data=fopen("random.txt","r");
	while(fscanf(data,"%d",&a[i])!=EOF){
		i++;
	}
	fclose(data);
}

備註

  • 打印數據需要花費很長時間,如果只打算比較各算法的運行時間,可以將打印的部分註釋掉。
  • 計算運行時間需要調用time.h,並有計算時長代碼如下
#include<time.h>
clock_t start,end;
start=clock();
float runtime;
end=clock();
runtime=(double)(end-start);
printf("程序運行時間爲%f毫秒(ms)\n",runtime);
  • 打開一個文件,並讀取其中數據的函數實現如下
int i=0,a[1000000];
int Read(){
	FILE *data;
	data=fopen("random.txt","r");
	while(fscanf(data,"%d",&a[i])!=EOF){
		i++;
	}
	fclose(data);
}
  • random.txt文件中包含的是諸如下列形式的許多整數,整數大小與數量無要求,可以自行生成。
12960
560
28628
9692
2842
30927
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章