解決したい疑問点
環境は、Windows7, eclipse を使っています。
一方向連結リストをソートするプログラムに、未ソートデータとソート済データを入力した場合について、プログラムの実行時間を比較したところ、未ソートデータのほうが時間がかかったのですが、なぜそうなるのかがわかりません。
下記ソースコードの概要は、
・gpa,credit,name の3要素から構成される生徒の成績データが、input.txtファイルに
....................
3.5 40 Taro
2.1 60 Hanako
.
.
....................
のような形で数万人分記録されていて、それを input 関数で構造体の連結リストに入力する。
・input.txtファイルとoutput.txtファイルの場所はコマンドライン引数で入力。
・gpa,credit,name のいずれでソートするかもコマンドライン引数で入力。今回は name でソート。
・ソートは昇順。最大値抽出法を使用。
・ソート部分の実行時間を知るため、sort 関数を clock 関数で囲んでいる。
・入力データには、バラバラの未ソートデータおよび、すでにソート済のデータの2つを使用し、比較する。
該当のソースコード
C
1 2#include <stdio.h> 3#include <stdlib.h> 4#include <string.h> 5#include <time.h> 6 7typedef struct school_record SRec; 8struct school_record{ 9 float gpa; 10 int credit; 11 char name[200]; 12 SRec *next; 13}; 14 15SRec* input(SRec* p, char* fname1); 16void output(SRec* p, char* fname2); 17int gpa_comp(const void* val1, const void* val2); 18int credit_comp(const void* val1, const void* val2); 19int name_comp(const void* val1, const void* val2); 20SRec* listsort(SRec* header, int (*comp)(const void*, const void*)); 21SRec* sort(SRec* header, char* key); 22 23 24 25int main(int argc, char *argv[]) 26{ 27 SRec* header = NULL, *Sheader; 28 clock_t clk_start, clk_end; 29 30 // コマンドライン引数チェック 31 if(argc != 4){ 32 fputs("コマンドライン引数の数が間違っています.\n", stderr); 33 exit(0); 34 } 35 if(strcmp(argv[1],"gpa") && strcmp(argv[1],"credit") && strcmp(argv[1],"name")){ 36 fputs("1つめのコマンドライン引数は, gpa, credit, name のいずれかにしてください.\n", stderr); 37 exit(0); 38 } 39 40 header = input(header, argv[2]); 41 42 clk_start = clock(); // sort関数の処理時間を計測 43 Sheader = sort(header, argv[1]); 44 clk_end = clock(); 45 46 output(Sheader, argv[3]); 47 48 printf("%f", (double)(clk_end - clk_start) / CLOCKS_PER_SEC); 49 50 return 0; 51} 52 53 54 55 56SRec* input(SRec* header, char* fname1) 57{ 58 float gpa; 59 int credit; 60 char name[200]; 61 SRec *p; 62 SRec **tail = &header; 63 64 // ファイルのオープン 65 FILE *fp; 66 if ((fp = fopen(fname1, "r")) == NULL){ 67 fprintf(stderr, "%sのオープンに失敗しました.\n", fname1); 68 exit(1); 69 } 70 71 // データをリストへコピー 72 while( fscanf(fp, "%f%d%s", &gpa, &credit, name) != EOF){ 73 74 // 領域確保 75 if( (p = (SRec*)malloc(sizeof(SRec)) ) == NULL){ 76 fprintf(stderr, "データ領域の確保に失敗しました.\n"); 77 exit(1); 78 } 79 // 一時変数からリストへ書き込み 80 p->gpa = gpa; 81 p->credit = credit; 82 strcpy(p->name, name); 83 p->next = NULL; 84 85 // リストの末尾に要素を追加 (tail は header か next を指すポインタ) 86 *tail = p; 87 tail = &(p->next); 88 } 89 fclose(fp); 90 return header; 91} 92 93 94 95void output(SRec* p, char* fname2) 96{ 97 SRec* tmp; 98 99 // ファイルのオープン 100 FILE *fp; 101 if ((fp = fopen(fname2, "w")) == NULL){ 102 fprintf(stderr, "%sのオープンに失敗しました.\n", fname2); 103 exit(1); 104 } 105 // データの書き込み 106 while( p != NULL ){ 107 fprintf(fp, "%.1f %3d %s\n", p->gpa, p->credit, p->name); 108 tmp = p; 109 p = p->next; 110 free(tmp); 111 } 112 fclose(fp); 113} 114 115 116 117 118 119 120// gpa比較関数 121int gpa_comp(const void* val1, const void* val2) 122{ 123 return ( (*(SRec*)val1).gpa < (*(SRec*)val2).gpa ) ? -1 : ( (*(SRec*)val1).gpa == (*(SRec*)val2).gpa ) ? 0 : 1 ; 124} 125 126// credit比較関数 127int credit_comp(const void* val1, const void* val2) 128{ 129 return ( (*(SRec*)val1).credit < (*(SRec*)val2).credit ) ? -1 : ( (*(SRec*)val1).credit == (*(SRec*)val2).credit ) ? 0 : 1 ; 130} 131 132// name比較関数 133int name_comp(const void* val1, const void* val2) 134{ 135 return strcmp( (*(SRec*)val1).name, (*(SRec*)val2).name); 136} 137 138 139 140// ソートを行う関数 141SRec* listsort(SRec* header, int (*comp)(const void*, const void*)) 142{ 143 SRec* Sheader = NULL, *q; 144 SRec** p, **max; //header か next を指すポインタ 145 146 // 元リストが空になるまで以下を繰り返し 147 while( header != NULL ){ 148 p = &header, max = &header; 149 150 // 元リストの最大値を探す 151 while( *p != NULL ){ 152 if( comp(*max, *p) < 0 ){ 153 max = p; 154 } 155 p = &( (*p)->next ); 156 } 157 // 元リストの最大値を削除して、新リストの先頭に挿入 158 q = Sheader; 159 Sheader = *max; 160 *max = (*max)->next; // 削除完了 161 Sheader->next = q; // 挿入完了 162 } 163 return Sheader; 164} 165 166 167// keyに対応する比較関数をlistsort関数へ振り分ける 168SRec* sort(SRec* header, char* key) 169{ 170 SRec* Sheader = NULL; 171 172 if(strcmp(key, "gpa") == 0){ 173 Sheader = listsort(header, gpa_comp); 174 } 175 if(strcmp(key, "credit") == 0){ 176 Sheader = listsort(header, credit_comp); 177 } 178 if(strcmp(key, "name") == 0){ 179 Sheader = listsort(header, name_comp); 180 } 181 182 return Sheader; 183} 184 185 186 187 188
結果
入力データ
データ数 未ソート ソート済
.............................................
10000個 0.70秒 0.76秒
20000個 3.02秒 2.91秒
40000個 13.58秒 12.96秒
50000個 24.72秒 21.22秒
60000個 41.66秒 34.53秒
80000個 86.76秒 68.59秒
100000個 147.48秒 111.96秒
..............................................
となりました。ソートは問題なく行われます。
データ数が多くなると、ソート済データのほうが実行時間がかなり早いです。
なぜなのか
listsort 関数内に関して、ループを回す回数は未ソートとソート済で変わらないはずです。未ソートとソート済で違うところがあるとすれば、それは
C
1// 元リストの最大値を探す 2 while( *p != NULL ){ 3 if( comp(*max, *p) < 0 ){ 4 max = p; 5 } 6 p = &( (*p)->next ); 7 }
のif文が、未ソートの場合は実行される時とされない時があり、ソート済の場合は毎回実行される、ということだと思います。
ですから、むしろソート済のほうが実行時間が長くなるはずだと思うのですが・・・
なぜ未ソートのほうが時間がかかるのか、教えてください。
回答4件
あなたの回答
tips
プレビュー
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2018/06/27 03:35