diff --git a/bubblesort.cpp b/bubblesort.cpp new file mode 100644 index 0000000..25376aa --- /dev/null +++ b/bubblesort.cpp @@ -0,0 +1,62 @@ +#include +#include +#include +#include +#include +using namespace std; + +// ============================================= +// KONFIGURASI — ubah nilai di sini +// ============================================= +const int DATA_SIZE = 1000000; // jumlah elemen + +// Pilih kondisi: "avg", "best", atau "worst" +const string CASE = "best"; +// ============================================= + +void bubbleSort(vector& a) { + int n = a.size(); + for (int i = 0; i < n - 1; i++) { + bool swapped = false; + for (int j = 0; j < n - i - 1; j++) + if (a[j] > a[j+1]) { swap(a[j], a[j+1]); swapped = true; } + if (!swapped) break; + } +} + +vector generateData(int n, const string& caseType) { + vector data(n); + + if (caseType == "best") { + // Best case: sudah terurut ascending + for (int i = 0; i < n; i++) data[i] = i + 1; + + } else if (caseType == "worst") { + // Worst case: urutan terbalik (descending) + for (int i = 0; i < n; i++) data[i] = n - i; + + } else { + // Average case: acak (random shuffle) + for (int i = 0; i < n; i++) data[i] = i + 1; + mt19937 rng(42); + shuffle(data.begin(), data.end(), rng); + } + + return data; +} + +int main() { + vector data = generateData(DATA_SIZE, CASE); + + auto start = chrono::high_resolution_clock::now(); + bubbleSort(data); + auto end = chrono::high_resolution_clock::now(); + + double ms = chrono::duration(end - start).count(); + + cout << "Case : " << CASE << endl; + cout << "Size : " << DATA_SIZE << endl; + cout << "Waktu : " << ms << " ms" << endl; + + return 0; +} \ No newline at end of file diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..2e7dd44 --- /dev/null +++ b/main.cpp @@ -0,0 +1,417 @@ +/** + * ============================================================ + * ANALISIS KOMPLEKSITAS WAKTU - ALGORITMA PENGURUTAN + * ============================================================ + * Algoritma : Bubble Sort, Selection Sort, Merge Sort, Quick Sort + * Kasus : Best Case, Worst Case, Average Case + * Kompilasi : g++ -O0 -std=c++11 -o sort_benchmark sort_benchmark.cpp + * ============================================================ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace std::chrono; + +// ============================================================ +// KONFIGURASI +// ============================================================ + +const int SIZES[] = { 10000, 20000, 100000, 200000, 1000000, 2000000 }; +const int SIZES_COUNT = 6; + +const bool RUN_BUBBLE = true; +const bool RUN_SELECTION = true; +const bool RUN_MERGE = true; +const bool RUN_QUICK = true; + +const bool RUN_BEST = true; // sudah terurut naik +const bool RUN_WORST = true; // terurut terbalik +const bool RUN_AVERAGE = true; // acak + +// Seed random (0 = acak setiap run) +const unsigned int RANDOM_SEED = 42; + +// O(n^2) di-skip jika N melebihi ini +const int O2_LIMIT = 200000; + +// Lebar bar chart +const int BAR_WIDTH = 40; + +// Batas total waktu program (menit). Set 0 untuk nonaktifkan. +const double OVERTIME_MINUTES = 30.0; + +// ============================================================ +// SENTINEL +// -1 = skipped (N > O2_LIMIT) +// -2 = overtime +// ============================================================ +const double VAL_SKIP = -1.0; +const double VAL_OT = -2.0; + +// ============================================================ +// GENERATOR +// ============================================================ + +vector genBest(int n) { + vector d(n); + for (int i = 0; i < n; i++) d[i] = i + 1; + return d; +} + +vector genWorst(int n) { + vector d(n); + for (int i = 0; i < n; i++) d[i] = n - i; + return d; +} + +vector genAverage(int n) { + vector d(n); + for (int i = 0; i < n; i++) d[i] = i + 1; + mt19937 rng(RANDOM_SEED == 0 ? (unsigned int)random_device{}() : RANDOM_SEED); + shuffle(d.begin(), d.end(), rng); + return d; +} + +// ============================================================ +// ALGORITMA +// ============================================================ + +void bubbleSort(vector& a) { + int n = a.size(); + for (int i = 0; i < n - 1; i++) { + bool swapped = false; + for (int j = 0; j < n - i - 1; j++) + if (a[j] > a[j+1]) { swap(a[j], a[j+1]); swapped = true; } + if (!swapped) break; + } +} + +void selectionSort(vector& a) { + int n = a.size(); + for (int i = 0; i < n - 1; i++) { + int m = i; + for (int j = i + 1; j < n; j++) if (a[j] < a[m]) m = j; + if (m != i) swap(a[i], a[m]); + } +} + +void mergeHelp(vector& a, int l, int mid, int r) { + int n1 = mid-l+1, n2 = r-mid; + vector L(n1), R(n2); + for (int i = 0; i < n1; i++) L[i] = a[l+i]; + for (int j = 0; j < n2; j++) R[j] = a[mid+1+j]; + int i = 0, j = 0, k = l; + while (i < n1 && j < n2) a[k++] = (L[i] <= R[j]) ? L[i++] : R[j++]; + while (i < n1) a[k++] = L[i++]; + while (j < n2) a[k++] = R[j++]; +} + +void mergeRec(vector& a, int l, int r) { + if (l < r) { + int m = l + (r-l)/2; + mergeRec(a, l, m); mergeRec(a, m+1, r); + mergeHelp(a, l, m, r); + } +} + +void mergeSort(vector& a) { mergeRec(a, 0, (int)a.size()-1); } + +int qpartition(vector& a, int lo, int hi) { + int mid = lo + (hi-lo)/2; + if (a[lo] > a[mid]) swap(a[lo], a[mid]); + if (a[lo] > a[hi]) swap(a[lo], a[hi]); + if (a[mid] > a[hi]) swap(a[mid], a[hi]); + swap(a[mid], a[hi-1]); + int pivot = a[hi-1], i = lo-1; + for (int j = lo; j < hi-1; j++) if (a[j] <= pivot) swap(a[++i], a[j]); + swap(a[i+1], a[hi-1]); + return i+1; +} + +void quickRec(vector& a, int lo, int hi) { + if (lo < hi) { int p = qpartition(a,lo,hi); quickRec(a,lo,p-1); quickRec(a,p+1,hi); } +} + +void quickSort(vector& a) { if (a.size() > 1) quickRec(a, 0, (int)a.size()-1); } + +// ============================================================ +// TIMER +// ============================================================ + +typedef time_point TimePoint; + +double measure(void (*fn)(vector&), vector data) { + TimePoint s = high_resolution_clock::now(); + fn(data); + TimePoint e = high_resolution_clock::now(); + return duration(e - s).count(); +} + +double elapsed(const TimePoint& start) { + return duration(high_resolution_clock::now() - start).count(); +} + +bool overtime(const TimePoint& start) { + return OVERTIME_MINUTES > 0 && elapsed(start) >= OVERTIME_MINUTES * 60000.0; +} + +// ============================================================ +// FORMAT +// ============================================================ + +string fmtN(int n) { + if (n >= 1000000) return to_string(n/1000000) + ".000.000"; + if (n >= 1000) return to_string(n/1000) + ".000"; + return to_string(n); +} + +string fmtT(double ms) { + if (ms == VAL_OT) return " OVERTIME"; + if (ms == VAL_SKIP) return " SKIPPED"; + ostringstream o; o << fixed << setprecision(2) << ms << " ms"; + return o.str(); +} + +string makeBar(double ms, double maxVal) { + if (ms == VAL_SKIP) { + return string(BAR_WIDTH, ' ') + " (skipped)"; + } + if (ms == VAL_OT) { + string b; + for (int i = 0; i < BAR_WIDTH; i++) b += "\xe2\x96\x91"; // ░ + return b + " OVERTIME"; + } + int fill = (maxVal > 0) ? (int)((ms / maxVal) * BAR_WIDTH) : 0; + if (fill == 0 && ms > 0) fill = 1; + string b; + for (int i = 0; i < fill; i++) b += "\xe2\x96\x88"; // █ + for (int i = fill; i < BAR_WIDTH; i++) b += " "; + return b; +} + +void sep2(int w=72) { cout << string(w,'=') << "\n"; } +void sep1(int w=72) { cout << string(w,'-') << "\n"; } + +// ============================================================ +// STRUKTUR HASIL +// ============================================================ + +struct Result { + double best, worst, avg; + Result() : best(VAL_SKIP), worst(VAL_SKIP), avg(VAL_SKIP) {} +}; + +// ============================================================ +// CHART PER KASUS +// ============================================================ + +struct AlgoEntry { + string name; + bool enabled; + void (*fn)(vector&); +}; + +void printChart( + const string& label, + const vector& algos, + const map >& res, + int caseIdx // 0=best 1=worst 2=avg +) { + // Cari max (nilai nyata saja) + double maxVal = 0; + for (int a = 0; a < (int)algos.size(); a++) { + if (!algos[a].enabled) continue; + for (int i = 0; i < SIZES_COUNT; i++) { + const Result& r = res.at(algos[a].name).at(SIZES[i]); + double v = (caseIdx==0)?r.best:(caseIdx==1)?r.worst:r.avg; + if (v > 0 && v > maxVal) maxVal = v; + } + } + + cout << "\n"; sep2(); + cout << " " << label << "\n"; sep2(); + + for (int a = 0; a < (int)algos.size(); a++) { + if (!algos[a].enabled) continue; + cout << "\n " << algos[a].name << "\n"; sep1(); + for (int i = 0; i < SIZES_COUNT; i++) { + const Result& r = res.at(algos[a].name).at(SIZES[i]); + double v = (caseIdx==0)?r.best:(caseIdx==1)?r.worst:r.avg; + cout << " " << setw(10) << left << fmtN(SIZES[i]) + << " | " << makeBar(v, maxVal) << "\n"; + } + cout << "\n"; + } +} + +// ============================================================ +// TABEL RINGKASAN +// ============================================================ + +void printTable( + const vector& algos, + const map >& res +) { + cout << "\n"; sep2(); + cout << " TABEL RINGKASAN WAKTU (ms)\n"; sep2(); + cout << left + << setw(14) << "Algoritma" + << setw(12) << "N" + << setw(18) << "Best Case" + << setw(18) << "Worst Case" + << setw(18) << "Avg Case" + << "\n"; + sep1(); + for (int a = 0; a < (int)algos.size(); a++) { + if (!algos[a].enabled) continue; + bool first = true; + for (int i = 0; i < SIZES_COUNT; i++) { + const Result& r = res.at(algos[a].name).at(SIZES[i]); + cout << left + << setw(14) << (first ? algos[a].name : "") + << setw(12) << fmtN(SIZES[i]) + << setw(18) << fmtT(r.best) + << setw(18) << fmtT(r.worst) + << setw(18) << fmtT(r.avg) + << "\n"; + first = false; + } + sep1(); + } +} + +// ============================================================ +// MAIN +// ============================================================ + +int main() { + vector algos; + AlgoEntry e; + + e.name="BubbleSort"; e.enabled=RUN_BUBBLE; e.fn=bubbleSort; algos.push_back(e); + e.name="SelectSort"; e.enabled=RUN_SELECTION; e.fn=selectionSort; algos.push_back(e); + e.name="MergeSort"; e.enabled=RUN_MERGE; e.fn=mergeSort; algos.push_back(e); + e.name="QuickSort"; e.enabled=RUN_QUICK; e.fn=quickSort; algos.push_back(e); + + // Inisialisasi hasil + map > results; + for (int a = 0; a < (int)algos.size(); a++) + for (int i = 0; i < SIZES_COUNT; i++) + results[algos[a].name][SIZES[i]] = Result(); + + // ── Phase 1: Benchmark ───────────────────────────────────── + TimePoint prog_start = high_resolution_clock::now(); + bool ot = false; + + cout << "\n"; sep2(); + cout << " BENCHMARK SEDANG BERJALAN...\n"; + if (OVERTIME_MINUTES > 0) + cout << " Batas waktu : " << OVERTIME_MINUTES << " menit\n"; + sep2(); cout << "\n"; + + for (int a = 0; a < (int)algos.size(); a++) { + if (!algos[a].enabled) continue; + const string& name = algos[a].name; + + for (int i = 0; i < SIZES_COUNT; i++) { + int n = SIZES[i]; + bool sizeSkip = (name=="BubbleSort"||name=="SelectSort") && n > O2_LIMIT; + Result r; + + if (sizeSkip) { + cout << " [--] " << setw(12) << name + << " N=" << setw(10) << fmtN(n) + << " | skipped (N > " << fmtN(O2_LIMIT) << ")\n"; + cout.flush(); + results[name][n] = r; + continue; + } + + // Best + if (RUN_BEST) { + if (ot) { + r.best = VAL_OT; + cout << " [!!] " << setw(12) << name << " N=" << setw(10) << fmtN(n) << " | Best = OVERTIME\n"; + } else { + r.best = measure(algos[a].fn, genBest(n)); + cout << " [OK] " << setw(12) << name << " N=" << setw(10) << fmtN(n) << " | Best = " << fmtT(r.best) << "\n"; + if (overtime(prog_start)) { + ot = true; + cout << "\n [!!] OVERTIME — " << fixed << setprecision(1) + << elapsed(prog_start)/60000.0 << " menit" + << " (batas: " << OVERTIME_MINUTES << " menit)\n" + << " [!!] Sisa run ditandai OVERTIME.\n\n"; + } + } + } + + // Worst + if (RUN_WORST) { + if (ot) { + r.worst = VAL_OT; + cout << " [!!] " << setw(12) << name << " N=" << setw(10) << fmtN(n) << " | Worst = OVERTIME\n"; + } else { + r.worst = measure(algos[a].fn, genWorst(n)); + cout << " [OK] " << setw(12) << name << " N=" << setw(10) << fmtN(n) << " | Worst = " << fmtT(r.worst) << "\n"; + if (overtime(prog_start)) { + ot = true; + cout << "\n [!!] OVERTIME — " << fixed << setprecision(1) + << elapsed(prog_start)/60000.0 << " menit" + << " (batas: " << OVERTIME_MINUTES << " menit)\n" + << " [!!] Sisa run ditandai OVERTIME.\n\n"; + } + } + } + + // Average + if (RUN_AVERAGE) { + if (ot) { + r.avg = VAL_OT; + cout << " [!!] " << setw(12) << name << " N=" << setw(10) << fmtN(n) << " | Average = OVERTIME\n"; + } else { + r.avg = measure(algos[a].fn, genAverage(n)); + cout << " [OK] " << setw(12) << name << " N=" << setw(10) << fmtN(n) << " | Average = " << fmtT(r.avg) << "\n"; + if (overtime(prog_start)) { + ot = true; + cout << "\n [!!] OVERTIME — " << fixed << setprecision(1) + << elapsed(prog_start)/60000.0 << " menit" + << " (batas: " << OVERTIME_MINUTES << " menit)\n" + << " [!!] Sisa run ditandai OVERTIME.\n\n"; + } + } + } + + results[name][n] = r; + cout.flush(); + } + cout << "\n"; + } + + // ── Phase 2: Chart per kasus ─────────────────────────────── + if (RUN_BEST) printChart("BEST CASE (array sudah terurut naik)", algos, results, 0); + if (RUN_WORST) printChart("WORST CASE (array terurut terbalik)", algos, results, 1); + if (RUN_AVERAGE) printChart("AVERAGE CASE (array acak / random)", algos, results, 2); + + // ── Phase 3: Tabel ───────────────────────────────────────── + printTable(algos, results); + + // ── Footer ───────────────────────────────────────────────── + double total = elapsed(prog_start); + cout << "\n"; sep2(); + if (ot) cout << " [!!] BENCHMARK DIHENTIKAN KARENA OVERTIME\n"; + cout << " Total waktu : " << fixed << setprecision(2) + << total/1000.0 << " detik (" << total/60000.0 << " menit)\n"; + cout << " Bar : \xe2\x96\x88 = normal \xe2\x96\x91 = OVERTIME (skipped) = N > batas\n"; + sep2(); cout << "\n"; + + return 0; +} \ No newline at end of file diff --git a/mergesort.cpp b/mergesort.cpp new file mode 100644 index 0000000..9546646 --- /dev/null +++ b/mergesort.cpp @@ -0,0 +1,76 @@ +#include +#include +#include +#include +#include +using namespace std; + +// ============================================= +// KONFIGURASI — ubah nilai di sini +// ============================================= +const int DATA_SIZE = 10000; // jumlah elemen + +// Pilih kondisi: "avg", "best", atau "worst" +const string CASE = "worst"; +// ============================================= + +void mergeHelp(vector& a, int l, int mid, int r) { + int n1 = mid - l + 1, n2 = r - mid; + vector L(n1), R(n2); + for (int i = 0; i < n1; i++) L[i] = a[l + i]; + for (int j = 0; j < n2; j++) R[j] = a[mid + 1 + j]; + int i = 0, j = 0, k = l; + while (i < n1 && j < n2) a[k++] = (L[i] <= R[j]) ? L[i++] : R[j++]; + while (i < n1) a[k++] = L[i++]; + while (j < n2) a[k++] = R[j++]; +} + +void mergeRec(vector& a, int l, int r) { + if (l < r) { + int m = l + (r - l) / 2; + mergeRec(a, l, m); + mergeRec(a, m + 1, r); + mergeHelp(a, l, m, r); + } +} + +void mergeSort(vector& a) { + mergeRec(a, 0, (int)a.size() - 1); +} + +vector generateData(int n, const string& caseType) { + vector data(n); + + if (caseType == "best") { + // Best case: sudah terurut ascending + for (int i = 0; i < n; i++) data[i] = i + 1; + + } else if (caseType == "worst") { + // Worst case: urutan terbalik (descending) + for (int i = 0; i < n; i++) data[i] = n - i; + + } else { + // Average case: acak (random shuffle) + for (int i = 0; i < n; i++) data[i] = i + 1; + mt19937 rng(42); + shuffle(data.begin(), data.end(), rng); + } + + return data; +} + +int main() { + vector data = generateData(DATA_SIZE, CASE); + + auto start = chrono::high_resolution_clock::now(); + mergeSort(data); + auto end = chrono::high_resolution_clock::now(); + + double ms = chrono::duration(end - start).count(); + + cout << "Case : " << CASE << endl; + cout << "Size : " << DATA_SIZE << endl; + cout << "Waktu : " << ms << " ms" << endl; + + return 0; +} \ No newline at end of file diff --git a/quicksort.cpp b/quicksort.cpp new file mode 100644 index 0000000..6b42767 --- /dev/null +++ b/quicksort.cpp @@ -0,0 +1,70 @@ +#include +#include +#include +#include +#include +using namespace std; + +// ============================================= +// KONFIGURASI — ubah nilai di sini +// ============================================= +const int DATA_SIZE = 1000000; // jumlah elemen + +// Pilih kondisi: "avg", "best", atau "worst" +const string CASE = "worst"; +// ============================================= + +int qpartition(vector& a, int lo, int hi) { + int mid = lo + (hi-lo)/2; + if (a[lo] > a[mid]) swap(a[lo], a[mid]); + if (a[lo] > a[hi]) swap(a[lo], a[hi]); + if (a[mid] > a[hi]) swap(a[mid], a[hi]); + swap(a[mid], a[hi-1]); + int pivot = a[hi-1], i = lo-1; + for (int j = lo; j < hi-1; j++) if (a[j] <= pivot) swap(a[++i], a[j]); + swap(a[i+1], a[hi-1]); + return i+1; +} + +void quickRec(vector& a, int lo, int hi) { + if (lo < hi) { int p = qpartition(a,lo,hi); quickRec(a,lo,p-1); quickRec(a,p+1,hi); } +} + +void quickSort(vector& a) { if (a.size() > 1) quickRec(a, 0, (int)a.size()-1); } + +vector generateData(int n, const string& caseType) { + vector data(n); + + if (caseType == "best") { + // Best case: sudah terurut ascending + for (int i = 0; i < n; i++) data[i] = i + 1; + + } else if (caseType == "worst") { + // Worst case: urutan terbalik (descending) + for (int i = 0; i < n; i++) data[i] = n - i; + + } else { + // Average case: acak (random shuffle) + for (int i = 0; i < n; i++) data[i] = i + 1; + mt19937 rng(42); + shuffle(data.begin(), data.end(), rng); + } + + return data; +} + +int main() { + vector data = generateData(DATA_SIZE, CASE); + + auto start = chrono::high_resolution_clock::now(); + quickSort(data); + auto end = chrono::high_resolution_clock::now(); + + double ms = chrono::duration(end - start).count(); + + cout << "Case : " << CASE << endl; + cout << "Size : " << DATA_SIZE << endl; + cout << "Waktu : " << ms << " ms" << endl; + + return 0; +} \ No newline at end of file diff --git a/selectionsort.cpp b/selectionsort.cpp new file mode 100644 index 0000000..75e00be --- /dev/null +++ b/selectionsort.cpp @@ -0,0 +1,61 @@ +#include +#include +#include +#include +#include +using namespace std; + +// ============================================= +// KONFIGURASI — ubah nilai di sini +// ============================================= +const int DATA_SIZE = 1000000; // jumlah elemen + +// Pilih kondisi: "avg", "best", atau "worst" +const string CASE = "best"; +// ============================================= + +void selectionSort(vector& a) { + int n = a.size(); + for (int i = 0; i < n - 1; i++) { + int m = i; + for (int j = i + 1; j < n; j++) if (a[j] < a[m]) m = j; + if (m != i) swap(a[i], a[m]); + } +} + +vector generateData(int n, const string& caseType) { + vector data(n); + + if (caseType == "best") { + // Best case: sudah terurut ascending + for (int i = 0; i < n; i++) data[i] = i + 1; + + } else if (caseType == "worst") { + // Worst case: urutan terbalik (descending) + for (int i = 0; i < n; i++) data[i] = n - i; + + } else { + // Average case: acak (random shuffle) + for (int i = 0; i < n; i++) data[i] = i + 1; + mt19937 rng(42); + shuffle(data.begin(), data.end(), rng); + } + + return data; +} + +int main() { + vector data = generateData(DATA_SIZE, CASE); + + auto start = chrono::high_resolution_clock::now(); + selectionSort(data); + auto end = chrono::high_resolution_clock::now(); + + double ms = chrono::duration(end - start).count(); + + cout << "Case : " << CASE << endl; + cout << "Size : " << DATA_SIZE << endl; + cout << "Waktu : " << ms << " ms" << endl; + + return 0; +} \ No newline at end of file