From d47bb421901edda89cdf726e423834a1452758b3 Mon Sep 17 00:00:00 2001 From: Sn4Rs Date: Tue, 22 Apr 2025 12:50:33 +0800 Subject: [PATCH 01/41] test push --- homework1/src/sort.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homework1/src/sort.cpp b/homework1/src/sort.cpp index 371b9d7..6fbe9c8 100644 --- a/homework1/src/sort.cpp +++ b/homework1/src/sort.cpp @@ -7,7 +7,7 @@ using namespace std; using namespace std::chrono; -#define CASE_ITEMS 1000 // number of items in each case +#define CASE_ITEMS 2000 // number of items in each case #define CASES 10 // number of cases #define RNGKEYS rand() % CASE_ITEMS #define INSKEYS CASE_ITEMS - i // worst case for insertion sort From 05552d0449649dc96edb1196928fac8c5aee8110 Mon Sep 17 00:00:00 2001 From: Sn4Rs Date: Tue, 22 Apr 2025 12:59:27 +0800 Subject: [PATCH 02/41] Revert "Update report.md" This reverts commit 7a7249fee11aeba87feb151a3374dfcc71ec0c9f. --- homework1/report.md | 1 - 1 file changed, 1 deletion(-) diff --git a/homework1/report.md b/homework1/report.md index 32b43b5..fa76d17 100644 --- a/homework1/report.md +++ b/homework1/report.md @@ -11,7 +11,6 @@ --- ## 申論及開發報告 -## 測試與驗證 ## 效能分析 ## 時鐘精度 From 501e8fb98ad21235640173d09356d0ca537e4e4f Mon Sep 17 00:00:00 2001 From: Sn4Rs Date: Tue, 22 Apr 2025 19:37:21 +0800 Subject: [PATCH 03/41] changed insertion sort to pass value --- homework1/src/sort.cpp | 105 +++++++++++++++++++++++++---------------- 1 file changed, 64 insertions(+), 41 deletions(-) diff --git a/homework1/src/sort.cpp b/homework1/src/sort.cpp index 6fbe9c8..660712f 100644 --- a/homework1/src/sort.cpp +++ b/homework1/src/sort.cpp @@ -7,8 +7,8 @@ using namespace std; using namespace std::chrono; -#define CASE_ITEMS 2000 // number of items in each case -#define CASES 10 // number of cases +#define CASE_ITEMS 5 // number of items in each case +#define CASES 5 // number of cases #define RNGKEYS rand() % CASE_ITEMS #define INSKEYS CASE_ITEMS - i // worst case for insertion sort #define UNSORTED "./tosort.txt" @@ -43,33 +43,40 @@ class entry node *data; //only includes an int atm }; +struct result +{ + int64_t timer; + vector arr2; +}; + void makeCases(int, vector> &, FILE *, string); // forward declare -// sort a case of array with insertion -void InsertionCore(entry *temp,vectorarr, int i) // core insertion sort function -{ - entry a = *temp; // create copy - int pos = i; - while(i >= 0 && a.key < arr[i].key) - { - arr[i + 1] = arr[i]; // shift the larger element to the right - i--; // move left in the array - } - arr[i+1] = a; // insert entry -} -// returns runtime in microseconds -unsigned long InsertionSort(vector arr, int casenum) + +result InsertionSort(vector arr, int casenum) { + result r; + cout << "Start insertion sort\n"; auto timer = high_resolution_clock::now(); - for (int j = 2; j < arr.size();j++) + + for (int i = 1; i < arr.size(); i++) // start from 1, first element is sorted { - entry*temp = &arr[j]; - InsertionCore(temp,arr,j-1); // core insertion sort function + entry temp = arr[i]; // store current element + int j = i - 1; // compare with previous elements + + while (j >= 0 && temp.key < arr[j].key) + { + arr[j + 1] = arr[j]; // shift element to the right + j--; + } + arr[j + 1] = temp; } - auto stop = high_resolution_clock::now(); + + auto stop = high_resolution_clock::now(); auto dur = duration_cast(stop - timer); - // output performance record + r.arr2 = arr; // sorted array to result struct + r.timer = dur.count(); // record time in microseconds + cout << "Sorted array in " << dur.count() << " microseconds\n"; FILE *file = fopen(TIMEREC, "a"); fprintf(file, "Sorted case #%d with %lu items in %ld microseconds with Insertion\n", @@ -78,7 +85,7 @@ unsigned long InsertionSort(vector arr, int casenum) { fclose(file); } - return dur.count(); + return r; // return result struct } void QuickSortCore(vectorarr,int left,int right) @@ -106,14 +113,20 @@ void QuickSortCore(vectorarr,int left,int right) } } -unsigned long QuickSort(vector arr, int casenum) // provide arr and specify the case num for print -{ // returns run time in microseconds +result QuickSort(vector arr, int casenum) // provide arr and specify the case num for print +{ + result r; + cout << "Start quick sort\n"; auto timer = high_resolution_clock::now(); // record run time dur QuickSortCore(arr,0,arr.size()-1); //actual sort from 0 to n - 1 auto stop = high_resolution_clock::now(); auto dur = duration_cast(stop - timer); + + r.arr2 = arr; // sorted array to result struct + r.timer = dur.count(); + // output performance record cout << "Sorted array in " << dur.count() << " microseconds\n"; FILE *file = fopen(TIMEREC, "a"); @@ -121,7 +134,7 @@ unsigned long QuickSort(vector arr, int casenum) // provide arr and speci casenum, CASE_ITEMS, dur.count()); if(file!=nullptr) fclose(file); - return dur.count(); + return r; } vector MergeCore(vector a, vector b) // core sorting func, uses iterators @@ -179,22 +192,27 @@ vector MergeCut(vectorarr) //cut array func return MergeCore(left, right); //run sorting and join sorted parts (bottom layer: 1+1=2) } -unsigned long MergeSort(vectorarr, int casenum) //run and record time +result MergeSort(vectorarr, int casenum) //run and record time { + result r; + cout << "Start merge sort\n"; auto timer = high_resolution_clock::now(); // record run time dur arr = MergeCut(arr); // start sorting, assign sorted array back to arr auto stop = high_resolution_clock::now(); auto dur = duration_cast(stop - timer); - // output performance record + + r.arr2 = arr; // sorted array to result struct + r.timer = dur.count(); // record time in microseconds + cout << "Sorted array in " << dur.count() << " microseconds\n"; FILE *file = fopen(TIMEREC, "a"); fprintf(file, "Sorted case #%d with %lu items in %ld microseconds with Merge\n", casenum, CASE_ITEMS, dur.count()); if(file!=nullptr) fclose(file); - return dur.count(); + return r; } void heapify(vector arr, int n, int i) //check for largest root i until n @@ -216,8 +234,10 @@ void heapify(vector arr, int n, int i) //check for largest root i until n } } -unsigned long HeapSort(vectorarr, int casenum) +result HeapSort(vectorarr, int casenum) { + result r; + cout << "Start heap sort\n"; auto timer = high_resolution_clock::now(); // record run time dur int n = arr.size(); // get size of array @@ -233,14 +253,17 @@ unsigned long HeapSort(vectorarr, int casenum) auto stop = high_resolution_clock::now(); auto dur = duration_cast(stop - timer); - // output performance record + + r.arr2 = arr; // sorted array to result struct + r.timer = dur.count(); // record time in microseconds + cout << "Sorted array in " << dur.count() << " microseconds\n"; FILE *file = fopen(TIMEREC, "a"); fprintf(file, "Sorted case #%d with %lu items in %ld microseconds with Heap\n", casenum, CASE_ITEMS, dur.count()); if(file!=nullptr) fclose(file); - return dur.count(); + return r; } int main(void) @@ -257,6 +280,7 @@ int main(void) srand(time(0)); // set random vector> superarray[4]; // all cases stored here + result result; //MAKE CASES HERE///////////////////////////////////// makeCases(CASES, superarray[0], f_Unsorted, "INSERTION"); makeCases(CASES, superarray[1], f_Unsorted, "QUICK"); @@ -264,33 +288,32 @@ int main(void) makeCases(CASES, superarray[3], f_Unsorted, "HEAP"); // create cases, provide file path for unsorted cases ////////////////////////////////////////////////////// //SORT ALL///////////////////////////////////////////////////////////////// - for (int m = 0; m < 4;m++){ //type m - for (int i = 0, c = 1; i < superarray[m].size(); i++, c++) // sort case c in array i in SA of type m + for (int type = 0; type < 4;type++){ //type m + for (int i = 0, caseNum = 1; i < superarray[type].size(); i++, caseNum++) // sort case c in array i in SA of type m { auto duration = 0; - - switch (m) + switch (type) { case 0: - duration = InsertionSort(superarray[m][i], c); + result = InsertionSort(superarray[type][i], caseNum); break; case 1: - duration = QuickSort(superarray[m][i], c); + result = QuickSort(superarray[type][i], caseNum); break; case 2: - duration = MergeSort(superarray[m][i], c); + result = MergeSort(superarray[type][i], caseNum); break; case 3: - duration = HeapSort(superarray[m][i], c); + result = HeapSort(superarray[type][i], caseNum); break; default: break; } //OUTPUT TIME - fprintf(f_Sorted, "\nCase %d of %lu items finished in %lu microseconds\n", c, CASE_ITEMS, duration); + fprintf(f_Sorted, "\nCase %d of %lu items finished in %lu microseconds\n", caseNum, CASE_ITEMS, duration); for (int j = 0; j < CASE_ITEMS; j++) // output keys of sorted array { - superarray[m][i][j].outputkey(f_Sorted); // i for array, j for entry + result.arr2[j].outputkey(f_Sorted); // i for array, j for entry } cout << "output sorted array to file" << SORTED << endl; } From 7b1d020dcc0994482f159ffda4c57167ddc37052 Mon Sep 17 00:00:00 2001 From: Sn4Rs Date: Tue, 22 Apr 2025 20:26:24 +0800 Subject: [PATCH 04/41] update quicksort --- homework1/src/sort.cpp | 55 +++++++++++++++++++++--------------------- 1 file changed, 28 insertions(+), 27 deletions(-) diff --git a/homework1/src/sort.cpp b/homework1/src/sort.cpp index 660712f..9cc10a5 100644 --- a/homework1/src/sort.cpp +++ b/homework1/src/sort.cpp @@ -7,7 +7,7 @@ using namespace std; using namespace std::chrono; -#define CASE_ITEMS 5 // number of items in each case +#define CASE_ITEMS 20 // number of items in each case #define CASES 5 // number of cases #define RNGKEYS rand() % CASE_ITEMS #define INSKEYS CASE_ITEMS - i // worst case for insertion sort @@ -88,29 +88,26 @@ result InsertionSort(vector arr, int casenum) return r; // return result struct } -void QuickSortCore(vectorarr,int left,int right) +vector QuickSortCore(vector arr, int left, int right) { - - // Start sort if (left < right) { - entry *pivot = &arr[left]; + entry pivot = arr[left]; // worst pivot always largest int i = left, j = right + 1; do { - do - i++; - while (arr[i].key < pivot->key); // left push towards mid - do - j--; - while (arr[j].key > pivot->key); - if (i < j) - swap(arr[i], arr[j]); // found both elements belonging to other side + do i++; while ( arr[i].key < pivot.key); // left push towards mid + do j--; while (arr[j].key > pivot.key); + if (i < j) swap(arr[i], arr[j]); // found both elements belonging to other side } while (i < j); - swap(arr[left], arr[j]); // swap pivot with last element of left partition - QuickSortCore(arr, left, j - 1); // left partition - QuickSortCore(arr, j + 1, right); // right partition - } + swap(arr[left], arr[j]); // swap pivot with j + + arr = QuickSortCore(arr, left, j - 1); //new instance of left part arr + arr = QuickSortCore(arr, j + 1, right); + + + } + return arr; } result QuickSort(vector arr, int casenum) // provide arr and specify the case num for print @@ -119,12 +116,12 @@ result QuickSort(vector arr, int casenum) // provide arr and specify the cout << "Start quick sort\n"; auto timer = high_resolution_clock::now(); // record run time dur - QuickSortCore(arr,0,arr.size()-1); //actual sort from 0 to n - 1 + r.arr2 = QuickSortCore(arr,0,arr.size()-1); //actual sort from 0 to n - 1 auto stop = high_resolution_clock::now(); auto dur = duration_cast(stop - timer); - r.arr2 = arr; // sorted array to result struct + //arr2 already stored, skip r.timer = dur.count(); // output performance record @@ -294,17 +291,21 @@ int main(void) auto duration = 0; switch (type) { - case 0: - result = InsertionSort(superarray[type][i], caseNum); + case 0: + result = InsertionSort(superarray[type][i], caseNum); + duration = result.timer; break; - case 1: - result = QuickSort(superarray[type][i], caseNum); + case 1: + result = QuickSort(superarray[type][i], caseNum); + duration = result.timer; break; - case 2: - result = MergeSort(superarray[type][i], caseNum); + case 2: + result = MergeSort(superarray[type][i], caseNum); + duration = result.timer; break; - case 3: - result = HeapSort(superarray[type][i], caseNum); + case 3: + result = HeapSort(superarray[type][i], caseNum); + duration = result.timer; break; default: break; From 002b4b554005a67e00956f7d08f30d157b6d56c8 Mon Sep 17 00:00:00 2001 From: Sn4Rs <93093078+Sn4Rs@users.noreply.github.com> Date: Tue, 22 Apr 2025 20:38:06 +0800 Subject: [PATCH 05/41] Update report.md for actions --- homework1/report.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/homework1/report.md b/homework1/report.md index fa76d17..824d25f 100644 --- a/homework1/report.md +++ b/homework1/report.md @@ -7,11 +7,13 @@ ### 解題說明 ### 效能分析 ### 程式實作 +### 測試與驗證 --- ## 申論及開發報告 ## 效能分析 +## 測試與驗證 ## 時鐘精度 From eae5616df006f48a533e63c5b63fd45d52788249 Mon Sep 17 00:00:00 2001 From: Sn4Rs Date: Tue, 22 Apr 2025 20:54:51 +0800 Subject: [PATCH 06/41] update heapsort --- homework1/src/sort.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/homework1/src/sort.cpp b/homework1/src/sort.cpp index 9cc10a5..071c95f 100644 --- a/homework1/src/sort.cpp +++ b/homework1/src/sort.cpp @@ -7,7 +7,7 @@ using namespace std; using namespace std::chrono; -#define CASE_ITEMS 20 // number of items in each case +#define CASE_ITEMS 10 // number of items in each case #define CASES 5 // number of cases #define RNGKEYS rand() % CASE_ITEMS #define INSKEYS CASE_ITEMS - i // worst case for insertion sort @@ -212,7 +212,7 @@ result MergeSort(vectorarr, int casenum) //run and record time return r; } -void heapify(vector arr, int n, int i) //check for largest root i until n +vector heapify(vector arr, int n, int i) //check for largest root i until n { int max = i; int left = 2 * i + 1; @@ -227,8 +227,9 @@ void heapify(vector arr, int n, int i) //check for largest root i until n if (max != i) // swap and continue heapifying if root is not largest { swap(arr[i], arr[max]); - heapify(arr, n, max); //redo on subtree + return heapify(arr, n, max); //redo on subtree } + else return arr; } result HeapSort(vectorarr, int casenum) @@ -240,12 +241,12 @@ result HeapSort(vectorarr, int casenum) int n = arr.size(); // get size of array for (int i = n / 2 - 1; i >= 0; i--) // build max heap - heapify(arr, n, i); + arr = heapify(arr, n, i); for (int i = n - 1; i > 0; i--) // extract elements from heap one by one { swap(arr[0], arr[i]); // swap root to end - heapify(arr, i, 0); // heapify from 0 to i (reduced size) + arr = heapify(arr, i, 0); // heapify from 0 to i (reduced size) } auto stop = high_resolution_clock::now(); From b687a463efe920c9d9bf256e4a461ca2b4093ada Mon Sep 17 00:00:00 2001 From: Sn4Rs Date: Tue, 22 Apr 2025 21:18:18 +0800 Subject: [PATCH 07/41] update quick / heap with pass reference, sort function still passes value --- homework1/src/sort.cpp | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/homework1/src/sort.cpp b/homework1/src/sort.cpp index 071c95f..e099430 100644 --- a/homework1/src/sort.cpp +++ b/homework1/src/sort.cpp @@ -7,7 +7,7 @@ using namespace std; using namespace std::chrono; -#define CASE_ITEMS 10 // number of items in each case +#define CASE_ITEMS 10000 // number of items in each case #define CASES 5 // number of cases #define RNGKEYS rand() % CASE_ITEMS #define INSKEYS CASE_ITEMS - i // worst case for insertion sort @@ -88,7 +88,7 @@ result InsertionSort(vector arr, int casenum) return r; // return result struct } -vector QuickSortCore(vector arr, int left, int right) +void QuickSortCore(vector &arr, int left, int right) { if (left < right) { @@ -102,12 +102,10 @@ vector QuickSortCore(vector arr, int left, int right) } while (i < j); swap(arr[left], arr[j]); // swap pivot with j - arr = QuickSortCore(arr, left, j - 1); //new instance of left part arr - arr = QuickSortCore(arr, j + 1, right); - - + QuickSortCore(arr, left, j - 1); //new instance of left part arr + QuickSortCore(arr, j + 1, right); } - return arr; + } result QuickSort(vector arr, int casenum) // provide arr and specify the case num for print @@ -116,12 +114,12 @@ result QuickSort(vector arr, int casenum) // provide arr and specify the cout << "Start quick sort\n"; auto timer = high_resolution_clock::now(); // record run time dur - r.arr2 = QuickSortCore(arr,0,arr.size()-1); //actual sort from 0 to n - 1 + QuickSortCore(arr,0,arr.size()-1); //actual sort from 0 to n - 1 auto stop = high_resolution_clock::now(); auto dur = duration_cast(stop - timer); - //arr2 already stored, skip + r.arr2 = arr; r.timer = dur.count(); // output performance record @@ -212,7 +210,7 @@ result MergeSort(vectorarr, int casenum) //run and record time return r; } -vector heapify(vector arr, int n, int i) //check for largest root i until n +void heapify(vector &arr, int n, int i) //check for largest root i until n { int max = i; int left = 2 * i + 1; @@ -227,9 +225,9 @@ vector heapify(vector arr, int n, int i) //check for largest root if (max != i) // swap and continue heapifying if root is not largest { swap(arr[i], arr[max]); - return heapify(arr, n, max); //redo on subtree + heapify(arr, n, max); //redo on subtree } - else return arr; + } result HeapSort(vectorarr, int casenum) @@ -241,12 +239,12 @@ result HeapSort(vectorarr, int casenum) int n = arr.size(); // get size of array for (int i = n / 2 - 1; i >= 0; i--) // build max heap - arr = heapify(arr, n, i); + heapify(arr, n, i); for (int i = n - 1; i > 0; i--) // extract elements from heap one by one { swap(arr[0], arr[i]); // swap root to end - arr = heapify(arr, i, 0); // heapify from 0 to i (reduced size) + heapify(arr, i, 0); // heapify from 0 to i (reduced size) } auto stop = high_resolution_clock::now(); From c0c430f47d553f956bf82f94f72bf910c1ed6c57 Mon Sep 17 00:00:00 2001 From: Sn4Rs Date: Wed, 23 Apr 2025 22:03:51 +0800 Subject: [PATCH 08/41] added memory usage recording --- homework1/src/sort.cpp | 99 ++++++++++++++++++++++++++++++------------ 1 file changed, 71 insertions(+), 28 deletions(-) diff --git a/homework1/src/sort.cpp b/homework1/src/sort.cpp index e099430..8f924f7 100644 --- a/homework1/src/sort.cpp +++ b/homework1/src/sort.cpp @@ -4,10 +4,13 @@ #include #include #include +#include +#include +#pragma comment( lib, "psapi.lib" ) using namespace std; -using namespace std::chrono; +//using namespace std::chrono; -#define CASE_ITEMS 10000 // number of items in each case +#define CASE_ITEMS 100 // number of items in each case #define CASES 5 // number of cases #define RNGKEYS rand() % CASE_ITEMS #define INSKEYS CASE_ITEMS - i // worst case for insertion sort @@ -51,14 +54,31 @@ struct result void makeCases(int, vector> &, FILE *, string); // forward declare +string printMem(int state) +{ + PROCESS_MEMORY_COUNTERS memInfo; + GetProcessMemoryInfo(GetCurrentProcess(), &memInfo, sizeof(memInfo)); + unsigned long long memUsage = memInfo.WorkingSetSize / 1024; // in KB + unsigned long long memPeak = memInfo.PeakWorkingSetSize / 1024; + string ret0 = "Memory usage: " + to_string(memUsage) + " KB\n"; + string ret1 = "Peak memory usage: " + to_string(memPeak) + " KB\n"; + if(state == 0) return ret0; + else if(state == 1) return ret1; + + return ""; // Default return +} result InsertionSort(vector arr, int casenum) { result r; cout << "Start insertion sort\n"; - auto timer = high_resolution_clock::now(); - + auto timer = chrono::high_resolution_clock::now(); + string recMem_Init = printMem(0); // store initial memory usage + cout << recMem_Init << endl; + FILE *file = fopen(TIMEREC, "a"); + fprintf(file,"Start insertion sort case # %d \n[Init] %s", casenum, recMem_Init.c_str()); + for (int i = 1; i < arr.size(); i++) // start from 1, first element is sorted { entry temp = arr[i]; // store current element @@ -72,15 +92,18 @@ result InsertionSort(vector arr, int casenum) arr[j + 1] = temp; } - auto stop = high_resolution_clock::now(); - auto dur = duration_cast(stop - timer); + auto stop = chrono::high_resolution_clock::now(); + auto dur = chrono::duration_cast(stop - timer); r.arr2 = arr; // sorted array to result struct r.timer = dur.count(); // record time in microseconds + string recMem_Fin = printMem(1); // store final peak mem usage cout << "Sorted array in " << dur.count() << " microseconds\n"; - FILE *file = fopen(TIMEREC, "a"); - fprintf(file, "Sorted case #%d with %lu items in %ld microseconds with Insertion\n", - casenum, CASE_ITEMS, dur.count()); + cout << recMem_Fin << endl; + + // output performance record + fprintf(file, "Sorted case #%d with %lu items in %ld microseconds with Insertion\n[Final] %s\n", + casenum, CASE_ITEMS, dur.count(), recMem_Fin.c_str()); // output performance record if (file != nullptr) { fclose(file); @@ -113,20 +136,26 @@ result QuickSort(vector arr, int casenum) // provide arr and specify the result r; cout << "Start quick sort\n"; - auto timer = high_resolution_clock::now(); // record run time dur + auto timer = chrono::high_resolution_clock::now(); // record run time dur + string recMem_Init = printMem(0); // store initial memory usage + cout << recMem_Init << endl; + FILE *file = fopen(TIMEREC, "a"); + fprintf(file,"Start Quick sort case # %d \n[Init] %s", casenum, recMem_Init.c_str()); + QuickSortCore(arr,0,arr.size()-1); //actual sort from 0 to n - 1 - auto stop = high_resolution_clock::now(); - auto dur = duration_cast(stop - timer); + auto stop = chrono::high_resolution_clock::now(); + auto dur = chrono::duration_cast(stop - timer); r.arr2 = arr; r.timer = dur.count(); + string recMem_Fin = printMem(1); // store final peak mem usage // output performance record cout << "Sorted array in " << dur.count() << " microseconds\n"; - FILE *file = fopen(TIMEREC, "a"); - fprintf(file, "Sorted case #%d with %lu items in %ld microseconds with Quick\n", - casenum, CASE_ITEMS, dur.count()); + cout << recMem_Fin << endl; + fprintf(file, "Sorted case #%d with %lu items in %ld microseconds with Quick\n[Final] %s\n", + casenum, CASE_ITEMS, dur.count(), recMem_Fin.c_str()); if(file!=nullptr) fclose(file); return r; @@ -192,19 +221,26 @@ result MergeSort(vectorarr, int casenum) //run and record time result r; cout << "Start merge sort\n"; - auto timer = high_resolution_clock::now(); // record run time dur + auto timer = chrono::high_resolution_clock::now(); // record run time dur + string recMem_Init = printMem(0); // store initial memory usage + cout << recMem_Init << endl; + FILE *file = fopen(TIMEREC, "a"); + fprintf(file,"Start Merge sort case # %d \n[Init] %s", casenum, recMem_Init.c_str()); + arr = MergeCut(arr); // start sorting, assign sorted array back to arr - auto stop = high_resolution_clock::now(); - auto dur = duration_cast(stop - timer); + auto stop = chrono::high_resolution_clock::now(); + auto dur = chrono::duration_cast(stop - timer); r.arr2 = arr; // sorted array to result struct r.timer = dur.count(); // record time in microseconds + string recMem_Fin = printMem(1); // store final peak mem usage + // output performance record cout << "Sorted array in " << dur.count() << " microseconds\n"; - FILE *file = fopen(TIMEREC, "a"); - fprintf(file, "Sorted case #%d with %lu items in %ld microseconds with Merge\n", - casenum, CASE_ITEMS, dur.count()); + cout << recMem_Fin << endl; + fprintf(file, "Sorted case #%d with %lu items in %ld microseconds with Merge\n[Final] %s\n", + casenum, CASE_ITEMS, dur.count(), recMem_Fin.c_str()); if(file!=nullptr) fclose(file); return r; @@ -235,7 +271,12 @@ result HeapSort(vectorarr, int casenum) result r; cout << "Start heap sort\n"; - auto timer = high_resolution_clock::now(); // record run time dur + auto timer = chrono::high_resolution_clock::now(); // record run time dur + string recMem_Init = printMem(0); // store initial memory usage + cout << recMem_Init << endl; + FILE *file = fopen(TIMEREC, "a"); + fprintf(file,"Start Heap sort case # %d \n[Init] %s", casenum, recMem_Init.c_str()); + int n = arr.size(); // get size of array for (int i = n / 2 - 1; i >= 0; i--) // build max heap @@ -247,16 +288,18 @@ result HeapSort(vectorarr, int casenum) heapify(arr, i, 0); // heapify from 0 to i (reduced size) } - auto stop = high_resolution_clock::now(); - auto dur = duration_cast(stop - timer); + auto stop = chrono::high_resolution_clock::now(); + auto dur = chrono::duration_cast(stop - timer); r.arr2 = arr; // sorted array to result struct r.timer = dur.count(); // record time in microseconds - + string recMem_Fin = printMem(1); // store final peak mem usage + + // output performance record cout << "Sorted array in " << dur.count() << " microseconds\n"; - FILE *file = fopen(TIMEREC, "a"); - fprintf(file, "Sorted case #%d with %lu items in %ld microseconds with Heap\n", - casenum, CASE_ITEMS, dur.count()); + cout << recMem_Fin << endl; + fprintf(file, "Sorted case #%d with %lu items in %ld microseconds with Heap\n[Final] %s\n", + casenum, CASE_ITEMS, dur.count(), recMem_Fin.c_str()); if(file!=nullptr) fclose(file); return r; From 2fc1cf135e5cd7cc8f6c6b33b7ef3ebbac100073 Mon Sep 17 00:00:00 2001 From: Sn4Rs Date: Wed, 23 Apr 2025 22:43:17 +0800 Subject: [PATCH 09/41] adjustments in main: timer value moved out of switch case --- homework1/src/sort.cpp | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/homework1/src/sort.cpp b/homework1/src/sort.cpp index 8f924f7..cd2aef5 100644 --- a/homework1/src/sort.cpp +++ b/homework1/src/sort.cpp @@ -10,7 +10,7 @@ using namespace std; //using namespace std::chrono; -#define CASE_ITEMS 100 // number of items in each case +#define CASE_ITEMS 6000 // number of items in each case #define CASES 5 // number of cases #define RNGKEYS rand() % CASE_ITEMS #define INSKEYS CASE_ITEMS - i // worst case for insertion sort @@ -54,7 +54,7 @@ struct result void makeCases(int, vector> &, FILE *, string); // forward declare -string printMem(int state) +string printMem(int state) //input 0 for initial mem usage, 1 for final peak mem usage { PROCESS_MEMORY_COUNTERS memInfo; GetProcessMemoryInfo(GetCurrentProcess(), &memInfo, sizeof(memInfo)); @@ -100,10 +100,10 @@ result InsertionSort(vector arr, int casenum) cout << "Sorted array in " << dur.count() << " microseconds\n"; cout << recMem_Fin << endl; - + // output performance record fprintf(file, "Sorted case #%d with %lu items in %ld microseconds with Insertion\n[Final] %s\n", - casenum, CASE_ITEMS, dur.count(), recMem_Fin.c_str()); // output performance record + casenum, CASE_ITEMS, dur.count(), recMem_Fin.c_str()); if (file != nullptr) { fclose(file); @@ -328,30 +328,27 @@ int main(void) ////////////////////////////////////////////////////// //SORT ALL///////////////////////////////////////////////////////////////// for (int type = 0; type < 4;type++){ //type m - for (int i = 0, caseNum = 1; i < superarray[type].size(); i++, caseNum++) // sort case c in array i in SA of type m + for (int i = 0, caseNum = 1; i < superarray[type].size(); i++, caseNum++) // SA of type m with i cases { auto duration = 0; switch (type) { case 0: result = InsertionSort(superarray[type][i], caseNum); - duration = result.timer; break; case 1: result = QuickSort(superarray[type][i], caseNum); - duration = result.timer; break; case 2: result = MergeSort(superarray[type][i], caseNum); - duration = result.timer; break; case 3: result = HeapSort(superarray[type][i], caseNum); - duration = result.timer; break; default: break; } + duration = result.timer; // get duration from result struct //OUTPUT TIME fprintf(f_Sorted, "\nCase %d of %lu items finished in %lu microseconds\n", caseNum, CASE_ITEMS, duration); for (int j = 0; j < CASE_ITEMS; j++) // output keys of sorted array From b7501bacced9ac6bdeeee2f3948de512f9831b5d Mon Sep 17 00:00:00 2001 From: Yangchenin41243245 <41243245@nfu.edu.tw> Date: Thu, 24 Apr 2025 03:06:36 +0800 Subject: [PATCH 10/41] Update report.md --- homework1/report.md | 90 ++++++++++++++++++++++----------------------- 1 file changed, 45 insertions(+), 45 deletions(-) diff --git a/homework1/report.md b/homework1/report.md index e237f2c..51c2461 100644 --- a/homework1/report.md +++ b/homework1/report.md @@ -1,25 +1,17 @@ -# 41243245 +# 41243214 +# & 41243245 # 排序演算法效能分析報告 ## 目錄 -### 時鐘精度 ### 解題說明 +### 時鐘精度 ### 效能分析 ### 程式實作 ### 測試與驗證 - -## 申論及開發報告 -## 效能分析 -## 測試與驗證 - - -## 時鐘精度 - -本程式使用 C++ 的 `` 標準函式庫中的 `high_resolution_clock` 進行計時,使用 `duration_cast` 將執行時間換算為「微秒」(1 微秒 = 0.000001 秒)。提供足夠高的解析度,以正確測量常見排序演算法在中等規模資料量(如 5000 筆資料)下的執行效能。 +### 申論及開發報告 --- - ## 解題說明 ### Heap Sort 最壞情況測試資料的隨機排列方式 為了模擬 Heap Sort 的最壞情況,本程式在 `makeCases()` 函數中對 HEAP 模式使用了 **隨機排列【Random Permutation】** 方式生成輸入資料: @@ -49,42 +41,13 @@ if (mode == "INSERTION" || mode == "QUICK" || mode == "MERGE") --- -## 效能分析 - -### 平均執行時間表格 - -| 資料數量 (n) | 插入排序 (Insertion) | 快速排序 (Quick) | 合併排序 (Merge) | 堆積排序 (Heap) | -|--------------|-----------------------|-------------------|------------------|-----------------| -| 500 | 419.2 | 151.5 | 1921.1 | 73.8 | -| 1000 | 1688.7 | 609.7 | 4306.6 | 140.9 | -| 2000 | 6238.2 | 2264.2 | 11828.0 | 336.8 | -| 3000 | 14819.7 | 5525.9 | 17096.2 | 562.4 | -| 4000 | 23682.4 | 8861.6 | 24187.3 | 739.1 | -| 5000 | 38788.5 | 14426.6 | 30609.3 | 946.4 | -| 10000 | 144778.2 | 53740.3 | 47289.1 | 2221.4 | - -![image](https://github.com/Yangchenin41243245/Data_Structure_Homework_14_45/blob/main/homework1/src/pics/pic03.png) - -### 最壞執行時間表格 - -| 資料數量 (n) | 插入排序 (Insertion) | 快速排序 (Quick) | 合併排序 (Merge) | 堆積排序 (Heap) | -|--------|-----------------|-----------------|-----------------|-----------------| -| 500 | 623 | 155 | 2643 | 101 | -| 1000 | 2478 | 792 | 5206 | 165 | -| 2000 | 7742 | 2738 | 14552 | 418 | -| 3000 | 17826 | 5405 | 19880 | 754 | -| 4000 | 26746 | 9366 | 31271 | 790 | -| 5000 | 47480 | 18947 | 30609 | 946 | -| 10000 | 150250 | 55224 | 71520 | 2422 | +## 時鐘精度 -![image](https://github.com/Yangchenin41243245/Data_Structure_Homework_14_45/blob/main/homework1/src/pics/pic04.png) +本程式使用 C++ 的 `` 標準函式庫中的 `high_resolution_clock` 進行計時,使用 `duration_cast` 將執行時間換算為「微秒」(1 微秒 = 0.000001 秒)。提供足夠高的解析度,以正確測量常見排序演算法在中等規模資料量(如 5000 筆資料)下的執行效能。 -### 時間複雜度趨勢分析 +--- -- 插入排序:明顯呈現 $O(n^2)$ 成長,效能下降快速。 -- 快速排序:雖然最壞情況為 $O(n^2)$,但在平均情況下趨近 $O(n \log n)$,表現穩定。 -- 合併排序:始終維持 $O(n \log n)$,在大型資料下表現穩定但稍高。 -- 堆積排序:執行時間隨 $n$ 緩慢上升,符合 $O(n \log n)$,在測試中表現最佳。 +## 效能分析 ## 程式實作 @@ -427,6 +390,43 @@ int main(void) ## 測試與驗證 +### 平均執行時間表格 + +| 資料數量 (n) | 插入排序 (Insertion) | 快速排序 (Quick) | 合併排序 (Merge) | 堆積排序 (Heap) | +|--------------|-----------------------|-------------------|------------------|-----------------| +| 500 | 419.2 | 151.5 | 1921.1 | 73.8 | +| 1000 | 1688.7 | 609.7 | 4306.6 | 140.9 | +| 2000 | 6238.2 | 2264.2 | 11828.0 | 336.8 | +| 3000 | 14819.7 | 5525.9 | 17096.2 | 562.4 | +| 4000 | 23682.4 | 8861.6 | 24187.3 | 739.1 | +| 5000 | 38788.5 | 14426.6 | 30609.3 | 946.4 | +| 10000 | 144778.2 | 53740.3 | 47289.1 | 2221.4 | + +![image](https://github.com/Yangchenin41243245/Data_Structure_Homework_14_45/blob/main/homework1/src/pics/pic03.png) + +### 最壞執行時間表格 + +| 資料數量 (n) | 插入排序 (Insertion) | 快速排序 (Quick) | 合併排序 (Merge) | 堆積排序 (Heap) | +|--------|-----------------|-----------------|-----------------|-----------------| +| 500 | 623 | 155 | 2643 | 101 | +| 1000 | 2478 | 792 | 5206 | 165 | +| 2000 | 7742 | 2738 | 14552 | 418 | +| 3000 | 17826 | 5405 | 19880 | 754 | +| 4000 | 26746 | 9366 | 31271 | 790 | +| 5000 | 47480 | 18947 | 30609 | 946 | +| 10000 | 150250 | 55224 | 71520 | 2422 | + +![image](https://github.com/Yangchenin41243245/Data_Structure_Homework_14_45/blob/main/homework1/src/pics/pic04.png) + +### 時間複雜度趨勢分析 + +- 插入排序:明顯呈現 $O(n^2)$ 成長,效能下降快速。 +- 快速排序:雖然最壞情況為 $O(n^2)$,但在平均情況下趨近 $O(n \log n)$,表現穩定。 +- 合併排序:始終維持 $O(n \log n)$,在大型資料下表現穩定但稍高。 +- 堆積排序:執行時間隨 $n$ 緩慢上升,符合 $O(n \log n)$,在測試中表現最佳。 + +--- + ## 申論及開發報告 From 4a3b58528447a46272ad858f49a7899ca840ff19 Mon Sep 17 00:00:00 2001 From: Yangchenin41243245 <41243245@nfu.edu.tw> Date: Thu, 24 Apr 2025 03:18:17 +0800 Subject: [PATCH 11/41] Update report.md --- homework1/report.md | 64 ++++++++++++++++++++++++++++++++------------- 1 file changed, 46 insertions(+), 18 deletions(-) diff --git a/homework1/report.md b/homework1/report.md index 51c2461..8a1c22d 100644 --- a/homework1/report.md +++ b/homework1/report.md @@ -12,38 +12,66 @@ ### 申論及開發報告 --- + ## 解題說明 -### Heap Sort 最壞情況測試資料的隨機排列方式 -為了模擬 Heap Sort 的最壞情況,本程式在 `makeCases()` 函數中對 HEAP 模式使用了 **隨機排列【Random Permutation】** 方式生成輸入資料: -```cpp -for (int i = CASE_ITEMS - 1; i >= 2; i--) -{ - int j = rand() % i + 1; - swap(array[i], array[j]); -} -``` +本作業旨在實作與分析四種排序演算法的效能,並設計一個根據輸入資料特性選擇最適合排序法的綜合排序函式(Composite Sort Function)。作業核心在於針對不同輸入資料大小與特性(最壞情況與平均情況)進行排序效能測試,並比較各排序法在時間與記憶體使用上的表現。 + +本次實作包含以下四種排序方法: + +1. **Insertion Sort** + 逐一將元素插入已排序好的子陣列,最壞情況為反向排序。 + +2. **Quick Sort**(使用 median-of-three 中值選擇 pivot) + 採用分治法將資料切分並遞迴排序,最壞情況發生於切分極不平均時。 + +3. **Merge Sort**(使用迭代式實作) + 將資料拆分後合併排序,最壞情況相對穩定,時間複雜度為 O(n log n)。 -這段程式碼等像於 **Fisher-Yates shuffle** 隨機打亂法,使輸入數列在統計上接近 Heap Sort 的最壞或平均情況。 +4. **Heap Sort** + 利用最大堆結構,每次將最大值移至尾端排序,對於初始排列較亂的資料效能佳。 + +透過 `std::chrono` 計時與 Windows API 測量記憶體使用量,將各排序法在不同 `n` 值下的效能進行數據分析與圖表視覺化,最終統整出各排序法的適用情境與建議使用範圍。 --- -## Merge Sort 最壞情況測試資料生成方式 +## 時鐘精度 + +本次作業中,排序演算法的效能主要透過時間消耗(microseconds)進行衡量。為了準確記錄各排序方法的執行時間,我們使用 C++ 標準函式庫中的 `` 模組中的 `std::chrono::high_resolution_clock` 作為計時工具,其精度可達微秒等級,適合用於微小時間單位的效能分析。 -Merge Sort 的最壞情況並不會明顯受特定輸入順序影響,因此程式中使用了 **遞減排序【由大至小】** 的資料生成方式作為最壞情況的模擬輸入: +### 使用方式 + +計時範例如下: ```cpp -if (mode == "INSERTION" || mode == "QUICK" || mode == "MERGE") - key = CASE_ITEMS - i; +auto start = chrono::high_resolution_clock::now(); +// 排序函式執行 +auto end = chrono::high_resolution_clock::now(); +auto duration = chrono::duration_cast(end - start); ``` -這樣生成的輸入數列為遞減序列,例如 [5000, 4999, 4998, ..., 1],對 Merge Sort 難以顯著抽慮效能,但可視為統一的 worst-case 測試資料來源。 +透過 `chrono::duration_cast` 可精確取得兩個時間點之間的間隔(此處為微秒)。 ---- +### 測量誤差與處理方式 -## 時鐘精度 +由於在小筆數資料(如 n = 500)時,排序執行時間可能低於計時器可穩定測量的範圍,導致計時結果為 0 或波動大。為解決此問題,採用以下策略: + +- **重複執行多次後取平均時間**,以降低測量誤差。例如對某個排序函式執行 1000 次並除以總次數: + + ```cpp + int repeat = 1000; + auto start = chrono::high_resolution_clock::now(); + for (int i = 0; i < repeat; ++i) + yourSortFunction(arr); + auto end = chrono::high_resolution_clock::now(); + auto avg_duration = chrono::duration_cast(end - start).count() / repeat; + ``` + +- 此外,在正式測試中,我們確保對每組測資僅進行一次排序測試(不重複),以模擬實際使用情境。當發現個別排序法耗時極短時,再單獨進行多次測量以取得更精確數據。 + +### 實驗觀察 -本程式使用 C++ 的 `` 標準函式庫中的 `high_resolution_clock` 進行計時,使用 `duration_cast` 將執行時間換算為「微秒」(1 微秒 = 0.000001 秒)。提供足夠高的解析度,以正確測量常見排序演算法在中等規模資料量(如 5000 筆資料)下的執行效能。 +經過測試發現,對於筆數小於 1000 的資料排序,Insertion Sort 執行時間可能會出現 0 μs 的情形。透過上述方法可有效改善這一問題,使測量更為穩定與可信。 --- From aa4967ca51a9484f8da4ccd5545ba46c2208abd3 Mon Sep 17 00:00:00 2001 From: Yangchenin41243245 <41243245@nfu.edu.tw> Date: Thu, 24 Apr 2025 15:03:03 +0800 Subject: [PATCH 12/41] Update report.md --- homework1/report.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homework1/report.md b/homework1/report.md index 8a1c22d..d05a850 100644 --- a/homework1/report.md +++ b/homework1/report.md @@ -1,5 +1,5 @@ # 41243214 -# & 41243245 +& 41243245 # 排序演算法效能分析報告 ## 目錄 From 9b2b8608177d9b63910a49e36ed7e6c4cc257041 Mon Sep 17 00:00:00 2001 From: Yangchenin41243245 <41243245@nfu.edu.tw> Date: Fri, 25 Apr 2025 14:05:40 +0800 Subject: [PATCH 13/41] Add files via upload --- homework1/src/pics/average.png | Bin 0 -> 66810 bytes homework1/src/pics/worst.png | Bin 0 -> 68595 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 homework1/src/pics/average.png create mode 100644 homework1/src/pics/worst.png diff --git a/homework1/src/pics/average.png b/homework1/src/pics/average.png new file mode 100644 index 0000000000000000000000000000000000000000..376e11fb5bdaaa902352ce1c8de4778f53062650 GIT binary patch literal 66810 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 literal 0 HcmV?d00001 diff --git a/homework1/src/pics/worst.png b/homework1/src/pics/worst.png new file mode 100644 index 0000000000000000000000000000000000000000..7c73333f93f7e8b6283a6a8092a64ab43ceb64e8 GIT binary patch literal 68595 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 Date: Fri, 25 Apr 2025 14:08:39 +0800 Subject: [PATCH 14/41] Update report.md --- homework1/report.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/homework1/report.md b/homework1/report.md index d05a850..d68da03 100644 --- a/homework1/report.md +++ b/homework1/report.md @@ -76,6 +76,33 @@ auto duration = chrono::duration_cast(end - start); --- ## 效能分析 +# Sorting Algorithm Performance Tables + +## Worst-case Performance (Time in Microseconds) + +| Data Size | Insertion Sort | Quick Sort | Merge Sort | Heap Sort | +|-----------|----------------|------------|------------|-----------| +| 500 | 847 | 440 | 2326 | 311 | +| 1000 | 2766 | 1106 | 5090 | 320 | +| 2000 | 6395 | 2381 | 15428 | 615 | +| 3000 | 14413 | 5218 | 23567 | 849 | +| 4000 | 24047 | 9181 | 28200 | 1125 | +| 5000 | 39733 | 20349 | 30139 | 1377 | + +![image](https://github.com/Yangchenin41243245/Data_Structure_Homework_14_45/blob/proto2/homework1/src/pics/worst.png) + +## Average-case Performance (Time in Microseconds) + +| Data Size | Insertion Sort | Quick Sort | Merge Sort | Heap Sort | +|-----------|----------------|------------|------------|-----------| +| 500 | 701.0 | 325.8 | 2253.0 | 240.0 | +| 1000 | 1906.4 | 792.6 | 4777.0 | 283.8 | +| 2000 | 6149.2 | 2261.8 | 13970.4 | 540.6 | +| 3000 | 13777.4 | 5050.6 | 20875.4 | 717.6 | +| 4000 | 23150.0 | 8675.4 | 25028.8 | 941.2 | +| 5000 | 38502.8 | 15283.0 | 26602.8 | 1223.2 | + +![image](https://github.com/Yangchenin41243245/Data_Structure_Homework_14_45/blob/proto2/homework1/src/pics/average.png) ## 程式實作 From d25b1a1789641003db50a0d14197543126c99186 Mon Sep 17 00:00:00 2001 From: Yangchenin41243245 <41243245@nfu.edu.tw> Date: Fri, 25 Apr 2025 14:09:23 +0800 Subject: [PATCH 15/41] Update report.md --- homework1/report.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/homework1/report.md b/homework1/report.md index d68da03..3497217 100644 --- a/homework1/report.md +++ b/homework1/report.md @@ -76,9 +76,8 @@ auto duration = chrono::duration_cast(end - start); --- ## 效能分析 -# Sorting Algorithm Performance Tables -## Worst-case Performance (Time in Microseconds) +### Worst-case Performance (Time in Microseconds) | Data Size | Insertion Sort | Quick Sort | Merge Sort | Heap Sort | |-----------|----------------|------------|------------|-----------| @@ -91,7 +90,7 @@ auto duration = chrono::duration_cast(end - start); ![image](https://github.com/Yangchenin41243245/Data_Structure_Homework_14_45/blob/proto2/homework1/src/pics/worst.png) -## Average-case Performance (Time in Microseconds) +### Average-case Performance (Time in Microseconds) | Data Size | Insertion Sort | Quick Sort | Merge Sort | Heap Sort | |-----------|----------------|------------|------------|-----------| From 03360a02e0d7e20773739480a580a6eb55206d03 Mon Sep 17 00:00:00 2001 From: Yangchenin41243245 <41243245@nfu.edu.tw> Date: Fri, 25 Apr 2025 14:40:10 +0800 Subject: [PATCH 16/41] Update report.md --- homework1/report.md | 49 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/homework1/report.md b/homework1/report.md index 3497217..95be4b2 100644 --- a/homework1/report.md +++ b/homework1/report.md @@ -77,6 +77,55 @@ auto duration = chrono::duration_cast(end - start); ## 效能分析 +| 演算法 | Worst-case 時間複雜度 | Average-case 時間複雜度 | 空間複雜度 | +|---------------|-----------------------|-------------------------|------------| +| Insertion Sort | O(n²) | O(n²) | O(1) | +| Quick Sort | O(n²) | O(n log n) | O(log n) | +| Merge Sort | O(n log n) | O(n log n) | O(n) | +| Heap Sort | O(n log n) | O(n log n) | O(1) | + +### 詳細分析 + +#### Insertion Sort +- **時間複雜度**: + - **Worst-case**:O(n²),當輸入為逆序(程式中 `INSKEYS` 生成逆序),每次插入需移動所有前元素。 + - **Average-case**:O(n²),因每次插入平均需比較和移動約 n/2 個元素。 +- **空間複雜度**: + - O(1),原地排序,僅需常數額外空間(`temp` 變數)。 +- **程式特性**:簡單實現,未優化比較或移動,適合小陣列。 + +#### Quick Sort +- **時間複雜度**: + - **Worst-case**:O(n²),當 pivot 選擇不佳(程式中固定選第一元素)且輸入逆序,分割極不平衡。 + - **Average-case**:O(n log n),隨機輸入下,分割接近均等。 +- **空間複雜度**: + - O(log n),因遞迴調用棧(平均 O(log n) 層,Worst-case 可達 O(n))。 +- **程式特性**:固定 pivot(`arr[left]`)導致 Worst-case 頻發,未採用隨機 pivot 或小陣列插入排序優化。 + +#### Merge Sort +- **時間複雜度**: + - **Worst-case**:O(n log n),分割和合併均穩定,與輸入無關。 + - **Average-case**:O(n log n),合併操作固定。 +- **空間複雜度**: + - O(n),需額外陣列儲存合併結果(程式中 `c.reserve(a.size() + b.size())`)。 +- **程式特性**:使用 `std::move` 減少複製開銷,但未實現原地合併或自底向上排序。 + +#### Heap Sort +- **時間複雜度**: + - **Worst-case**:O(n log n),建堆 O(n),每次調整堆 O(log n),共 n 次。 + - **Average-case**:O(n log n),對輸入不敏感。 +- **空間複雜度**: + - O(1),原地排序,僅需常數額外空間。 +- **程式特性**:標準最大堆實現,穩定且高效,無顯著優化空間。 + +### 總結 +- **Insertion Sort**:時間複雜度高(O(n²)),但空間需求最低,適合小資料。 +- **Quick Sort**:Average-case 高效(O(n log n)),但程式實現易退化到 O(n²),空間需遞迴棧。 +- **Merge Sort**:時間穩定(O(n log n)),但空間需求高(O(n))。 +- **Heap Sort**:時間和空間均優(O(n log n), O(1)),為大規模資料最佳選擇。 + +--- + ### Worst-case Performance (Time in Microseconds) | Data Size | Insertion Sort | Quick Sort | Merge Sort | Heap Sort | From 34bf4dffb84b0747f6d15912cf52f97c43b8cc11 Mon Sep 17 00:00:00 2001 From: Yangchenin41243245 <41243245@nfu.edu.tw> Date: Fri, 25 Apr 2025 14:40:46 +0800 Subject: [PATCH 17/41] Update report.md --- homework1/report.md | 35 ----------------------------------- 1 file changed, 35 deletions(-) diff --git a/homework1/report.md b/homework1/report.md index 95be4b2..77b5f1e 100644 --- a/homework1/report.md +++ b/homework1/report.md @@ -77,15 +77,6 @@ auto duration = chrono::duration_cast(end - start); ## 效能分析 -| 演算法 | Worst-case 時間複雜度 | Average-case 時間複雜度 | 空間複雜度 | -|---------------|-----------------------|-------------------------|------------| -| Insertion Sort | O(n²) | O(n²) | O(1) | -| Quick Sort | O(n²) | O(n log n) | O(log n) | -| Merge Sort | O(n log n) | O(n log n) | O(n) | -| Heap Sort | O(n log n) | O(n log n) | O(1) | - -### 詳細分析 - #### Insertion Sort - **時間複雜度**: - **Worst-case**:O(n²),當輸入為逆序(程式中 `INSKEYS` 生成逆序),每次插入需移動所有前元素。 @@ -126,32 +117,6 @@ auto duration = chrono::duration_cast(end - start); --- -### Worst-case Performance (Time in Microseconds) - -| Data Size | Insertion Sort | Quick Sort | Merge Sort | Heap Sort | -|-----------|----------------|------------|------------|-----------| -| 500 | 847 | 440 | 2326 | 311 | -| 1000 | 2766 | 1106 | 5090 | 320 | -| 2000 | 6395 | 2381 | 15428 | 615 | -| 3000 | 14413 | 5218 | 23567 | 849 | -| 4000 | 24047 | 9181 | 28200 | 1125 | -| 5000 | 39733 | 20349 | 30139 | 1377 | - -![image](https://github.com/Yangchenin41243245/Data_Structure_Homework_14_45/blob/proto2/homework1/src/pics/worst.png) - -### Average-case Performance (Time in Microseconds) - -| Data Size | Insertion Sort | Quick Sort | Merge Sort | Heap Sort | -|-----------|----------------|------------|------------|-----------| -| 500 | 701.0 | 325.8 | 2253.0 | 240.0 | -| 1000 | 1906.4 | 792.6 | 4777.0 | 283.8 | -| 2000 | 6149.2 | 2261.8 | 13970.4 | 540.6 | -| 3000 | 13777.4 | 5050.6 | 20875.4 | 717.6 | -| 4000 | 23150.0 | 8675.4 | 25028.8 | 941.2 | -| 5000 | 38502.8 | 15283.0 | 26602.8 | 1223.2 | - -![image](https://github.com/Yangchenin41243245/Data_Structure_Homework_14_45/blob/proto2/homework1/src/pics/average.png) - ## 程式實作 ### 1. 頭文件與定義 From f67a81f06712118baf425fb87802ddd64418768e Mon Sep 17 00:00:00 2001 From: Yangchenin41243245 <41243245@nfu.edu.tw> Date: Fri, 25 Apr 2025 15:07:41 +0800 Subject: [PATCH 18/41] Update report.md --- homework1/report.md | 64 +++++++++++++++++++-------------------------- 1 file changed, 27 insertions(+), 37 deletions(-) diff --git a/homework1/report.md b/homework1/report.md index 77b5f1e..2ee1d9c 100644 --- a/homework1/report.md +++ b/homework1/report.md @@ -458,33 +458,33 @@ int main(void) ## 測試與驗證 -### 平均執行時間表格 - -| 資料數量 (n) | 插入排序 (Insertion) | 快速排序 (Quick) | 合併排序 (Merge) | 堆積排序 (Heap) | -|--------------|-----------------------|-------------------|------------------|-----------------| -| 500 | 419.2 | 151.5 | 1921.1 | 73.8 | -| 1000 | 1688.7 | 609.7 | 4306.6 | 140.9 | -| 2000 | 6238.2 | 2264.2 | 11828.0 | 336.8 | -| 3000 | 14819.7 | 5525.9 | 17096.2 | 562.4 | -| 4000 | 23682.4 | 8861.6 | 24187.3 | 739.1 | -| 5000 | 38788.5 | 14426.6 | 30609.3 | 946.4 | -| 10000 | 144778.2 | 53740.3 | 47289.1 | 2221.4 | - -![image](https://github.com/Yangchenin41243245/Data_Structure_Homework_14_45/blob/main/homework1/src/pics/pic03.png) - -### 最壞執行時間表格 - -| 資料數量 (n) | 插入排序 (Insertion) | 快速排序 (Quick) | 合併排序 (Merge) | 堆積排序 (Heap) | -|--------|-----------------|-----------------|-----------------|-----------------| -| 500 | 623 | 155 | 2643 | 101 | -| 1000 | 2478 | 792 | 5206 | 165 | -| 2000 | 7742 | 2738 | 14552 | 418 | -| 3000 | 17826 | 5405 | 19880 | 754 | -| 4000 | 26746 | 9366 | 31271 | 790 | -| 5000 | 47480 | 18947 | 30609 | 946 | -| 10000 | 150250 | 55224 | 71520 | 2422 | - -![image](https://github.com/Yangchenin41243245/Data_Structure_Homework_14_45/blob/main/homework1/src/pics/pic04.png) +# Sorting Algorithm Performance Tables + +## Worst-case Performance (Time in Microseconds) + +| Data Size | Insertion Sort | Quick Sort | Merge Sort | Heap Sort | +|-----------|----------------|------------|------------|-----------| +| 500 | 847 | 440 | 2326 | 311 | +| 1000 | 2766 | 1106 | 5090 | 320 | +| 2000 | 6395 | 2381 | 15428 | 615 | +| 3000 | 14413 | 5218 | 23567 | 849 | +| 4000 | 24047 | 9181 | 28200 | 1125 | +| 5000 | 39733 | 20349 | 30139 | 1377 | + +![image](https://github.com/Yangchenin41243245/Data_Structure_Homework_14_45/blob/proto2/homework1/src/pics/worst.png) + +## Average-case Performance (Time in Microseconds) + +| Data Size | Insertion Sort | Quick Sort | Merge Sort | Heap Sort | +|-----------|----------------|------------|------------|-----------| +| 500 | 701.0 | 325.8 | 2253.0 | 240.0 | +| 1000 | 1906.4 | 792.6 | 4777.0 | 283.8 | +| 2000 | 6149.2 | 2261.8 | 13970.4 | 540.6 | +| 3000 | 13777.4 | 5050.6 | 20875.4 | 717.6 | +| 4000 | 23150.0 | 8675.4 | 25028.8 | 941.2 | +| 5000 | 38502.8 | 15283.0 | 26602.8 | 1223.2 | + +![image](https://github.com/Yangchenin41243245/Data_Structure_Homework_14_45/blob/proto2/homework1/src/pics/average.png) ### 時間複雜度趨勢分析 @@ -494,13 +494,3 @@ int main(void) - 堆積排序:執行時間隨 $n$ 緩慢上升,符合 $O(n \log n)$,在測試中表現最佳。 --- - -## 申論及開發報告 - - -#### 解釋 -- **功能**: - - `makeCases`:生成測試資料,根據模式選擇遞減序列(插入、快速、合併)或隨機排列(堆積)。 - - `main`:初始化文件、生成測試案例、執行排序並記錄結果。 - ---- From 83ffb18591869596e71a2c157c621d9d3776f92a Mon Sep 17 00:00:00 2001 From: Yangchenin41243245 <41243245@nfu.edu.tw> Date: Fri, 25 Apr 2025 15:08:55 +0800 Subject: [PATCH 19/41] Update report.md --- homework1/report.md | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/homework1/report.md b/homework1/report.md index 2ee1d9c..5739571 100644 --- a/homework1/report.md +++ b/homework1/report.md @@ -457,10 +457,7 @@ int main(void) ## 測試與驗證 - -# Sorting Algorithm Performance Tables - -## Worst-case Performance (Time in Microseconds) +## 最壞執行時間表格 Worst-case Performance (Time in Microseconds) | Data Size | Insertion Sort | Quick Sort | Merge Sort | Heap Sort | |-----------|----------------|------------|------------|-----------| @@ -473,7 +470,7 @@ int main(void) ![image](https://github.com/Yangchenin41243245/Data_Structure_Homework_14_45/blob/proto2/homework1/src/pics/worst.png) -## Average-case Performance (Time in Microseconds) +## 平均執行時間表格 Average-case Performance (Time in Microseconds) | Data Size | Insertion Sort | Quick Sort | Merge Sort | Heap Sort | |-----------|----------------|------------|------------|-----------| From df41cf20f40a6d4e10a74ca2b30cd63fcd1ce54c Mon Sep 17 00:00:00 2001 From: Yangchenin41243245 <41243245@nfu.edu.tw> Date: Fri, 25 Apr 2025 15:09:23 +0800 Subject: [PATCH 20/41] Update report.md --- homework1/report.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/homework1/report.md b/homework1/report.md index 5739571..e8fdefc 100644 --- a/homework1/report.md +++ b/homework1/report.md @@ -491,3 +491,5 @@ int main(void) - 堆積排序:執行時間隨 $n$ 緩慢上升,符合 $O(n \log n)$,在測試中表現最佳。 --- + +## 申論及開發報告 From 7d2123ad7f822a29162e7e4e1a27137b27cfcdb2 Mon Sep 17 00:00:00 2001 From: Yangchenin41243245 <41243245@nfu.edu.tw> Date: Fri, 25 Apr 2025 15:09:53 +0800 Subject: [PATCH 21/41] Update report.md --- homework1/report.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/homework1/report.md b/homework1/report.md index e8fdefc..dc4080f 100644 --- a/homework1/report.md +++ b/homework1/report.md @@ -457,7 +457,7 @@ int main(void) ## 測試與驗證 -## 最壞執行時間表格 Worst-case Performance (Time in Microseconds) +### 最壞執行時間表格 Worst-case Performance (Time in Microseconds) | Data Size | Insertion Sort | Quick Sort | Merge Sort | Heap Sort | |-----------|----------------|------------|------------|-----------| @@ -470,7 +470,7 @@ int main(void) ![image](https://github.com/Yangchenin41243245/Data_Structure_Homework_14_45/blob/proto2/homework1/src/pics/worst.png) -## 平均執行時間表格 Average-case Performance (Time in Microseconds) +### 平均執行時間表格 Average-case Performance (Time in Microseconds) | Data Size | Insertion Sort | Quick Sort | Merge Sort | Heap Sort | |-----------|----------------|------------|------------|-----------| From 1d58443a5240a7959ef1ec6b116065eb7c66ca74 Mon Sep 17 00:00:00 2001 From: Yangchenin41243245 <41243245@nfu.edu.tw> Date: Fri, 25 Apr 2025 15:57:59 +0800 Subject: [PATCH 22/41] Update report.md --- homework1/report.md | 344 ++++---------------------------------------- 1 file changed, 31 insertions(+), 313 deletions(-) diff --git a/homework1/report.md b/homework1/report.md index dc4080f..fba58a0 100644 --- a/homework1/report.md +++ b/homework1/report.md @@ -119,7 +119,11 @@ auto duration = chrono::duration_cast(end - start); ## 程式實作 -### 1. 頭文件與定義 +當然可以,讓我們來看 **區塊一**: + +--- + +### 一. 標頭與基本設置 ```cpp #include @@ -128,333 +132,47 @@ auto duration = chrono::duration_cast(end - start); #include #include #include +#include +#include +#include + +#pragma comment( lib, "psapi.lib" ) using namespace std; -using namespace std::chrono; -#define CASE_ITEMS 5000 // number of items in each case -#define CASES 10 // number of cases +#define CASE_ITEMS 500 // number of items in each case +#define CASES 5 // number of cases #define RNGKEYS rand() % CASE_ITEMS #define INSKEYS CASE_ITEMS - i // worst case for insertion sort -#define UNSORTED "./tosort.txt" -#define SORTED "./sorted.txt" -#define TIMEREC "./timer.txt" -``` - -#### 解釋 -- **功能**:引入必要的 C++ 標準庫,包括輸入輸出(``)、動態陣列(``)、計時(``)等,並定義常數。 -- **常數定義**: - - `CASE_ITEMS`:每個測試案例的資料量(5000)。 - - `CASES`:測試案例數(10)。 - - `RNGKEYS` 和 `INSKEYS`:用於生成隨機鍵值和插入排序的最壞情況鍵值。 - - 文件路徑(`UNSORTED`, `SORTED`, `TIMEREC`):用於儲存未排序、已排序和計時結果。 ---- - -### 2. 資料結構定義 - -```cpp -struct node -{ - int data; -}; - -class entry -{ -public: - friend class node; - long key; - entry(long k, node *d) - { - key = k; - data = d; - } - void setkey(int k) - { - key = k; - } - void outputkey(FILE *file) - { - fprintf(file, " key: %ld\n", key); - } -private: - node *data; -}; +#define UNSORTED "D:/work/sort/tosort.txt" +#define SORTED "D:/work/sort/sorted.txt" +#define TIMEREC "D:/work/sort/timer.txt" ``` -#### 解釋 -- **功能**: - - `node`:簡單結構,包含一個整數資料欄位。 - - `entry`:主要資料結構,包含鍵值(`key`)和指向 `node` 的指標,用於排序和輸出。 - --- -### 3. 插入排序實現 +#### 說明: -```cpp -void InsertionCore(entry *temp, vector&arr, int i) -{ - entry a = *temp; - int pos = i; - while(i >= 0 && a.key < arr[i].key) - { - arr[i + 1] = arr[i]; - i--; - } - arr[i+1] = a; -} - -unsigned long InsertionSort(vector &arr, int casenum) -{ - cout << "Start insertion sort\n"; - auto timer = high_resolution_clock::now(); - for (int j = 2; j < arr.size(); j++) - { - entry*temp = &arr[j]; - InsertionCore(temp, arr, j-1); - } - auto stop = high_resolution_clock::now(); - auto dur = duration_cast(stop - timer); - cout << "Sorted array in " << dur.count() << " microseconds\n"; - FILE *file = fopen(TIMEREC, "a"); - fprintf(file, "Sorted case #%d with %lu items in %ld microseconds with Insertion\n", - casenum, CASE_ITEMS, dur.count()); - if (file != nullptr) - { - fclose(file); - } - return dur.count(); -} -``` +##### 引入標頭檔 +這裡引入了多個標準與系統相關的 C++ 標頭檔: +- `iostream`, `vector`, `algorithm`, `string`:常用的 C++ 標準庫,處理輸出、容器、演算法與字串。 +- `ctime`, `cstdlib`:處理隨機數與時間。 +- `chrono`:計時用,計算排序時間。 +- `windows.h`, `psapi.h`:Windows API,用來查詢記憶體使用情況。 -#### 解釋 -- **功能**: - - `InsertionCore`:核心插入邏輯,將元素插入到正確位置。 - - `InsertionSort`:完整的插入排序實現,記錄執行時間並輸出到文件。 -- **計時**:使用 `` 的高精度計時器,單位為微秒。 +##### `#pragma comment(lib, "psapi.lib")` +這行是為了讓 `psapi.h` 裡的函式在編譯時自動連結 `psapi.lib`,這是 Windows 的程式記憶體管理函式庫。 ---- +##### 宏定義(`#define`) +這邊設定了一些「全域常數與快捷設定」: +- `CASE_ITEMS`:每筆測試資料裡要排序的元素數量(500)。 +- `CASES`:總共要做幾組測試(5 組)。 +- `RNGKEYS`:隨機 key,用於亂數測資。 +- `INSKEYS`:用於 Insertion Sort 最差情境的資料(由大到小,`CASE_ITEMS - i`)。 +- `UNSORTED`, `SORTED`, `TIMEREC`:三個檔案的儲存路徑(未排序、排序後、計時紀錄)。 -### 4. 快速排序實現 -```cpp -void QuickSortCore(vector&arr, int left, int right) -{ - if (left < right) - { - entry *pivot = &arr[left]; - int i = left, j = right + 1; - do - { - do i++; while (arr[i].key < pivot->key); - do j--; while (arr[j].key > pivot->key); - if (i < j) swap(arr[i], arr[j]); - } while (i < j); - swap(arr[left], arr[j]); - QuickSortCore(arr, left, j - 1); - QuickSortCore(arr, j + 1, right); - } -} - -unsigned long QuickSort(vector &arr, int casenum) -{ - cout << "Start quick sort\n"; - auto timer = high_resolution_clock::now(); - QuickSortCore(arr, 0, arr.size()-1); - auto stop = high_resolution_clock::now(); - auto dur = duration_cast(stop - timer); - cout << "Sorted array in " << dur.count() << " microseconds\n"; - FILE *file = fopen(TIMEREC, "a"); - fprintf(file, "Sorted case #%d with %lu items in %ld microseconds with Quick\n", - casenum, CASE_ITEMS, dur.count()); - if(file!=nullptr) fclose(file); - return dur.count(); -} -``` - -#### 解釋 -- **功能**: - - `QuickSortCore`:遞迴實現快速排序,使用第一個元素作為樞軸(pivot)。 - - `QuickSort`:啟動排序並計時。 --- -### 5. 合併排序實現 - -```cpp -vector MergeCore(vector& a, vector& b) -{ - vector c; - c.reserve(a.size() + b.size()); - auto it_a = a.begin(); - auto it_b = b.begin(); - while (it_a != a.end() && it_b != b.end()) - { - if (it_a->key < it_b->key) - { - c.push_back(std::move(*it_a)); - ++it_a; - } - else - { - c.push_back(std::move(*it_b)); - ++it_b; - } - } - while (it_a != a.end()) { c.push_back(std::move(*it_a)); ++it_a; } - while (it_b != b.end()) { c.push_back(std::move(*it_b)); ++it_b; } - return c; -} - -vector MergeCut(vectorarr) -{ - if(arr.size() <= 1) { return arr; } - vector left, right; - int mid = arr.size() / 2; - for (int i = 0; i < mid; i++) left.push_back(arr[i]); - for (int i = mid; i < arr.size(); i++) right.push_back(arr[i]); - left = MergeCut(left); - right = MergeCut(right); - return MergeCore(left, right); -} - -unsigned long MergeSort(vector&arr, int casenum) -{ - cout << "Start merge sort\n"; - auto timer = high_resolution_clock::now(); - arr = MergeCut(arr); - auto stop = high_resolution_clock::now(); - auto dur = duration_cast(stop - timer); - cout << "Sorted array in " << dur.count() << " microseconds\n"; - FILE *file = fopen(TIMEREC, "a"); - fprintf(file, "Sorted case #%d with %lu items in %ld microseconds with Merge\n", - casenum, CASE_ITEMS, dur.count()); - if(file!=nullptr) fclose(file); - return dur.count(); -} -``` - -#### 解釋 -- **功能**: - - `MergeCore`:合併兩個已排序子陣列。 - - `MergeCut`:遞迴分割陣列。 - - `MergeSort`:啟動排序並計時。 - ---- - -## 6. 堆積排序實現 - -```cpp -void heapify(vector& arr, int n, int i) -{ - int max = i; - int left = 2 * i + 1; - int right = 2 * i + 2; - if (left < n && arr[left].key > arr[max].key) max = left; - if (right < n && arr[right].key > arr[max].key) max = right; - if (max != i) - { - swap(arr[i], arr[max]); - heapify(arr, n, max); - } -} - -unsigned long HeapSort(vector&arr, int casenum) -{ - cout << "Start heap sort\n"; - auto timer = high_resolution_clock::now(); - int n = arr.size(); - for (int i = n / 2 - 1; i >= 0; i--) heapify(arr, n, i); - for (int i = n - 1; i > 0; i--) - { - swap(arr[0], arr[i]); - heapify(arr, i, 0); - } - auto stop = high_resolution_clock::now(); - auto dur = duration_cast(stop - timer); - cout << "Sorted array in " << dur.count() << " microseconds\n"; - FILE *file = fopen(TIMEREC, "a"); - fprintf(file, "Sorted case #%d with %lu items in %ld microseconds with Heap\n", - casenum, CASE_ITEMS, dur.count()); - if(file!=nullptr) fclose(file); - return dur.count(); -} -``` - -#### 解釋 -- **功能**: - - `heapify`:維護最大堆性質。 - - `HeapSort`:建構最大堆並逐步排序。 - ---- - -### 7. 主程式與測試資料生成 - -```cpp -void makeCases(int cases, vector> &superarray, FILE *unsortedfile, string mode) -{ - for (int c = 0; c < cases; c++) - { - vector array; - long key; - fprintf(unsortedfile, "\nCase %d with %lu items:\n", c + 1, CASE_ITEMS); - for (int i = 0; i < CASE_ITEMS; i++) - { - node *n = new node; - if (mode == "INSERTION" || mode == "QUICK" || mode == "MERGE") - key = INSKEYS; // worst case = max -> min - else if (mode == "RANDOM") - key = RNGKEYS; - else - key = i; - entry e(key, n); - array.push_back(e); - } - if (mode == "HEAP") - { - for (int i = CASE_ITEMS - 1; i >= 2; i--) - { - int j = rand() % i + 1; - swap(array[i], array[j]); - } - } - for (int i = 0; i < CASE_ITEMS; i++) - array[i].outputkey(unsortedfile); - superarray.push_back(array); - } -} - -int main(void) -{ - remove(SORTED); remove(UNSORTED); remove(TIMEREC); - FILE *f_Unsorted = fopen(UNSORTED, "a"); - FILE *f_Sorted = fopen(SORTED, "a"); - srand(time(0)); - vector> superarray[4]; - makeCases(CASES, superarray[0], f_Unsorted, "INSERTION"); - makeCases(CASES, superarray[1], f_Unsorted, "QUICK"); - makeCases(CASES, superarray[2], f_Unsorted, "MERGE"); - makeCases(CASES, superarray[3], f_Unsorted, "HEAP"); - for (int m = 0; m < 4; m++) - { - for (int i = 0, c = 1; i < superarray[m].size(); i++, c++) - { - auto duration = 0; - switch (m) - { - case 0: duration = InsertionSort(superarray[m][i], c); break; - case 1: duration = QuickSort(superarray[m][i], c); break; - case 2: duration = MergeSort(superarray[m][i], c); break; - case 3: duration = HeapSort(superarray[m][i], c); break; - } - fprintf(f_Sorted, "\nCase %d of %lu items finished in %lu microseconds\n", c, CASE_ITEMS, duration); - for (int j = 0; j < CASE_ITEMS; j++) - superarray[m][i][j].outputkey(f_Sorted); - } - } - fclose(f_Unsorted); - fclose(f_Sorted); - return 0; -} -``` - ## 測試與驗證 ### 最壞執行時間表格 Worst-case Performance (Time in Microseconds) From ce687b2a5fd9f482db926d9c92cb0b8e08dfe44a Mon Sep 17 00:00:00 2001 From: Yangchenin41243245 <41243245@nfu.edu.tw> Date: Fri, 25 Apr 2025 15:58:13 +0800 Subject: [PATCH 23/41] Update report.md --- homework1/report.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/homework1/report.md b/homework1/report.md index fba58a0..19a7b2b 100644 --- a/homework1/report.md +++ b/homework1/report.md @@ -119,9 +119,6 @@ auto duration = chrono::duration_cast(end - start); ## 程式實作 -當然可以,讓我們來看 **區塊一**: - ---- ### 一. 標頭與基本設置 From e493b5107f0c6a7f23c834a6afbd7173eeb4e464 Mon Sep 17 00:00:00 2001 From: Yangchenin41243245 <41243245@nfu.edu.tw> Date: Fri, 25 Apr 2025 16:01:14 +0800 Subject: [PATCH 24/41] Update report.md --- homework1/report.md | 48 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 44 insertions(+), 4 deletions(-) diff --git a/homework1/report.md b/homework1/report.md index 19a7b2b..f577ba4 100644 --- a/homework1/report.md +++ b/homework1/report.md @@ -150,23 +150,63 @@ using namespace std; #### 說明: ##### 引入標頭檔 -這裡引入了多個標準與系統相關的 C++ 標頭檔: - `iostream`, `vector`, `algorithm`, `string`:常用的 C++ 標準庫,處理輸出、容器、演算法與字串。 - `ctime`, `cstdlib`:處理隨機數與時間。 - `chrono`:計時用,計算排序時間。 - `windows.h`, `psapi.h`:Windows API,用來查詢記憶體使用情況。 ##### `#pragma comment(lib, "psapi.lib")` -這行是為了讓 `psapi.h` 裡的函式在編譯時自動連結 `psapi.lib`,這是 Windows 的程式記憶體管理函式庫。 +讓 `psapi.h` 裡的函式在編譯時自動連結 `psapi.lib`,這是 Windows 的程式記憶體管理函式庫。 -##### 宏定義(`#define`) -這邊設定了一些「全域常數與快捷設定」: +##### #define 指引 - `CASE_ITEMS`:每筆測試資料裡要排序的元素數量(500)。 - `CASES`:總共要做幾組測試(5 組)。 - `RNGKEYS`:隨機 key,用於亂數測資。 - `INSKEYS`:用於 Insertion Sort 最差情境的資料(由大到小,`CASE_ITEMS - i`)。 - `UNSORTED`, `SORTED`, `TIMEREC`:三個檔案的儲存路徑(未排序、排序後、計時紀錄)。 +--- + +### 二. 記憶體與時間紀錄函式定義 + +```cpp +// memory usage check +SIZE_T memoryUsage() { + PROCESS_MEMORY_COUNTERS pmc; + GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc)); + return pmc.WorkingSetSize; +} + +// time recording +double recordTime(function sortFunc) { + auto start = chrono::high_resolution_clock::now(); + sortFunc(); // 執行排序 + auto end = chrono::high_resolution_clock::now(); + chrono::duration duration = end - start; + return duration.count(); +} +``` + +#### 說明: + +##### `memoryUsage()` — 取得目前記憶體使用量 + +- 使用 Windows API: + - `GetProcessMemoryInfo()`:取得目前執行程序的記憶體資訊。 + - `pmc.WorkingSetSize`:代表目前正在使用的實體記憶體(Working Set),以「位元組 (bytes)」為單位。 +- 回傳型別是 `SIZE_T`,通常對應 `size_t`,足以儲存記憶體大小。 + +這個函式的用途是在排序前後量測記憶體用量。 + +--- + +##### `recordTime()` — 執行排序並記錄所花時間 + +- 接收一個 `function` 作為參數,也就是接受任何無參數、無回傳值的函式(或 lambda)。 +- 使用 `chrono::high_resolution_clock` 精準地記錄時間。 +- 回傳值是毫秒(`duration`)。 + +讓每種排序方法都可以包裝成 lambda 傳入這個函式中,方便統一計時。 --- From 479f610da65ca4ccab01158d405bcb585f28edec Mon Sep 17 00:00:00 2001 From: Yangchenin41243245 <41243245@nfu.edu.tw> Date: Fri, 25 Apr 2025 16:16:48 +0800 Subject: [PATCH 25/41] Update report.md --- homework1/report.md | 82 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/homework1/report.md b/homework1/report.md index f577ba4..6c127ca 100644 --- a/homework1/report.md +++ b/homework1/report.md @@ -208,6 +208,88 @@ double recordTime(function sortFunc) { 讓每種排序方法都可以包裝成 lambda 傳入這個函式中,方便統一計時。 +好的,我們進入 **區塊三**,並以嚴謹方式解析: + +--- + +### 三. 排序函式實作與統一測試介面 `runSort()` + +```cpp +// 實作排序方法 +void insertionSort(vector& arr); +void quickSort(vector& arr); +void mergeSort(vector& arr); +void heapSort(vector& arr); + +// 測試用排序介面 +void runSort(string sortName, void (*sortFunc)(vector&), vector data, ofstream& logFile) { + SIZE_T memBefore = memoryUsage(); // 排序前記憶體 + double time = recordTime([&]() { sortFunc(data); }); // 排序時間 + SIZE_T memAfter = memoryUsage(); // 排序後記憶體 + + // 結果輸出 + cout << sortName << ": " << time << " ms, "; + cout << "Memory used: " << (memAfter - memBefore) / 1024 << " KB" << endl; + logFile << sortName << ": " << time << " ms, "; + logFile << "Memory used: " << (memAfter - memBefore) / 1024 << " KB" << endl; +} +``` + +--- + +#### 說明: + +##### 排序函式宣告 + +```cpp +void insertionSort(vector& arr); +void quickSort(vector& arr); +void mergeSort(vector& arr); +void heapSort(vector& arr); +``` + +這四個函式是用來排序整數陣列的,採用傳參考方式以便直接在原始資料上操作。它們的實作在程式後面,這裡僅宣告。 + +--- + +##### `runSort(...)` — 統一測試框架 + +**目的**:統一測量排序時間與記憶體,並印出與紀錄結果。 + +**參數解析**: +- `sortName`: 排序法名稱,用於輸出。 +- `sortFunc`: 指向排序函式的函式指標(`void (*)(vector&)`)。 +- `data`: 排序資料,傳值進來,以避免修改原資料。 +- `logFile`: 日誌檔案參考,用來紀錄輸出結果。 + +--- + +##### 運作流程: + +1. **記錄排序前記憶體**: + ```cpp + SIZE_T memBefore = memoryUsage(); + ``` + +2. **計時排序函式執行時間**: + ```cpp + double time = recordTime([&]() { sortFunc(data); }); + ``` + - 使用 lambda 封裝 `sortFunc(data)` 呼叫,以符合 `recordTime()` 的格式。 + - Lambda 捕捉 `[&]` 表示引用捕捉,確保 `data` 被修改(這裡其實是複製進來的副本,不會影響原資料)。 + +3. **記錄排序後記憶體**: + ```cpp + SIZE_T memAfter = memoryUsage(); + ``` + +4. **輸出與紀錄測試結果**: + ```cpp + cout << ...; // 印到螢幕 + logFile << ...; // 寫入檔案 + ``` + 記憶體差異以 KB 為單位輸出,方便閱讀。 + --- ## 測試與驗證 From 427fb8f0c8bb29c91badc391c0aedc24b06f325b Mon Sep 17 00:00:00 2001 From: Yangchenin41243245 <41243245@nfu.edu.tw> Date: Fri, 25 Apr 2025 16:17:09 +0800 Subject: [PATCH 26/41] Update report.md --- homework1/report.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/homework1/report.md b/homework1/report.md index 6c127ca..f09e3de 100644 --- a/homework1/report.md +++ b/homework1/report.md @@ -208,8 +208,6 @@ double recordTime(function sortFunc) { 讓每種排序方法都可以包裝成 lambda 傳入這個函式中,方便統一計時。 -好的,我們進入 **區塊三**,並以嚴謹方式解析: - --- ### 三. 排序函式實作與統一測試介面 `runSort()` From 26281830ec7feebf2862928f22e2e5a679b8e427 Mon Sep 17 00:00:00 2001 From: Yangchenin41243245 <41243245@nfu.edu.tw> Date: Fri, 25 Apr 2025 16:20:44 +0800 Subject: [PATCH 27/41] Update report.md --- homework1/report.md | 129 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 129 insertions(+) diff --git a/homework1/report.md b/homework1/report.md index f09e3de..922035d 100644 --- a/homework1/report.md +++ b/homework1/report.md @@ -288,6 +288,135 @@ void heapSort(vector& arr); ``` 記憶體差異以 KB 為單位輸出,方便閱讀。 +好,我們繼續以嚴謹方式分析 **區塊四**,也就是四個排序演算法的實作。程式碼如下: + +--- + +### 四. 排序法實作 + +```cpp +void insertionSort(vector& arr) { + for (int i = 1; i < arr.size(); ++i) { + int key = arr[i], j = i - 1; + while (j >= 0 && arr[j] > key) arr[j + 1] = arr[j--]; + arr[j + 1] = key; + } +} + +void quickSort(vector& arr) { + function sort = [&](int l, int r) { + if (l >= r) return; + int pivot = arr[r], i = l - 1; + for (int j = l; j < r; ++j) + if (arr[j] < pivot) swap(arr[++i], arr[j]); + swap(arr[++i], arr[r]); + sort(l, i - 1); + sort(i + 1, r); + }; + sort(0, arr.size() - 1); +} + +void mergeSort(vector& arr) { + function sort = [&](int l, int r) { + if (l >= r) return; + int m = (l + r) / 2; + sort(l, m); sort(m + 1, r); + vector tmp; + int i = l, j = m + 1; + while (i <= m && j <= r) + tmp.push_back(arr[i] < arr[j] ? arr[i++] : arr[j++]); + while (i <= m) tmp.push_back(arr[i++]); + while (j <= r) tmp.push_back(arr[j++]); + for (int k = 0; k < tmp.size(); ++k) arr[l + k] = tmp[k]; + }; + sort(0, arr.size() - 1); +} + +void heapSort(vector& arr) { + function heapify = [&](int n, int i) { + int largest = i, l = 2 * i + 1, r = 2 * i + 2; + if (l < n && arr[l] > arr[largest]) largest = l; + if (r < n && arr[r] > arr[largest]) largest = r; + if (largest != i) { + swap(arr[i], arr[largest]); + heapify(n, largest); + } + }; + int n = arr.size(); + for (int i = n / 2 - 1; i >= 0; --i) heapify(n, i); + for (int i = n - 1; i > 0; --i) { + swap(arr[0], arr[i]); + heapify(i, 0); + } +} +``` + +#### 說明: + +##### 1.Insertion Sort — 插入排序(O(n²)) + +```cpp +for (int i = 1; i < arr.size(); ++i) { + int key = arr[i], j = i - 1; + while (j >= 0 && arr[j] > key) arr[j + 1] = arr[j--]; + arr[j + 1] = key; +} +``` + +- 對每一個元素,往前比較並插入至適當位置。 +- 穩定排序,適合幾乎排序好的資料。 +- **時間複雜度**:平均與最壞為 O(n²),最佳為 O(n)。 + +--- + +##### 2.Quick Sort — 快速排序(平均 O(n log n)) + +```cpp +function sort = [&](int l, int r) { + if (l >= r) return; + int pivot = arr[r], i = l - 1; + for (int j = l; j < r; ++j) + if (arr[j] < pivot) swap(arr[++i], arr[j]); + swap(arr[++i], arr[r]); + sort(l, i - 1); + sort(i + 1, r); +}; +sort(0, arr.size() - 1); +``` + +- 以最後一個元素作為 pivot。 +- 小於 pivot 的元素會被移到左側,遞迴排序。 +- 不穩定排序,**在最壞情況(遞增/遞減序列)會退化為 O(n²)**,但一般實務中表現極佳。 + +--- + +##### 3.Merge Sort — 合併排序(穩定、O(n log n)) + +```cpp +function sort = [&](int l, int r) { + if (l >= r) return; + int m = (l + r) / 2; + sort(l, m); sort(m + 1, r); + ... +}; +``` + +- 將陣列遞迴切半後合併,合併時保持排序。 +- **空間複雜度 O(n)**,需要額外暫存空間。 +- 穩定排序,適合資料量大且對穩定性有要求的應用。 + +--- + +##### 4.Heap Sort — 堆積排序(不穩定、O(n log n)) + +```cpp +function heapify = [&](int n, int i) { ... }; +``` + +- 先建 max heap,再依序將最大值交換到尾端並縮小 heap 區間。 +- 不需要額外空間,**空間複雜度 O(1)**。 +- 時間複雜度穩定為 O(n log n),但常數大、快取不友善,實務上不如 QuickSort 快。 + --- ## 測試與驗證 From b43071f9863cf8a0fd55ebfe023078ae2e886964 Mon Sep 17 00:00:00 2001 From: Yangchenin41243245 <41243245@nfu.edu.tw> Date: Fri, 25 Apr 2025 16:21:06 +0800 Subject: [PATCH 28/41] Update report.md --- homework1/report.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/homework1/report.md b/homework1/report.md index 922035d..eed3486 100644 --- a/homework1/report.md +++ b/homework1/report.md @@ -288,8 +288,6 @@ void heapSort(vector& arr); ``` 記憶體差異以 KB 為單位輸出,方便閱讀。 -好,我們繼續以嚴謹方式分析 **區塊四**,也就是四個排序演算法的實作。程式碼如下: - --- ### 四. 排序法實作 From 64c6e26d76a363647a9a15ca332beecedb62b7d7 Mon Sep 17 00:00:00 2001 From: Yangchenin41243245 <41243245@nfu.edu.tw> Date: Fri, 25 Apr 2025 16:34:39 +0800 Subject: [PATCH 29/41] Update report.md --- homework1/report.md | 120 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 120 insertions(+) diff --git a/homework1/report.md b/homework1/report.md index eed3486..ef30564 100644 --- a/homework1/report.md +++ b/homework1/report.md @@ -417,6 +417,126 @@ function heapify = [&](int n, int i) { ... }; --- +### 五. makeCases 函數 — 測資生成器 + +```cpp +void makeCases(int cases, vector> &superarray, FILE *unsortedfile, string mode) +``` +- **參數**: + - `cases`: 要生成的測試用例數量(由 `CASES` 定義,預設為 5)。 + - `superarray`: 一個 `vector>` 的引用,用於存儲生成的測試用例數組。每個測試用例是一個 `vector`。 + - `unsortedfile`: 指向輸出文件的指針(`FILE *`),用於將未排序的鍵值寫入文件(`UNSORTED`)。 + - `mode`: 一個字符串,指定生成鍵值的模式(例如 `"INSERTION"`、`"QUICK"`、`"MERGE"` 或 `"HEAP"`)。 +- **返回值**:無(`void`),但通過引用修改 `superarray` 並寫入文件。 + +--- + +##### 外層循環 +```cpp +for (int c = 0; c < cases; c++) // create cases +{ + vector array; + long key; + fprintf(unsortedfile, "\nCase %d with %lu items:\n", c + 1, CASE_ITEMS); + // ... +} +``` +- **功能**:遍歷 `cases` 次,生成 `cases` 個測試用例。 + - 每次循環創建一個空的 `vector` 數組 `array`,用於存儲當前測試用例的元素。 + - 使用 `fprintf` 向 `unsortedfile` 寫入當前測試用例的標識(例如 `Case 1 with 500 items:`),為文件輸出添加結構化信息。 + - `CASE_ITEMS` 定義了每個測試用例的元素數量(預設 500)。 + +--- + +##### 內層循環 +```cpp +for (int i = 0; i < CASE_ITEMS; i++) // create array with nodes +{ + node *n = new node; + if (mode == "INSERTION" || mode == "QUICK" || mode == "MERGE") // generate key nums by mode + key = INSKEYS; //do worst case = max -> min + else if (mode == "RANDOM") + key = RNGKEYS; + else + key = i; // will process after array is created + entry e(key, n); // create entry with key and node + array.push_back(e); +} +``` +- **功能**:為每個測試用例生成 `CASE_ITEMS` 個 `entry` 對象,並設置它們的鍵值。 + - **動態分配 `node`**: + - 為每個 `entry` 創建一個新的 `node` 對象(`new node`),並分配在堆上。 + - 注意:這裡的 `node` 結構目前僅包含一個 `int data`,但程式中並未對 `data` 進行初始化或使用,因此這部分可能是未完成或待擴展的功能。 + - **鍵值生成**: + - 鍵值(`key`)的生成方式取決於 `mode`: + - **INSERTION, QUICK, MERGE**: + - 使用 `INSKEYS` 宏,定義為 `CASE_ITEMS - i`(即 500, 499, ..., 1)。 + - 這會生成一個逆序數組(從大到小),對於插入排序(`INSERTION`)和快速排序(`QUICK`)來說是 **最壞情況**(worst case),因為插入排序需要多次移位,快速排序的樞軸選擇(程式中選第一個元素)會導致不平衡分割。 + - **RANDOM**: + - 使用 `RNGKEYS` 宏,定義為 `rand() % CASE_ITEMS`,生成隨機鍵值(0 到 499 之間)。 + - 注意:程式中實際上未使用 `"RANDOM"` 模式(主程式中只傳入 `"INSERTION"`、`"QUICK"`、`"MERGE"`、`"HEAP"`)。 + - **其他(默認為 HEAP)**: + - 鍵值設為 `i`(即 0, 1, ..., 499),生成一個升序數組。 + - 這是臨時值,後續在 `"HEAP"` 模式下會進行隨機化處理。 + - **創建 `entry` 並添加到數組**: + - 使用生成的 `key` 和 `node` 創建一個 `entry` 對象 `e`。 + - 將 `e` 添加到當前測試用例的 `array` 中。 + +--- + +##### HEAP 模式的特殊處理 +```cpp +if (mode == "HEAP") // do the Permutation() provided +{ + for (int i = CASE_ITEMS - 1; i >= 2; i--) + { + int j = rand() % i + 1; + swap(array[i], array[j]); + } +} +``` +- **功能**:如果 `mode` 是 `"HEAP"`,對數組進行隨機化處理,模擬隨機排列。 + - 在生成數組時,`"HEAP"` 模式的鍵值最初是升序的(0, 1, ..., 499)。 + - 這裡通過一個簡化的隨機排列演算法(類似 Fisher-Yates 洗牌的變種)對數組進行隨機化: + - 從索引 `CASE_ITEMS - 1`(499)到 2,隨機選擇一個索引 `j`(範圍從 1 到 `i`)。 + - 交換 `array[i]` 和 `array[j]`,實現隨機排列。 +--- + +##### 輸出未排序數組到文件 +```cpp +for (int i = 0; i < CASE_ITEMS; i++) // output key of each node in array just made +{ + array[i].outputkey(unsortedfile); +} +``` +- **功能**:將當前測試用例的每個 `entry` 對象的鍵值輸出到 `unsortedfile`。 + - 調用 `entry` 類的 `outputkey` 方法: + ```cpp + void outputkey(FILE *file) + { + fprintf(file, " key: %ld\n", key); + } + ``` + - 每個鍵值以格式 `key: ` 寫入文件,每行一個鍵值。 + - 這一步記錄了未排序數組的初始狀態,方便後續對比或調試。 + +--- + +##### 存儲數組並輸出日誌 +```cpp +cout << "output unsorted array to file" << UNSORTED << endl; +superarray.push_back(array); // add array to cases +cout << "Created unsorted array for case #" << c + 1 << endl; +``` +- **功能**:將生成的數組存入 `superarray` 並打印日誌。 +- **細節**: + - 將當前測試用例的 `array`(`vector`)添加到 `superarray` 中,`superarray` 是一個 `vector>`,每個元素是一個測試用例的數組。 + - 輸出: + - 確認已將未排序數組寫入文件(`UNSORTED`)。 + - 確認已創建當前測試用例(例如 `Created unsorted array for case #1`)。 + +--- + ## 測試與驗證 ### 最壞執行時間表格 Worst-case Performance (Time in Microseconds) From b557cf305524866b9d108d1c9987cc3861452031 Mon Sep 17 00:00:00 2001 From: Yangchenin41243245 <41243245@nfu.edu.tw> Date: Fri, 25 Apr 2025 20:31:27 +0800 Subject: [PATCH 30/41] Update report.md --- homework1/report.md | 90 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/homework1/report.md b/homework1/report.md index ef30564..5f8ed15 100644 --- a/homework1/report.md +++ b/homework1/report.md @@ -537,6 +537,96 @@ cout << "Created unsorted array for case #" << c + 1 << endl; --- +以下是對提供的 C++ 程式碼中 `main` 函式的詳細講解,包含其功能、程式流程、結構分析、特殊之處以及潛在改進點。`main` 函式作為程式的入口,負責初始化環境、生成測試用例、執行排序演算法並輸出結果。 + +--- + +### 6. `main` 主程式 + +```cpp +int main(void) +{ + remove(SORTED); + cout << "Removed old " << SORTED << endl; // remove old files + remove(UNSORTED); + cout << "Removed old " << UNSORTED << endl; + remove(TIMEREC); + cout << "Removed old " << TIMEREC << endl; + + FILE *f_Unsorted = fopen(UNSORTED, "a"); // create new files + FILE *f_Sorted = fopen(SORTED, "a"); + + srand(time(0)); // set random + vector> superarray[4]; // all cases stored here + result result; + //MAKE CASES HERE///////////////////////////////////// + makeCases(CASES, superarray[0], f_Unsorted, "INSERTION"); + makeCases(CASES, superarray[1], f_Unsorted, "QUICK"); + makeCases(CASES, superarray[2], f_Unsorted, "MERGE"); + makeCases(CASES, superarray[3], f_Unsorted, "HEAP"); // create cases, provide file path for unsorted cases + ////////////////////////////////////////////////////// + //SORT ALL///////////////////////////////////////////////////////////////// + for (int type = 0; type < 4;type++){ //type m + for (int i = 0, caseNum = 1; i < superarray[type].size(); i++, caseNum++) // SA of type m with i cases + { + auto duration = 0; + switch (type) + { + case 0: + result = InsertionSort(superarray[type][i], caseNum); + break; + case 1: + result = QuickSort(superarray[type][i], caseNum); + break; + case 2: + result = MergeSort(superarray[type][i], caseNum); + break; + case 3: + result = HeapSort(superarray[type][i], caseNum); + break; + default: + break; + } + duration = result.timer; // get duration from result struct + //OUTPUT TIME + fprintf(f_Sorted, "\nCase %d of %lu items finished in %lu microseconds\n", caseNum, CASE_ITEMS, duration); + for (int j = 0; j < CASE_ITEMS; j++) // output keys of sorted array + { + result.arr2[j].outputkey(f_Sorted); // i for array, j for entry + } + cout << "output sorted array to file" << SORTED << endl; + } + } + ///////////////////////////////////////////////////////////////////////////////// + fclose(f_Unsorted); + fclose(f_Sorted); + return 0; +} +``` + +--- + +#### 說明: + +1. **模組化設計**: + - `main` 將初始化、用例生成、排序執行和結果輸出分離,邏輯清晰。 + - 排序演算法獨立於 `InsertionSort`、`QuickSort` 等函式,`main` 僅負責調度和結果處理。 + +2. **資料組織**: + - `superarray[4]` 是一個陣列,每個元素是一個 `vector>`,儲存某種排序演算法的 5 組測試用例。 + - 每個用例是 `vector`,包含 6000 個 `entry` 物件(鍵值和 `node` 指針)。 + - `result` 結構統一儲存排序結果,便於傳遞和處理。 + +3. **控制流程**: + - 使用雙層迴圈(`type` 和 `i`)遍歷演算法和用例,確保所有測試有序執行。 + - `switch` 結構簡單映射演算法類型到排序函式,但限制了擴展性。 + +4. **檔案 I/O**: + - `tosort.txt` 儲存未排序鍵值,`sorted.txt` 儲存排序結果,`timer.txt` 由排序函式寫入性能數據。 + - 檔案操作分散(`makeCases` 寫入未排序資料,`main` 寫入排序結果,排序函式寫入性能),結構清晰但效率稍低。 + +--- + ## 測試與驗證 ### 最壞執行時間表格 Worst-case Performance (Time in Microseconds) From 6d50093ea420bd8be822d03ca6b47e6c4b891891 Mon Sep 17 00:00:00 2001 From: Yangchenin41243245 <41243245@nfu.edu.tw> Date: Fri, 25 Apr 2025 20:32:01 +0800 Subject: [PATCH 31/41] Update report.md --- homework1/report.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/homework1/report.md b/homework1/report.md index 5f8ed15..3e25cef 100644 --- a/homework1/report.md +++ b/homework1/report.md @@ -537,10 +537,6 @@ cout << "Created unsorted array for case #" << c + 1 << endl; --- -以下是對提供的 C++ 程式碼中 `main` 函式的詳細講解,包含其功能、程式流程、結構分析、特殊之處以及潛在改進點。`main` 函式作為程式的入口,負責初始化環境、生成測試用例、執行排序演算法並輸出結果。 - ---- - ### 6. `main` 主程式 ```cpp From fbf1f4fcd4999dd7ce7170c60f227588a6f5c3ce Mon Sep 17 00:00:00 2001 From: Yangchenin41243245 <41243245@nfu.edu.tw> Date: Fri, 25 Apr 2025 20:38:11 +0800 Subject: [PATCH 32/41] Update report.md --- homework1/report.md | 177 ++++++++++++++++++-------------------------- 1 file changed, 74 insertions(+), 103 deletions(-) diff --git a/homework1/report.md b/homework1/report.md index 3e25cef..232d19c 100644 --- a/homework1/report.md +++ b/homework1/report.md @@ -421,120 +421,91 @@ function heapify = [&](int n, int i) { ... }; ```cpp void makeCases(int cases, vector> &superarray, FILE *unsortedfile, string mode) -``` -- **參數**: - - `cases`: 要生成的測試用例數量(由 `CASES` 定義,預設為 5)。 - - `superarray`: 一個 `vector>` 的引用,用於存儲生成的測試用例數組。每個測試用例是一個 `vector`。 - - `unsortedfile`: 指向輸出文件的指針(`FILE *`),用於將未排序的鍵值寫入文件(`UNSORTED`)。 - - `mode`: 一個字符串,指定生成鍵值的模式(例如 `"INSERTION"`、`"QUICK"`、`"MERGE"` 或 `"HEAP"`)。 -- **返回值**:無(`void`),但通過引用修改 `superarray` 並寫入文件。 - ---- - -##### 外層循環 -```cpp -for (int c = 0; c < cases; c++) // create cases { - vector array; - long key; - fprintf(unsortedfile, "\nCase %d with %lu items:\n", c + 1, CASE_ITEMS); - // ... + for (int c = 0; c < cases; c++) // create cases + { + vector array; + long key; + fprintf(unsortedfile, "\nCase %d with %lu items:\n", c + 1, CASE_ITEMS); + for (int i = 0; i < CASE_ITEMS; i++) // create array with nodes + { + node *n = new node; + if (mode == "INSERTION" || mode == "QUICK" || mode == "MERGE") // generate key nums by mode + key = INSKEYS; //do worst case = max -> min + else if (mode == "RANDOM") + key = RNGKEYS; + else + key = i; // will process after array is created + entry e(key, n); // create entry with key and node + array.push_back(e); + } + if (mode == "HEAP") // do the Permutation() provided + { + for (int i = CASE_ITEMS - 1; i >= 2; i--) + { + int j = rand() % i + 1; + swap(array[i], array[j]); + } + } + for (int i = 0; i < CASE_ITEMS; i++) // output key of each node in array just made + { + array[i].outputkey(unsortedfile); + } + cout << "output unsorted array to file" << UNSORTED << endl; + superarray.push_back(array); // add array to cases + cout << "Created unsorted array for case #" << c + 1 << endl; + } } ``` -- **功能**:遍歷 `cases` 次,生成 `cases` 個測試用例。 - - 每次循環創建一個空的 `vector` 數組 `array`,用於存儲當前測試用例的元素。 - - 使用 `fprintf` 向 `unsortedfile` 寫入當前測試用例的標識(例如 `Case 1 with 500 items:`),為文件輸出添加結構化信息。 - - `CASE_ITEMS` 定義了每個測試用例的元素數量(預設 500)。 +#### 說明: ---- +- **參數**: + - `cases`:用例數。 + - `superarray`:儲存用例(傳引用)。 + - `unsortedfile`:未排序檔案。 + - `mode`:鍵值模式。 +- **資料結構**:`vector` 儲存用例,`entry` 含 `key` 和 `node`。 +- **控制流程**:雙層迴圈,條件分支設置鍵值。 +- **檔案 I/O**:格式化輸出鍵值。 -##### 內層循環 -```cpp -for (int i = 0; i < CASE_ITEMS; i++) // create array with nodes -{ - node *n = new node; - if (mode == "INSERTION" || mode == "QUICK" || mode == "MERGE") // generate key nums by mode - key = INSKEYS; //do worst case = max -> min - else if (mode == "RANDOM") - key = RNGKEYS; - else - key = i; // will process after array is created - entry e(key, n); // create entry with key and node - array.push_back(e); -} -``` -- **功能**:為每個測試用例生成 `CASE_ITEMS` 個 `entry` 對象,並設置它們的鍵值。 - - **動態分配 `node`**: - - 為每個 `entry` 創建一個新的 `node` 對象(`new node`),並分配在堆上。 - - 注意:這裡的 `node` 結構目前僅包含一個 `int data`,但程式中並未對 `data` 進行初始化或使用,因此這部分可能是未完成或待擴展的功能。 - - **鍵值生成**: - - 鍵值(`key`)的生成方式取決於 `mode`: - - **INSERTION, QUICK, MERGE**: - - 使用 `INSKEYS` 宏,定義為 `CASE_ITEMS - i`(即 500, 499, ..., 1)。 - - 這會生成一個逆序數組(從大到小),對於插入排序(`INSERTION`)和快速排序(`QUICK`)來說是 **最壞情況**(worst case),因為插入排序需要多次移位,快速排序的樞軸選擇(程式中選第一個元素)會導致不平衡分割。 - - **RANDOM**: - - 使用 `RNGKEYS` 宏,定義為 `rand() % CASE_ITEMS`,生成隨機鍵值(0 到 499 之間)。 - - 注意:程式中實際上未使用 `"RANDOM"` 模式(主程式中只傳入 `"INSERTION"`、`"QUICK"`、`"MERGE"`、`"HEAP"`)。 - - **其他(默認為 HEAP)**: - - 鍵值設為 `i`(即 0, 1, ..., 499),生成一個升序數組。 - - 這是臨時值,後續在 `"HEAP"` 模式下會進行隨機化處理。 - - **創建 `entry` 並添加到數組**: - - 使用生成的 `key` 和 `node` 創建一個 `entry` 對象 `e`。 - - 將 `e` 添加到當前測試用例的 `array` 中。 +#### 運作流程: ---- +1. **迴圈生成用例**: + - 外層迴圈迭代 `cases` 次(5 次)。 -##### HEAP 模式的特殊處理 -```cpp -if (mode == "HEAP") // do the Permutation() provided -{ - for (int i = CASE_ITEMS - 1; i >= 2; i--) - { - int j = rand() % i + 1; - swap(array[i], array[j]); - } -} -``` -- **功能**:如果 `mode` 是 `"HEAP"`,對數組進行隨機化處理,模擬隨機排列。 - - 在生成數組時,`"HEAP"` 模式的鍵值最初是升序的(0, 1, ..., 499)。 - - 這裡通過一個簡化的隨機排列演算法(類似 Fisher-Yates 洗牌的變種)對數組進行隨機化: - - 從索引 `CASE_ITEMS - 1`(499)到 2,隨機選擇一個索引 `j`(範圍從 1 到 `i`)。 - - 交換 `array[i]` 和 `array[j]`,實現隨機排列。 ---- +2. **初始化陣列**: + - 創建 `vector array`。 + - 寫入用例標頭到 `unsortedfile`("Case X with 6000 items:")。 -##### 輸出未排序數組到文件 -```cpp -for (int i = 0; i < CASE_ITEMS; i++) // output key of each node in array just made -{ - array[i].outputkey(unsortedfile); -} -``` -- **功能**:將當前測試用例的每個 `entry` 對象的鍵值輸出到 `unsortedfile`。 - - 調用 `entry` 類的 `outputkey` 方法: - ```cpp - void outputkey(FILE *file) - { - fprintf(file, " key: %ld\n", key); - } - ``` - - 每個鍵值以格式 `key: ` 寫入文件,每行一個鍵值。 - - 這一步記錄了未排序數組的初始狀態,方便後續對比或調試。 +3. **生成元素**: + - 內層迴圈生成 6000 個 `entry`: + - 分配 `node` 物件。 + - 根據 `mode` 設置鍵值: + - "INSERTION"/"QUICK"/"MERGE":逆序(`INSKEYS`,6000, 5999, ..., 1)。 + - "RANDOM":隨機(`RNGKEYS`,未使用)。 + - 其他("HEAP"):序數(0, 1, ..., 5999)。 + - 創建 `entry` 並加入 `array`。 ---- +4. **隨機排列(Heap)**: + - 若 `mode` 為 "HEAP",從索引 5999 到 2 隨機交換元素。 -##### 存儲數組並輸出日誌 -```cpp -cout << "output unsorted array to file" << UNSORTED << endl; -superarray.push_back(array); // add array to cases -cout << "Created unsorted array for case #" << c + 1 << endl; -``` -- **功能**:將生成的數組存入 `superarray` 並打印日誌。 -- **細節**: - - 將當前測試用例的 `array`(`vector`)添加到 `superarray` 中,`superarray` 是一個 `vector>`,每個元素是一個測試用例的數組。 - - 輸出: - - 確認已將未排序數組寫入文件(`UNSORTED`)。 - - 確認已創建當前測試用例(例如 `Created unsorted array for case #1`)。 +5. **輸出鍵值**: + - 遍歷 `array`,寫入鍵值到 `unsortedfile`。 + - 控制台確認寫入。 + +6. **儲存用例**: + - 將 `array` 加入 `superarray`,控制台確認創建。 +## 結構分析 + +- **參數**: + - `cases`:用例數。 + - `superarray`:儲存用例(傳引用)。 + - `unsortedfile`:未排序檔案。 + - `mode`:鍵值模式。 +- **資料結構**:`vector` 儲存用例,`entry` 含 `key` 和 `node`。 +- **控制流程**:雙層迴圈,條件分支設置鍵值。 +- **檔案 I/O**:格式化輸出鍵值。 --- ### 6. `main` 主程式 From 7e890ed3e59c88d45bfff6cdd3b18c50809daba1 Mon Sep 17 00:00:00 2001 From: Yangchenin41243245 <41243245@nfu.edu.tw> Date: Fri, 25 Apr 2025 20:40:45 +0800 Subject: [PATCH 33/41] Update report.md From 95c91f90c5906f3f8bebbfb63471f738d038d54c Mon Sep 17 00:00:00 2001 From: Yangchenin41243245 <41243245@nfu.edu.tw> Date: Fri, 25 Apr 2025 20:41:16 +0800 Subject: [PATCH 34/41] Update report.md --- homework1/report.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/homework1/report.md b/homework1/report.md index 232d19c..637b7b7 100644 --- a/homework1/report.md +++ b/homework1/report.md @@ -4,12 +4,12 @@ ## 目錄 -### 解題說明 -### 時鐘精度 -### 效能分析 -### 程式實作 -### 測試與驗證 -### 申論及開發報告 +### 1.解題說明 +### 2.時鐘精度 +### 3.效能分析 +### 4.程式實作 +### 5.測試與驗證 +### 6.申論及開發報告 --- From eb496ce166dc4ed8f4194e93e31cc6a851898994 Mon Sep 17 00:00:00 2001 From: Yangchenin41243245 <41243245@nfu.edu.tw> Date: Fri, 25 Apr 2025 21:03:00 +0800 Subject: [PATCH 35/41] Update report.md --- homework1/report.md | 94 ++++++++++++++++++++++++++++++++++----------- 1 file changed, 71 insertions(+), 23 deletions(-) diff --git a/homework1/report.md b/homework1/report.md index 637b7b7..5a3bf6d 100644 --- a/homework1/report.md +++ b/homework1/report.md @@ -496,16 +496,6 @@ void makeCases(int cases, vector> &superarray, FILE *unsortedfile, 6. **儲存用例**: - 將 `array` 加入 `superarray`,控制台確認創建。 -## 結構分析 - -- **參數**: - - `cases`:用例數。 - - `superarray`:儲存用例(傳引用)。 - - `unsortedfile`:未排序檔案。 - - `mode`:鍵值模式。 -- **資料結構**:`vector` 儲存用例,`entry` 含 `key` 和 `node`。 -- **控制流程**:雙層迴圈,條件分支設置鍵值。 -- **檔案 I/O**:格式化輸出鍵值。 --- ### 6. `main` 主程式 @@ -575,25 +565,83 @@ int main(void) #### 說明: -1. **模組化設計**: - - `main` 將初始化、用例生成、排序執行和結果輸出分離,邏輯清晰。 - - 排序演算法獨立於 `InsertionSort`、`QuickSort` 等函式,`main` 僅負責調度和結果處理。 +##### 移除舊檔案(清空資料) +```cpp +remove(SORTED); +remove(UNSORTED); +remove(TIMEREC); +``` +這段會刪除之前執行時產生的三個檔案: +- `sorted.txt`:儲存排序後資料 +- `tosort.txt`:儲存原始(未排序)資料 +- `timer.txt`:儲存每次排序的時間與記憶體紀錄 -2. **資料組織**: - - `superarray[4]` 是一個陣列,每個元素是一個 `vector>`,儲存某種排序演算法的 5 組測試用例。 - - 每個用例是 `vector`,包含 6000 個 `entry` 物件(鍵值和 `node` 指針)。 - - `result` 結構統一儲存排序結果,便於傳遞和處理。 +--- -3. **控制流程**: - - 使用雙層迴圈(`type` 和 `i`)遍歷演算法和用例,確保所有測試有序執行。 - - `switch` 結構簡單映射演算法類型到排序函式,但限制了擴展性。 +##### 開啟檔案進行輸出 +```cpp +FILE *f_Unsorted = fopen(UNSORTED, "a"); +FILE *f_Sorted = fopen(SORTED, "a"); +``` +開啟(或建立)用來輸出未排序與已排序資料的檔案。 -4. **檔案 I/O**: - - `tosort.txt` 儲存未排序鍵值,`sorted.txt` 儲存排序結果,`timer.txt` 由排序函式寫入性能數據。 - - 檔案操作分散(`makeCases` 寫入未排序資料,`main` 寫入排序結果,排序函式寫入性能),結構清晰但效率稍低。 +--- + +##### 初始化亂數與資料結構 +```cpp +srand(time(0)); +vector> superarray[4]; +result result; +``` +- `srand(time(0))`:初始化亂數種子。 +- `superarray[4]`:四種排序法的所有測試案例存放區,每個排序法都會有 5 組測資。 +- `result`:記錄每次排序後的時間與排序結果。 --- +##### 產生排序測試案例 +```cpp +makeCases(CASES, superarray[0], f_Unsorted, "INSERTION"); +makeCases(CASES, superarray[1], f_Unsorted, "QUICK"); +makeCases(CASES, superarray[2], f_Unsorted, "MERGE"); +makeCases(CASES, superarray[3], f_Unsorted, "HEAP"); +``` +每次呼叫 `makeCases()` 會生成 5 組測試資料,依不同排序類型使用不同排序特性資料: +- Insertion、Quick、Merge:worst-case 由大排到小(INSKEYS) +- Heap:隨機打亂 + +--- + +##### 執行排序與輸出結果 +```cpp +for (int type = 0; type < 4;type++){ + for (int i = 0, caseNum = 1; i < superarray[type].size(); i++, caseNum++){ + ... + result = InsertionSort(...); // 或其他排序 + ... + fprintf(f_Sorted, "...", caseNum, CASE_ITEMS, duration); + ... + result.arr2[j].outputkey(f_Sorted); + } +} +``` + +- 外層迴圈跑四種排序。 +- 內層跑每種排序對應的五筆資料。 +- 執行排序後: + - 印出花費的時間 + - 記錄記憶體用量 + - 將排序結果寫入檔案 + +--- + +##### 關閉檔案 +```cpp +fclose(f_Unsorted); +fclose(f_Sorted); +``` +--- + ## 測試與驗證 ### 最壞執行時間表格 Worst-case Performance (Time in Microseconds) From 8b4eab6eae26b2af2908f17fe50e9d7d14350588 Mon Sep 17 00:00:00 2001 From: Yangchenin41243245 <41243245@nfu.edu.tw> Date: Fri, 25 Apr 2025 21:04:11 +0800 Subject: [PATCH 36/41] Update report.md --- homework1/report.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/homework1/report.md b/homework1/report.md index 5a3bf6d..91d76d1 100644 --- a/homework1/report.md +++ b/homework1/report.md @@ -264,24 +264,24 @@ void heapSort(vector& arr); ##### 運作流程: -1. **記錄排序前記憶體**: +**記錄排序前記憶體**: ```cpp SIZE_T memBefore = memoryUsage(); ``` -2. **計時排序函式執行時間**: +**計時排序函式執行時間**: ```cpp double time = recordTime([&]() { sortFunc(data); }); ``` - 使用 lambda 封裝 `sortFunc(data)` 呼叫,以符合 `recordTime()` 的格式。 - Lambda 捕捉 `[&]` 表示引用捕捉,確保 `data` 被修改(這裡其實是複製進來的副本,不會影響原資料)。 -3. **記錄排序後記憶體**: +**記錄排序後記憶體**: ```cpp SIZE_T memAfter = memoryUsage(); ``` -4. **輸出與紀錄測試結果**: +**輸出與紀錄測試結果**: ```cpp cout << ...; // 印到螢幕 logFile << ...; // 寫入檔案 @@ -470,14 +470,14 @@ void makeCases(int cases, vector> &superarray, FILE *unsortedfile, #### 運作流程: -1. **迴圈生成用例**: +**迴圈生成用例**: - 外層迴圈迭代 `cases` 次(5 次)。 -2. **初始化陣列**: +**初始化陣列**: - 創建 `vector array`。 - 寫入用例標頭到 `unsortedfile`("Case X with 6000 items:")。 -3. **生成元素**: +**生成元素**: - 內層迴圈生成 6000 個 `entry`: - 分配 `node` 物件。 - 根據 `mode` 設置鍵值: @@ -486,14 +486,14 @@ void makeCases(int cases, vector> &superarray, FILE *unsortedfile, - 其他("HEAP"):序數(0, 1, ..., 5999)。 - 創建 `entry` 並加入 `array`。 -4. **隨機排列(Heap)**: +**隨機排列(Heap)**: - 若 `mode` 為 "HEAP",從索引 5999 到 2 隨機交換元素。 -5. **輸出鍵值**: +**輸出鍵值**: - 遍歷 `array`,寫入鍵值到 `unsortedfile`。 - 控制台確認寫入。 -6. **儲存用例**: +**儲存用例**: - 將 `array` 加入 `superarray`,控制台確認創建。 --- From c5f6904dde7155b64197024b50b197540176e2e4 Mon Sep 17 00:00:00 2001 From: Yangchenin41243245 <41243245@nfu.edu.tw> Date: Sun, 27 Apr 2025 15:40:58 +0800 Subject: [PATCH 37/41] Update report.md From 33aa6c796641cc57ad9575239e1735d2f07f2029 Mon Sep 17 00:00:00 2001 From: Yangchenin41243245 <41243245@nfu.edu.tw> Date: Mon, 28 Apr 2025 09:16:05 +0800 Subject: [PATCH 38/41] Update report.md --- homework1/report.md | 755 ++++++++++++++++++++++---------------------- 1 file changed, 375 insertions(+), 380 deletions(-) diff --git a/homework1/report.md b/homework1/report.md index 91d76d1..ebf7078 100644 --- a/homework1/report.md +++ b/homework1/report.md @@ -119,7 +119,6 @@ auto duration = chrono::duration_cast(end - start); ## 程式實作 - ### 一. 標頭與基本設置 ```cpp @@ -131,404 +130,453 @@ auto duration = chrono::duration_cast(end - start); #include #include #include -#include +#pragma comment(lib, "psapi.lib") -#pragma comment( lib, "psapi.lib" ) using namespace std; -#define CASE_ITEMS 500 // number of items in each case -#define CASES 5 // number of cases -#define RNGKEYS rand() % CASE_ITEMS -#define INSKEYS CASE_ITEMS - i // worst case for insertion sort -#define UNSORTED "D:/work/sort/tosort.txt" -#define SORTED "D:/work/sort/sorted.txt" -#define TIMEREC "D:/work/sort/timer.txt" +// 設定常數 +#define CASE_ITEMS 6000 +#define CASES 5 +#define UNSORTED "./tosort.txt" +#define SORTED "./sorted.txt" +#define TIMEREC "./timer.txt" +#define COMPOSITE_TIMEREC "./composite_timer.txt" ``` ---- - #### 說明: -##### 引入標頭檔 -- `iostream`, `vector`, `algorithm`, `string`:常用的 C++ 標準庫,處理輸出、容器、演算法與字串。 -- `ctime`, `cstdlib`:處理隨機數與時間。 -- `chrono`:計時用,計算排序時間。 -- `windows.h`, `psapi.h`:Windows API,用來查詢記憶體使用情況。 - -##### `#pragma comment(lib, "psapi.lib")` -讓 `psapi.h` 裡的函式在編譯時自動連結 `psapi.lib`,這是 Windows 的程式記憶體管理函式庫。 - -##### #define 指引 -- `CASE_ITEMS`:每筆測試資料裡要排序的元素數量(500)。 -- `CASES`:總共要做幾組測試(5 組)。 -- `RNGKEYS`:隨機 key,用於亂數測資。 -- `INSKEYS`:用於 Insertion Sort 最差情境的資料(由大到小,`CASE_ITEMS - i`)。 -- `UNSORTED`, `SORTED`, `TIMEREC`:三個檔案的儲存路徑(未排序、排序後、計時紀錄)。 +- **功能**:定義程式所需的標頭檔、命名空間及常數,為排序實作和效能測試提供基礎環境設置。 +- **標頭檔**: + - `iostream`:提供輸入輸出功能,用於檔案操作與結果顯示。 + - `vector`:用於動態陣列,儲存測試資料與排序結果。 + - `algorithm`:提供 STL `sort` 函數,用於快速排序、合併排序和堆排序的實現。 + - `ctime`, `cstdlib`:支援隨機數生成(`rand`)與時間種子(`time`),用於測試資料生成。 + - `chrono`:提供高精度計時工具(`high_resolution_clock`),用於測量排序執行時間。 + - `windows.h`, `psapi.h`:Windows API,用於記憶體使用量測量。 +- **#pragma comment**:自動連結 `psapi.lib`,提供記憶體資訊查詢功能。 +- **常數定義**: + - `CASE_ITEMS`:每個測試用例的元素數(6000)。 + - `CASES`:測試用例數(5)。 + - `UNSORTED`, `SORTED`, `TIMEREC`, `COMPOSITE_TIMEREC`:指定檔案路徑,分別儲存未排序資料、排序結果、排序時間記錄及 Composite Sort 時間記錄。 +- **程式特性**:設置統一的檔案路徑與測試參數,確保程式可重複執行並生成一致的測試資料與結果。相較於傳統實現,程式利用 STL `sort` 簡化部分排序演算法的實作。 --- -### 二. 記憶體與時間紀錄函式定義 +### 二. 資料結構與記憶體分析 ```cpp -// memory usage check -SIZE_T memoryUsage() { - PROCESS_MEMORY_COUNTERS pmc; - GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc)); - return pmc.WorkingSetSize; -} +struct node { int data; }; + +class entry { +public: + friend class node; + long key; + entry(long k, node* d) { key = k; data = d; } + void setkey(int k) { key = k; } + void outputkey(FILE* file) const { fprintf(file, " key: %ld\n", key); } +private: + node* data; +}; -// time recording -double recordTime(function sortFunc) { - auto start = chrono::high_resolution_clock::now(); - sortFunc(); // 執行排序 - auto end = chrono::high_resolution_clock::now(); - chrono::duration duration = end - start; - return duration.count(); +struct result { + int64_t timer; + vector arr2; +}; + +string printMem(int state) { + PROCESS_MEMORY_COUNTERS memInfo; + GetProcessMemoryInfo(GetCurrentProcess(), &memInfo, sizeof(memInfo)); + unsigned long long memUsage = memInfo.WorkingSetSize / 1024; + unsigned long long memPeak = memInfo.PeakWorkingSetSize / 1024; + string ret0 = "Memory usage: " + to_string(memUsage) + " KB\n"; + string ret1 = "Peak memory usage: " + to_string(memPeak) + " KB\n"; + if (state == 0) return ret0; + else return ret1; } ``` #### 說明: -##### `memoryUsage()` — 取得目前記憶體使用量 - -- 使用 Windows API: - - `GetProcessMemoryInfo()`:取得目前執行程序的記憶體資訊。 - - `pmc.WorkingSetSize`:代表目前正在使用的實體記憶體(Working Set),以「位元組 (bytes)」為單位。 -- 回傳型別是 `SIZE_T`,通常對應 `size_t`,足以儲存記憶體大小。 - -這個函式的用途是在排序前後量測記憶體用量。 +- **功能**:定義資料結構(`node`, `entry`, `result`)與記憶體分析函式(`printMem`),用於儲存測試資料、排序結果及測量記憶體使用量。 +- **資料結構**: + - `node`:簡單結構,包含單一整數 `data`,作為 `entry` 的附加資料。 + - `entry`:核心資料結構,包含排序鍵值 `key`(long 型別)及指向 `node` 的指標 `data`。提供 `setkey` 和 `outputkey` 方法,`outputkey` 為 const 方法,提升 const 正確性。 + - `result`:儲存排序結果,包含執行時間 `timer`(微秒,int64_t 型別)及排序後的陣列 `arr2`(`vector` 型別)。 +- **printMem 函式**: + - **功能**:使用 Windows API 的 `GetProcessMemoryInfo` 獲取程式記憶體使用資訊。 + - **參數**:`state`(int),`state == 0` 返回當前記憶體使用量,`state == 1` 返回峰值記憶體使用量。 + - **返回值**:字串,包含記憶體使用量(單位:KB)。 + - **實現細節**:透過 `PROCESS_MEMORY_COUNTERS` 結構獲取記憶體數據,轉換為 KB 並格式化為字串。 +- **程式特性**:結構化資料設計便於排序與輸出,記憶體分析函式提供即時與峰值記憶體使用量,支援效能比較。`entry` 的 const 正確性增強程式穩健性。 --- -##### `recordTime()` — 執行排序並記錄所花時間 - -- 接收一個 `function` 作為參數,也就是接受任何無參數、無回傳值的函式(或 lambda)。 -- 使用 `chrono::high_resolution_clock` 精準地記錄時間。 -- 回傳值是毫秒(`duration`)。 - -讓每種排序方法都可以包裝成 lambda 傳入這個函式中,方便統一計時。 - ---- +### 三. 排序函式實作 -### 三. 排序函式實作與統一測試介面 `runSort()` +#### Insertion Sort ```cpp -// 實作排序方法 -void insertionSort(vector& arr); -void quickSort(vector& arr); -void mergeSort(vector& arr); -void heapSort(vector& arr); - -// 測試用排序介面 -void runSort(string sortName, void (*sortFunc)(vector&), vector data, ofstream& logFile) { - SIZE_T memBefore = memoryUsage(); // 排序前記憶體 - double time = recordTime([&]() { sortFunc(data); }); // 排序時間 - SIZE_T memAfter = memoryUsage(); // 排序後記憶體 - - // 結果輸出 - cout << sortName << ": " << time << " ms, "; - cout << "Memory used: " << (memAfter - memBefore) / 1024 << " KB" << endl; - logFile << sortName << ": " << time << " ms, "; - logFile << "Memory used: " << (memAfter - memBefore) / 1024 << " KB" << endl; +result InsertionSort(vector arr, int casenum, bool writeToFile = true) { + result r; + cout << "Start insertion sort\n"; + auto timer = chrono::high_resolution_clock::now(); + string recMem_Init = printMem(0); + cout << recMem_Init << endl; + + for (int i = 1; i < arr.size(); i++) { + entry temp = arr[i]; + int j = i - 1; + while (j >= 0 && temp.key < arr[j].key) { + arr[j + 1] = arr[j]; + j--; + } + arr[j + 1] = temp; + } + + auto stop = chrono::high_resolution_clock::now(); + auto dur = chrono::duration_cast(stop - timer); + r.arr2 = arr; + r.timer = dur.count(); + string recMem_Fin = printMem(1); + + cout << "Sorted array in " << dur.count() << " microseconds\n"; + cout << recMem_Fin << endl; + + if (writeToFile) { + FILE *file = fopen(TIMEREC, "a"); + fprintf(file, "Start insertion sort case # %d \n[Init] %s", casenum, recMem_Init.c_str()); + fprintf(file, "Sorted case #%d with %lu items in %ld microseconds with Insertion\n[Final] %s\n", + casenum, CASE_ITEMS, dur.count(), recMem_Fin.c_str()); + fclose(file); + } + return r; } ``` ---- - #### 說明: -##### 排序函式宣告 - -```cpp -void insertionSort(vector& arr); -void quickSort(vector& arr); -void mergeSort(vector& arr); -void heapSort(vector& arr); -``` - -這四個函式是用來排序整數陣列的,採用傳參考方式以便直接在原始資料上操作。它們的實作在程式後面,這裡僅宣告。 - ---- - -##### `runSort(...)` — 統一測試框架 - -**目的**:統一測量排序時間與記憶體,並印出與紀錄結果。 - -**參數解析**: -- `sortName`: 排序法名稱,用於輸出。 -- `sortFunc`: 指向排序函式的函式指標(`void (*)(vector&)`)。 -- `data`: 排序資料,傳值進來,以避免修改原資料。 -- `logFile`: 日誌檔案參考,用來紀錄輸出結果。 +- **功能**:實現插入排序(Insertion Sort),將輸入陣列按鍵值(`key`)遞增排序,並記錄執行時間與記憶體使用量。 +- **參數**: + - `arr`:待排序的 `vector` 陣列,包含鍵值與資料指標。 + - `casenum`:測試用例編號,用於記錄輸出。 + - `writeToFile`:布林值,控制是否將效能指標寫入 `timer.txt`(預設為 true)。 +- **實現細節**: + - 排序邏輯:從第二個元素開始,逐一與前面的已排序部分比較,若鍵值小於前一元素則後移,直至找到正確插入位置。 + - 使用 `std::chrono::high_resolution_clock` 記錄排序時間,單位為微秒。 + - 透過 `printMem` 記錄初始與峰值記憶體使用量,若 `writeToFile` 為 true,寫入 `timer.txt`。 + - 輸出排序開始/結束資訊及時間到控制台。 +- **程式特性**: + - 穩定排序,適合小資料量(n ≤ 32)或近乎排序的資料。 + - 時間複雜度:最佳 O(n)(已排序),平均/最壞 O(n²)(隨機/逆序)。 + - 空間複雜度:O(1)(原地排序,僅需常數額外空間)。 + - 未優化比較或移動,適用於簡單場景。 --- -##### 運作流程: - -**記錄排序前記憶體**: - ```cpp - SIZE_T memBefore = memoryUsage(); - ``` +#### Quick Sort -**計時排序函式執行時間**: - ```cpp - double time = recordTime([&]() { sortFunc(data); }); - ``` - - 使用 lambda 封裝 `sortFunc(data)` 呼叫,以符合 `recordTime()` 的格式。 - - Lambda 捕捉 `[&]` 表示引用捕捉,確保 `data` 被修改(這裡其實是複製進來的副本,不會影響原資料)。 +```cpp +result QuickSort(vector arr, int casenum, bool writeToFile = true) { + result r; + cout << "Start quick sort\n"; + auto timer = chrono::high_resolution_clock::now(); + string recMem_Init = printMem(0); + cout << recMem_Init << endl; + + sort(arr.begin(), arr.end(), [](entry a, entry b) { return a.key < b.key; }); + + auto stop = chrono::high_resolution_clock::now(); + auto dur = chrono::duration_cast(stop - timer); + r.arr2 = arr; + r.timer = dur.count(); + string recMem_Fin = printMem(1); + + cout << "Sorted array in " << dur.count() << " microseconds\n"; + cout << recMem_Fin << endl; + + if (writeToFile) { + FILE *file = fopen(TIMEREC, "a"); + fprintf(file, "Start quick sort case # %d \n[Init] %s", casenum, recMem_Init.c_str()); + fprintf(file, "Sorted case #%d with %lu items in %ld microseconds with Quick\n[Final] %s\n", + casenum, CASE_ITEMS, dur.count(), recMem_Fin.c_str()); + fclose(file); + } + return r; +} +``` -**記錄排序後記憶體**: - ```cpp - SIZE_T memAfter = memoryUsage(); - ``` +#### 說明: -**輸出與紀錄測試結果**: - ```cpp - cout << ...; // 印到螢幕 - logFile << ...; // 寫入檔案 - ``` - 記憶體差異以 KB 為單位輸出,方便閱讀。 +- **功能**:實現快速排序(Quick Sort),使用 STL `sort` 按鍵值遞增排序,記錄時間與記憶體使用量。 +- **參數**: + - `arr`:待排序的 `vector` 陣列。 + - `casenum`:測試用例編號。 + - `writeToFile`:布林值,控制是否寫入 `timer.txt`。 +- **實現細節**: + - 使用 STL `sort` 函數,搭配 lambda 比較器(`a.key < b.key`)實現排序,取代傳統快速排序的分區和遞迴邏輯。 + - `sort` 內部實現為 introsort(混合快速排序、堆排序和插入排序),確保平均 O(n log n) 且避免最壞情況 O(n²)。 + - 使用 `std::chrono` 計時,記錄初始與峰值記憶體,若 `writeToFile` 為 true,寫入 `timer.txt`。 +- **程式特性**: + - 非穩定排序,依賴 STL 優化實現,平均效能優異(O(n log n))。 + - 空間複雜度:O(log n)(遞迴棧,平均)。 + - 簡化實現犧牲了對快速排序傳統行為的分析,但提升執行效率。 --- -### 四. 排序法實作 +#### Merge Sort ```cpp -void insertionSort(vector& arr) { - for (int i = 1; i < arr.size(); ++i) { - int key = arr[i], j = i - 1; - while (j >= 0 && arr[j] > key) arr[j + 1] = arr[j--]; - arr[j + 1] = key; - } -} - -void quickSort(vector& arr) { - function sort = [&](int l, int r) { - if (l >= r) return; - int pivot = arr[r], i = l - 1; - for (int j = l; j < r; ++j) - if (arr[j] < pivot) swap(arr[++i], arr[j]); - swap(arr[++i], arr[r]); - sort(l, i - 1); - sort(i + 1, r); - }; - sort(0, arr.size() - 1); -} - -void mergeSort(vector& arr) { - function sort = [&](int l, int r) { - if (l >= r) return; - int m = (l + r) / 2; - sort(l, m); sort(m + 1, r); - vector tmp; - int i = l, j = m + 1; - while (i <= m && j <= r) - tmp.push_back(arr[i] < arr[j] ? arr[i++] : arr[j++]); - while (i <= m) tmp.push_back(arr[i++]); - while (j <= r) tmp.push_back(arr[j++]); - for (int k = 0; k < tmp.size(); ++k) arr[l + k] = tmp[k]; - }; - sort(0, arr.size() - 1); -} - -void heapSort(vector& arr) { - function heapify = [&](int n, int i) { - int largest = i, l = 2 * i + 1, r = 2 * i + 2; - if (l < n && arr[l] > arr[largest]) largest = l; - if (r < n && arr[r] > arr[largest]) largest = r; - if (largest != i) { - swap(arr[i], arr[largest]); - heapify(n, largest); - } - }; - int n = arr.size(); - for (int i = n / 2 - 1; i >= 0; --i) heapify(n, i); - for (int i = n - 1; i > 0; --i) { - swap(arr[0], arr[i]); - heapify(i, 0); +result MergeSort(vector arr, int casenum, bool writeToFile = true) { + result r; + cout << "Start merge sort\n"; + auto timer = chrono::high_resolution_clock::now(); + string recMem_Init = printMem(0); + cout << recMem_Init << endl; + + sort(arr.begin(), arr.end(), [](entry a, entry b) { return a.key < b.key; }); + + auto stop = chrono::high_resolution_clock::now(); + auto dur = chrono::duration_cast(stop - timer); + r.arr2 = arr; + r.timer = dur.count(); + string recMem_Fin = printMem(1); + + cout << "Sorted array in " << dur.count() << " microseconds\n"; + cout << recMem_Fin << endl; + + if (writeToFile) { + FILE *file = fopen(TIMEREC, "a"); + fprintf(file, "Start merge sort case # %d \n[Init] %s", casenum, recMem_Init.c_str()); + fprintf(file, "Sorted case #%d with %lu items in %ld microseconds with Merge\n[Final] %s\n", + casenum, CASE_ITEMS, dur.count(), recMem_Fin.c_str()); + fclose(file); } + return r; } ``` #### 說明: -##### 1.Insertion Sort — 插入排序(O(n²)) - -```cpp -for (int i = 1; i < arr.size(); ++i) { - int key = arr[i], j = i - 1; - while (j >= 0 && arr[j] > key) arr[j + 1] = arr[j--]; - arr[j + 1] = key; -} -``` - -- 對每一個元素,往前比較並插入至適當位置。 -- 穩定排序,適合幾乎排序好的資料。 -- **時間複雜度**:平均與最壞為 O(n²),最佳為 O(n)。 +- **功能**:實現合併排序(Merge Sort),使用 STL `sort` 按鍵值遞增排序,記錄時間與記憶體使用量。 +- **參數**: + - `arr`:待排序的 `vector` 陣列。 + - `casenum`:測試用例編號。 + - `writeToFile`:布林值,控制是否寫入 `timer.txt`。 +- **實現細節**: + - 使用 STL `sort` 函數,搭配 lambda 比較器實現排序,取代傳統合併排序的分割和合併邏輯。 + - `sort` 提供穩定的 O(n log n) 效能,內部優化確保高效執行。 + - 使用 `std::chrono` 計時,記錄記憶體使用量,若 `writeToFile` 為 true,寫入 `timer.txt`。 +- **程式特性**: + - 非穩定排序(因 STL `sort` 非穩定),時間複雜度為 O(n log n)。 + - 空間複雜度:O(n)(STL `sort` 內部可能使用臨時陣列)。 + - 簡化實現便於維護,但無法反映傳統合併排序的遞迴特性。 --- -##### 2.Quick Sort — 快速排序(平均 O(n log n)) +#### Heap Sort ```cpp -function sort = [&](int l, int r) { - if (l >= r) return; - int pivot = arr[r], i = l - 1; - for (int j = l; j < r; ++j) - if (arr[j] < pivot) swap(arr[++i], arr[j]); - swap(arr[++i], arr[r]); - sort(l, i - 1); - sort(i + 1, r); -}; -sort(0, arr.size() - 1); +result HeapSort(vector arr, int casenum, bool writeToFile = true) { + result r; + cout << "Start heap sort\n"; + auto timer = chrono::high_resolution_clock::now(); + string recMem_Init = printMem(0); + cout << recMem_Init << endl; + + sort(arr.begin(), arr.end(), [](entry a, entry b) { return a.key < b.key; }); + + auto stop = chrono::high_resolution_clock::now(); + auto dur = chrono::duration_cast(stop - timer); + r.arr2 = arr; + r.timer = dur.count(); + string recMem_Fin = printMem(1); + + cout << "Sorted array in " << dur.count() << " microseconds\n"; + cout << recMem_Fin << endl; + + if (writeToFile) { + FILE *file = fopen(TIMEREC, "a"); + fprintf(file, "Start heap sort case # %d \n[Init] %s", casenum, recMem_Init.c_str()); + fprintf(file, "Sorted case #%d with %lu items in %ld microseconds with Heap\n[Final] %s\n", + casenum, CASE_ITEMS, dur.count(), recMem_Fin.c_str()); + fclose(file); + } + return r; +} ``` -- 以最後一個元素作為 pivot。 -- 小於 pivot 的元素會被移到左側,遞迴排序。 -- 不穩定排序,**在最壞情況(遞增/遞減序列)會退化為 O(n²)**,但一般實務中表現極佳。 +#### 說明: + +- **功能**:實現堆排序(Heap Sort),使用 STL `sort` 按鍵值遞增排序,記錄時間與記憶體使用量。 +- **參數**: + - `arr`:待排序的 `vector` 陣列。 + - `casenum`:測試用例編號。 + - `writeToFile`:布林值,控制是否寫入 `timer.txt`。 +- **實現細節**: + - 使用 STL `sort` 函數,搭配 lambda 比較器實現排序,取代傳統堆排序的建堆和調整邏輯。 + - `sort` 確保 O(n log n) 效能,內部優化提升執行速度。 + - 使用 `std::chrono` 計時,記錄記憶體使用量,若 `writeToFile` 為 true,寫入 `timer.txt`。 +- **程式特性**: + - 非穩定排序,時間複雜度為 O(n log n)。 + - 空間複雜度:O(1) 或 O(n)(取決於 STL `sort` 內部實現)。 + - 簡化實現犧牲了堆排序的原地排序特性,但提升執行效率。 --- -##### 3.Merge Sort — 合併排序(穩定、O(n log n)) +#### Composite Sort ```cpp -function sort = [&](int l, int r) { - if (l >= r) return; - int m = (l + r) / 2; - sort(l, m); sort(m + 1, r); - ... -}; -``` +result CompositeSort(vector arr, int casenum, bool writeToFile = true) { + result r; + string sort_name = "composite"; + cout << "Start " << sort_name << " sort\n"; + string memInit = printMem(0); + cout << memInit; + + FILE* file = nullptr; + if (writeToFile) { + file = fopen(COMPOSITE_TIMEREC, "a"); + fprintf(file, "Start Composite sort case # %d \n[Init] %s", casenum, memInit.c_str()); + } -- 將陣列遞迴切半後合併,合併時保持排序。 -- **空間複雜度 O(n)**,需要額外暫存空間。 -- 穩定排序,適合資料量大且對穩定性有要求的應用。 + if (arr.size() <= 32) { + r = InsertionSort(arr, casenum, false); + } else if (arr.size() > 32 && arr.size() <= 1000) { + r = HeapSort(arr, casenum, false); + } else if (arr.size() > 1000 && arr.size() <= 5000) { + r = HeapSort(arr, casenum, false); + } else { + r = QuickSort(arr, casenum, false); + } ---- + int64_t duration = r.timer; + string memFin = printMem(1); -##### 4.Heap Sort — 堆積排序(不穩定、O(n log n)) + if (writeToFile) { + fprintf(file, "Sorted case #%d with %lu items in %ld microseconds with Composite\n[Final] %s\n", + casenum, arr.size(), duration, memFin.c_str()); + fclose(file); + } -```cpp -function heapify = [&](int n, int i) { ... }; + cout << "Sorted array in " << duration << " microseconds\n"; + cout << memFin; + + return r; +} ``` -- 先建 max heap,再依序將最大值交換到尾端並縮小 heap 區間。 -- 不需要額外空間,**空間複雜度 O(1)**。 -- 時間複雜度穩定為 O(n log n),但常數大、快取不友善,實務上不如 QuickSort 快。 +#### 說明: + +- **功能**:實現複合排序(Composite Sort),根據輸入陣列大小動態選擇最適排序演算法,記錄時間與記憶體使用量。 +- **參數**: + - `arr`:待排序的 `vector` 陣列。 + - `casenum`:測試用例編號。 + - `writeToFile`:布林值,控制是否寫入 `composite_timer.txt`。 +- **實現細節**: + - 根據陣列大小選擇排序法: + - `n ≤ 32`:使用 `InsertionSort`,因小資料量下簡單高效。 + - `32 < n ≤ 1000`:使用 `HeapSort`,提供穩定的 O(n log n) 效能(實際使用 STL `sort`)。 + - `1000 < n ≤ 5000`:使用 `HeapSort`,適合中型資料(實際使用 STL `sort`)。 + - `n > 5000`:使用 `QuickSort`,平均效能佳(使用 STL `sort`)。 + - 選擇的排序函數以 `writeToFile = false` 執行,避免重複寫入 `timer.txt`。 + - 使用 `std::chrono` 計時,記錄記憶體使用量,若 `writeToFile` 為 true,寫入 `composite_timer.txt`。 +- **程式特性**: + - 結合各排序法優勢,適應不同資料規模。 + - 時間複雜度:隨選擇的排序法變化: + - `n ≤ 32`:O(n²)(Insertion Sort)。 + - `32 < n ≤ 5000`:O(n log n)(Heap Sort,實際為 STL `sort`)。 + - `n > 5000`:O(n log n)(Quick Sort,實際為 STL `sort`)。 + - 空間複雜度:隨排序法變化(O(1) 至 O(n))。 + - 閾值(32、1000、5000)基於經驗設定,細分中型資料範圍以優化效能,未來可進一步調整。 --- -### 五. makeCases 函數 — 測資生成器 +### 四. 生成測試用例 ```cpp -void makeCases(int cases, vector> &superarray, FILE *unsortedfile, string mode) -{ - for (int c = 0; c < cases; c++) // create cases - { +void makeCases(int cases, vector>& superarray, FILE* unsortedfile, string mode) { + for (int c = 0; c < cases; c++) { vector array; - long key; - fprintf(unsortedfile, "\nCase %d with %lu items:\n", c + 1, CASE_ITEMS); - for (int i = 0; i < CASE_ITEMS; i++) // create array with nodes - { - node *n = new node; - if (mode == "INSERTION" || mode == "QUICK" || mode == "MERGE") // generate key nums by mode - key = INSKEYS; //do worst case = max -> min + for (int i = 0; i < CASE_ITEMS; i++) { + node* n = new node; + long key; + if (mode == "INSERTION" || mode == "QUICK" || mode == "MERGE") + key = CASE_ITEMS - i; else if (mode == "RANDOM") - key = RNGKEYS; + key = rand() % CASE_ITEMS; else - key = i; // will process after array is created - entry e(key, n); // create entry with key and node + key = i; + entry e(key, n); array.push_back(e); } - if (mode == "HEAP") // do the Permutation() provided - { - for (int i = CASE_ITEMS - 1; i >= 2; i--) - { + if (mode == "HEAP") { + for (int i = CASE_ITEMS - 1; i >= 2; i--) { int j = rand() % i + 1; swap(array[i], array[j]); } } - for (int i = 0; i < CASE_ITEMS; i++) // output key of each node in array just made - { + for (int i = 0; i < CASE_ITEMS; i++) { array[i].outputkey(unsortedfile); } - cout << "output unsorted array to file" << UNSORTED << endl; - superarray.push_back(array); // add array to cases + cout << "output unsorted array to file " << UNSORTED << endl; + superarray.push_back(array); cout << "Created unsorted array for case #" << c + 1 << endl; } } ``` + #### 說明: +- **功能**:生成指定數量的測試用例,每個用例包含 `CASE_ITEMS`(6000)個 `entry` 物件,根據模式生成鍵值並輸出至未排序檔案,儲存於 `superarray`。 - **參數**: - - `cases`:用例數。 - - `superarray`:儲存用例(傳引用)。 - - `unsortedfile`:未排序檔案。 - - `mode`:鍵值模式。 -- **資料結構**:`vector` 儲存用例,`entry` 含 `key` 和 `node`。 -- **控制流程**:雙層迴圈,條件分支設置鍵值。 -- **檔案 I/O**:格式化輸出鍵值。 - -#### 運作流程: - -**迴圈生成用例**: - - 外層迴圈迭代 `cases` 次(5 次)。 - -**初始化陣列**: - - 創建 `vector array`。 - - 寫入用例標頭到 `unsortedfile`("Case X with 6000 items:")。 - -**生成元素**: - - 內層迴圈生成 6000 個 `entry`: - - 分配 `node` 物件。 - - 根據 `mode` 設置鍵值: - - "INSERTION"/"QUICK"/"MERGE":逆序(`INSKEYS`,6000, 5999, ..., 1)。 - - "RANDOM":隨機(`RNGKEYS`,未使用)。 - - 其他("HEAP"):序數(0, 1, ..., 5999)。 - - 創建 `entry` 並加入 `array`。 - -**隨機排列(Heap)**: - - 若 `mode` 為 "HEAP",從索引 5999 到 2 隨機交換元素。 - -**輸出鍵值**: - - 遍歷 `array`,寫入鍵值到 `unsortedfile`。 - - 控制台確認寫入。 - -**儲存用例**: - - 將 `array` 加入 `superarray`,控制台確認創建。 + - `cases`:生成用例數(預設 5)。 + - `superarray`:儲存測試用例的 `vector>`。 + - `unsortedfile`:未排序資料輸出檔案(`tosort.txt`)。 + - `mode`:鍵值生成模式(`"INSERTION"`, `"QUICK"`, `"MERGE"`, `"RANDOM"`, `"HEAP"`)。 +- **實現細節**: + - 根據 `mode` 生成鍵值: + - `"INSERTION"`, `"QUICK"`, `"MERGE"`:逆序鍵值(`CASE_ITEMS - i`),模擬最壞情況。 + - `"RANDOM"`:隨機鍵值(`rand() % CASE_ITEMS`),模擬平均情況。 + - `"HEAP"`:順序鍵值(`i`),後隨機打亂部分元素(從索引 2 開始)。 + - 每個 `entry` 包含鍵值與動態分配的 `node` 指標。 + - 使用 `outputkey` 將鍵值寫入 `unsortedfile`。 + - 將生成的陣列存入 `superarray`。 +- **程式特性**: + - 支援多種測試場景(最壞、平均、特定情況)。 + - 動態分配 `node`,需注意記憶體管理(本程式未顯式釋放)。 + - 隨機打亂邏輯僅針對 Heap Sort 優化,模擬部分亂序資料。 --- -### 6. `main` 主程式 +### 五. 主程式與測試流程 ```cpp -int main(void) -{ +int main(void) { remove(SORTED); - cout << "Removed old " << SORTED << endl; // remove old files + cout << "Removed old " << SORTED << endl; remove(UNSORTED); cout << "Removed old " << UNSORTED << endl; remove(TIMEREC); cout << "Removed old " << TIMEREC << endl; + remove(COMPOSITE_TIMEREC); + cout << "Removed old " << COMPOSITE_TIMEREC << endl; - FILE *f_Unsorted = fopen(UNSORTED, "a"); // create new files + FILE *f_Unsorted = fopen(UNSORTED, "a"); FILE *f_Sorted = fopen(SORTED, "a"); - srand(time(0)); // set random - vector> superarray[4]; // all cases stored here + srand(time(0)); + vector> superarray[5]; result result; - //MAKE CASES HERE///////////////////////////////////// + makeCases(CASES, superarray[0], f_Unsorted, "INSERTION"); makeCases(CASES, superarray[1], f_Unsorted, "QUICK"); makeCases(CASES, superarray[2], f_Unsorted, "MERGE"); - makeCases(CASES, superarray[3], f_Unsorted, "HEAP"); // create cases, provide file path for unsorted cases - ////////////////////////////////////////////////////// - //SORT ALL///////////////////////////////////////////////////////////////// - for (int type = 0; type < 4;type++){ //type m - for (int i = 0, caseNum = 1; i < superarray[type].size(); i++, caseNum++) // SA of type m with i cases - { - auto duration = 0; - switch (type) - { + makeCases(CASES, superarray[3], f_Unsorted, "HEAP"); + makeCases(CASES, superarray[4], f_Unsorted, "RANDOM"); + + for (int type = 0; type < 4; type++) { + for (int i = 0, caseNum = 1; i < superarray[type].size(); i++, caseNum++) { + switch (type) { case 0: result = InsertionSort(superarray[type][i], caseNum); break; @@ -541,105 +589,52 @@ int main(void) case 3: result = HeapSort(superarray[type][i], caseNum); break; - default: - break; } - duration = result.timer; // get duration from result struct - //OUTPUT TIME - fprintf(f_Sorted, "\nCase %d of %lu items finished in %lu microseconds\n", caseNum, CASE_ITEMS, duration); - for (int j = 0; j < CASE_ITEMS; j++) // output keys of sorted array - { - result.arr2[j].outputkey(f_Sorted); // i for array, j for entry + fprintf(f_Sorted, "\nCase %d of %lu items finished in %lu microseconds\n", caseNum, CASE_ITEMS, result.timer); + for (int j = 0; j < CASE_ITEMS; j++) { + result.arr2[j].outputkey(f_Sorted); } - cout << "output sorted array to file" << SORTED << endl; + cout << "output sorted array to file " << SORTED << endl; } } - ///////////////////////////////////////////////////////////////////////////////// + + for (int i = 0; i < superarray[4].size(); i++) { + int caseNum = i + 1; + result = CompositeSort(superarray[4][i], caseNum); + fprintf(f_Sorted, "\nComposite Sort Case %d finished in %ld us\n", caseNum, result.timer); + for (const auto& e : result.arr2) { + e.outputkey(f_Sorted); + } + cout << "output sorted array to file " << SORTED << endl; + } + fclose(f_Unsorted); fclose(f_Sorted); return 0; } ``` ---- - #### 說明: -##### 移除舊檔案(清空資料) -```cpp -remove(SORTED); -remove(UNSORTED); -remove(TIMEREC); -``` -這段會刪除之前執行時產生的三個檔案: -- `sorted.txt`:儲存排序後資料 -- `tosort.txt`:儲存原始(未排序)資料 -- `timer.txt`:儲存每次排序的時間與記憶體紀錄 - ---- - -##### 開啟檔案進行輸出 -```cpp -FILE *f_Unsorted = fopen(UNSORTED, "a"); -FILE *f_Sorted = fopen(SORTED, "a"); -``` -開啟(或建立)用來輸出未排序與已排序資料的檔案。 - ---- - -##### 初始化亂數與資料結構 -```cpp -srand(time(0)); -vector> superarray[4]; -result result; -``` -- `srand(time(0))`:初始化亂數種子。 -- `superarray[4]`:四種排序法的所有測試案例存放區,每個排序法都會有 5 組測資。 -- `result`:記錄每次排序後的時間與排序結果。 +- **功能**:主程式負責初始化環境、生成測試資料、執行排序演算法並輸出結果,統籌整個效能測試流程。 +- **實現細節**: + - **檔案管理**:移除舊檔案(`sorted.txt`, `tosort.txt`, `timer.txt`, `composite_timer.txt`),開啟新檔案進行輸出。 + - **隨機種子**:使用 `srand(time(0))` 初始化隨機數生成器,確保隨機資料可重現。 + - **測資生成**:調用 `makeCases` 生成五組測資,分別對應 Insertion Sort(逆序)、Quick Sort(逆序)、Merge Sort(逆序)、Heap Sort(部分亂序)及 Composite Sort(隨機)。 + - **排序執行**: + - 對前四組測資(`type = 0~3`)執行對應排序演算法(Insertion, Quick, Merge, Heap)。 + - 對第五組測資(隨機)執行 Composite Sort,單獨處理並輸出。 + - **結果輸出**: + - 排序結果(鍵值)與執行時間寫入 `sorted.txt`,Composite Sort 使用不同標頭(`"Composite Sort Case X finished in Y us"`)。 + - 時間與記憶體記錄分別寫入 `timer.txt`(單一排序法)與 `composite_timer.txt`(Composite Sort)。 + - 關閉檔案並結束程式。 +- **程式特性**: + - 模組化設計,清晰分離測資生成、排序執行與結果輸出。 + - 支援多種排序法與測試場景,方便擴展。 + - 未實現小資料量平均計時,適用於大資料量(n = 6000)測試。 + - 未處理動態分配的 `node` 記憶體釋放,可能導致記憶體洩漏。 --- - -##### 產生排序測試案例 -```cpp -makeCases(CASES, superarray[0], f_Unsorted, "INSERTION"); -makeCases(CASES, superarray[1], f_Unsorted, "QUICK"); -makeCases(CASES, superarray[2], f_Unsorted, "MERGE"); -makeCases(CASES, superarray[3], f_Unsorted, "HEAP"); -``` -每次呼叫 `makeCases()` 會生成 5 組測試資料,依不同排序類型使用不同排序特性資料: -- Insertion、Quick、Merge:worst-case 由大排到小(INSKEYS) -- Heap:隨機打亂 - ---- - -##### 執行排序與輸出結果 -```cpp -for (int type = 0; type < 4;type++){ - for (int i = 0, caseNum = 1; i < superarray[type].size(); i++, caseNum++){ - ... - result = InsertionSort(...); // 或其他排序 - ... - fprintf(f_Sorted, "...", caseNum, CASE_ITEMS, duration); - ... - result.arr2[j].outputkey(f_Sorted); - } -} -``` - -- 外層迴圈跑四種排序。 -- 內層跑每種排序對應的五筆資料。 -- 執行排序後: - - 印出花費的時間 - - 記錄記憶體用量 - - 將排序結果寫入檔案 - ---- - -##### 關閉檔案 -```cpp -fclose(f_Unsorted); -fclose(f_Sorted); -``` --- ## 測試與驗證 From 3ebd02b1562dc510e80f9eb12a5109e5ba075953 Mon Sep 17 00:00:00 2001 From: Yangchenin41243245 <41243245@nfu.edu.tw> Date: Mon, 28 Apr 2025 09:16:58 +0800 Subject: [PATCH 39/41] Update report.md --- homework1/report.md | 1 - 1 file changed, 1 deletion(-) diff --git a/homework1/report.md b/homework1/report.md index ebf7078..ae02629 100644 --- a/homework1/report.md +++ b/homework1/report.md @@ -635,7 +635,6 @@ int main(void) { - 未處理動態分配的 `node` 記憶體釋放,可能導致記憶體洩漏。 --- ---- ## 測試與驗證 From c0bd863e00fc8ab143f29a19c444204bc4a8e8a4 Mon Sep 17 00:00:00 2001 From: Yangchenin41243245 <41243245@nfu.edu.tw> Date: Mon, 28 Apr 2025 09:17:19 +0800 Subject: [PATCH 40/41] Update report.md --- homework1/report.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/homework1/report.md b/homework1/report.md index ae02629..e4f37e8 100644 --- a/homework1/report.md +++ b/homework1/report.md @@ -4,12 +4,12 @@ ## 目錄 -### 1.解題說明 -### 2.時鐘精度 -### 3.效能分析 -### 4.程式實作 -### 5.測試與驗證 -### 6.申論及開發報告 +### 解題說明 +### 時鐘精度 +### 效能分析 +### 程式實作 +### 測試與驗證 +### 申論及開發報告 --- From 2d41344345a49f926da7d468b384b541b3b23917 Mon Sep 17 00:00:00 2001 From: Yangchenin41243245 <41243245@nfu.edu.tw> Date: Mon, 28 Apr 2025 09:17:59 +0800 Subject: [PATCH 41/41] Update report.md --- homework1/report.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/homework1/report.md b/homework1/report.md index e4f37e8..2f08e10 100644 --- a/homework1/report.md +++ b/homework1/report.md @@ -4,12 +4,12 @@ ## 目錄 -### 解題說明 -### 時鐘精度 -### 效能分析 -### 程式實作 -### 測試與驗證 -### 申論及開發報告 +### 1. 解題說明 +### 2. 時鐘精度 +### 3. 效能分析 +### 4. 程式實作 +### 5. 測試與驗證 +### 6. 申論及開發報告 ---