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