first commit

This commit is contained in:
2025-12-18 16:00:22 +08:00
commit 785f306726
69 changed files with 33171 additions and 0 deletions

View File

@ -0,0 +1,35 @@
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
/*
* function : implement bubble sort
* param nums : the vector to be sorted
* param comp_count : count of comparisons
* param move_count : count of moves
* return : ---
*/
void BubbleSort(vector<int> &nums, long long &comp_count, long long &move_count)
{
bool swapped;
for (int i = 0; i < static_cast<int>(nums.size()) - 1; i++)
{
swapped = false;
for (int j = 0; j < static_cast<int>(nums.size()) - 1 - i; j++)
{
comp_count++;
if (nums[j] > nums[j + 1])
{
swap(nums[j], nums[j + 1]);
move_count += 3; // std::swap is 3 moves
swapped = true;
}
}
if (!swapped)
{
break;
}
}
}

View File

@ -0,0 +1,67 @@
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
void dualPivotQuickSortHelper(vector<int>& nums, int left, int right, long long& comp_count, long long& move_count) {
if (left < right) {
comp_count++;
if (nums[left] > nums[right]) {
swap(nums[left], nums[right]);
move_count += 3;
}
int p = nums[left], q = nums[right];
int l = left + 1, g = right - 1, k = l;
while (k <= g) {
comp_count++;
if (nums[k] < p) {
swap(nums[k], nums[l]);
move_count += 3;
l++;
} else {
comp_count++;
if (nums[k] > q) {
while (k < g && (comp_count++, nums[g] > q)) {
g--;
}
swap(nums[k], nums[g]);
move_count += 3;
g--;
comp_count++;
if (nums[k] < p) {
swap(nums[k], nums[l]);
move_count += 3;
l++;
}
}
}
k++;
}
l--;
g++;
swap(nums[left], nums[l]);
move_count += 3;
swap(nums[right], nums[g]);
move_count += 3;
dualPivotQuickSortHelper(nums, left, l - 1, comp_count, move_count);
dualPivotQuickSortHelper(nums, l + 1, g - 1, comp_count, move_count);
dualPivotQuickSortHelper(nums, g + 1, right, comp_count, move_count);
}
}
/*
* function : implement dual pivot quick sort
* param nums : the vector to be sorted
* param comp_count : count of comparisons
* param move_count : count of moves
* return : ---
*/
void DualPivotQuickSort(vector<int>& nums, long long& comp_count, long long& move_count) {
if (nums.empty()) return;
dualPivotQuickSortHelper(nums, 0, nums.size() - 1, comp_count, move_count);
}

View File

@ -0,0 +1,33 @@
#include <iostream>
#include <vector>
#include <algorithm>
#include <ctime>
#include <cstdlib>
#include <stack>
#include <string>
#include <cstring>
using namespace std;
/*
* function : implement insert sort
* param nums : the vector to be sorted
* param comp_count : count of comparisons
* param move_count : count of moves
* return : ---
*/
void InsertSort(vector<int> &nums, long long &comp_count, long long &move_count)
{
for (int i = 1; i < static_cast<int>(nums.size()); i++)
{
int key = nums[i];
int j = i - 1;
while (j >= 0 && (comp_count++, nums[j] > key))
{
nums[j + 1] = nums[j];
move_count++;
j--;
}
nums[j + 1] = key;
move_count++;
}
}

View File

@ -0,0 +1,36 @@
# Compiler and flags
CXX := g++
CXXFLAGS := -std=c++11 -Wall -O2
# Executable name
TARGET := sorting_experiment
# Source files
# Automatically find all .cpp files in the current directory
SRCS := $(wildcard *.cpp)
# Object files
# Replace .cpp extension with .o
OBJS := $(SRCS:.cpp=.o)
# Default target
all: $(TARGET)
# Link the program
$(TARGET): $(OBJS)
$(CXX) $(CXXFLAGS) -o $(TARGET) $(OBJS)
# Compile source files into object files
%.o: %.cpp
$(CXX) $(CXXFLAGS) -c $< -o $@
# Target to run the experiment
run: all
@./$(TARGET)
# Clean up build files
clean:
rm -f $(TARGET) $(OBJS)
# Phony targets
.PHONY: all clean run

View File

@ -0,0 +1,86 @@
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
void merge(vector<int> &nums, int left, int mid, int right, long long &comp_count, long long &move_count)
{
int n1 = mid - left + 1;
int n2 = right - mid;
vector<int> L(n1), R(n2);
for (int i = 0; i < n1; i++)
{
L[i] = nums[left + i];
move_count++;
}
for (int j = 0; j < n2; j++)
{
R[j] = nums[mid + 1 + j];
move_count++;
}
int i = 0;
int j = 0;
int k = left;
while (i < n1 && j < n2)
{
comp_count++;
if (L[i] <= R[j])
{
nums[k] = L[i];
move_count++;
i++;
}
else
{
nums[k] = R[j];
move_count++;
j++;
}
k++;
}
while (i < n1)
{
nums[k] = L[i];
move_count++;
i++;
k++;
}
while (j < n2)
{
nums[k] = R[j];
move_count++;
j++;
k++;
}
}
void mergeSortHelper(vector<int> &nums, int left, int right, long long &comp_count, long long &move_count)
{
if (left < right)
{
int mid = left + (right - left) / 2;
mergeSortHelper(nums, left, mid, comp_count, move_count);
mergeSortHelper(nums, mid + 1, right, comp_count, move_count);
merge(nums, left, mid, right, comp_count, move_count);
}
}
/*
* function : implement merge sort
* param nums : the vector to be sorted
* param comp_count : count of comparisons
* param move_count : count of moves
* return : ---
*/
void MergeSort(vector<int> &nums, long long &comp_count, long long &move_count)
{
if (nums.empty()) return;
mergeSortHelper(nums, 0, nums.size() - 1, comp_count, move_count);
}

View File

@ -0,0 +1,48 @@
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int partition(vector<int> &nums, int low, int high, long long &comp_count, long long &move_count)
{
int pivot = nums[high];
int i = (low - 1);
for (int j = low; j <= high - 1; j++)
{
comp_count++;
if (nums[j] < pivot)
{
i++;
swap(nums[i], nums[j]);
move_count += 3;
}
}
swap(nums[i + 1], nums[high]);
move_count += 3;
return (i + 1);
}
void quickSortHelper(vector<int> &nums, int low, int high, long long &comp_count, long long &move_count)
{
if (low < high)
{
int pi = partition(nums, low, high, comp_count, move_count);
quickSortHelper(nums, low, pi - 1, comp_count, move_count);
quickSortHelper(nums, pi + 1, high, comp_count, move_count);
}
}
/*
* function : implement quick sort
* param nums : the vector to be sorted
* param comp_count : count of comparisons
* param move_count : count of moves
* return : ---
*/
void QuickSort(vector<int> &nums, long long &comp_count, long long &move_count)
{
if (nums.empty()) return;
quickSortHelper(nums, 0, nums.size() - 1, comp_count, move_count);
}

View File

@ -0,0 +1,70 @@
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
/*
* This function partitions the array into three parts:
* a) arr[l..i] contains all elements smaller than pivot
* b) arr[i+1..j-1] contains all elements equal to pivot
* c) arr[j..r] contains all elements greater than pivot
*/
void partition3Way(vector<int> &nums, int l, int r, int &i, int &j, long long &comp_count, long long &move_count)
{
// To handle cases where array is sorted or nearly sorted
int mid = l + (r - l) / 2;
swap(nums[mid], nums[l]);
move_count += 3;
i = l, j = r + 1;
int pivot = nums[l];
while (true)
{
while (comp_count++, nums[++i] < pivot)
if (i == r) break;
while (comp_count++, pivot < nums[--j])
if (j == l) break;
// check if pointers cross
if (i >= j) break;
swap(nums[i], nums[j]);
move_count += 3;
}
swap(nums[l], nums[j]);
move_count += 3;
i = j; // Return the pivot position
}
void quickSort3WayHelper(vector<int> &nums, int low, int high, long long &comp_count, long long &move_count)
{
if (high <= low) return;
int i, j;
partition3Way(nums, low, high, i, j, comp_count, move_count);
quickSort3WayHelper(nums, low, i - 1, comp_count, move_count);
quickSort3WayHelper(nums, i + 1, high, comp_count, move_count);
}
/*
* function : implement 3-way quick sort
* param nums : the vector to be sorted
* param comp_count : count of comparisons
* param move_count : count of moves
* return : ---
*/
void QuickSort3Way(vector<int> &nums, long long &comp_count, long long &move_count)
{
if (nums.empty()) return;
// For better performance on random data, shuffle is recommended, but we'll skip for this experiment
// random_shuffle(nums.begin(), nums.end());
quickSort3WayHelper(nums, 0, nums.size() - 1, comp_count, move_count);
}

View File

@ -0,0 +1,81 @@
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
// Helper to find median of three and swap pivot to the end
int medianOfThree(vector<int> &nums, int low, int high, long long &comp_count, long long &move_count)
{
int mid = low + (high - low) / 2;
comp_count++;
if (nums[low] > nums[mid])
{
swap(nums[low], nums[mid]);
move_count += 3;
}
comp_count++;
if (nums[low] > nums[high])
{
swap(nums[low], nums[high]);
move_count += 3;
}
comp_count++;
if (nums[mid] > nums[high])
{
swap(nums[mid], nums[high]);
move_count += 3;
}
// Now nums[low] <= nums[mid] <= nums[high]
// Pivot is nums[mid], but we swap it with nums[high-1] to use in partition
// The partition logic will ignore the already sorted nums[low] and nums[high]
swap(nums[mid], nums[high]); // Move pivot to the end
move_count += 3;
return nums[high];
}
int partition_optimized(vector<int> &nums, int low, int high, long long &comp_count, long long &move_count)
{
int pivot = medianOfThree(nums, low, high, comp_count, move_count);
int i = (low - 1);
for (int j = low; j <= high - 1; j++)
{
comp_count++;
if (nums[j] < pivot)
{
i++;
swap(nums[i], nums[j]);
move_count += 3;
}
}
swap(nums[i + 1], nums[high]);
move_count += 3;
return (i + 1);
}
void quickSortOptimizedHelper(vector<int> &nums, int low, int high, long long &comp_count, long long &move_count)
{
if (low < high)
{
int pi = partition_optimized(nums, low, high, comp_count, move_count);
quickSortOptimizedHelper(nums, low, pi - 1, comp_count, move_count);
quickSortOptimizedHelper(nums, pi + 1, high, comp_count, move_count);
}
}
/*
* function : implement quick sort with median-of-three pivot selection
* param nums : the vector to be sorted
* param comp_count : count of comparisons
* param move_count : count of moves
* return : ---
*/
void QuickSortOptimized(vector<int> &nums, long long &comp_count, long long &move_count)
{
if (nums.empty()) return;
quickSortOptimizedHelper(nums, 0, nums.size() - 1, comp_count, move_count);
}

View File

@ -0,0 +1,82 @@
# 排序算法性能分析项目
本项目使用 C++ 实现并深入分析了多种经典排序算法的性能。项目提供了一个自动化测试框架,能够在不同规模的随机生成数据集上对每种算法进行测试,并精确测量其**执行时间**、**比较次数**和**移动次数**,以供量化分析。
## 项目内容与实现方式
### 1. 模块化的算法实现
为了保证代码的清晰度和可扩展性,每一种排序算法都作为独立的模块在各自的 `.cpp` 文件中实现。所有算法的函数声明都统一在 `algorithm.h` 头文件中进行管理。
这种结构使得添加新的算法或修改现有算法变得非常简单。
### 2. 精确的性能指标统计
为了超越单纯的运行时间测量,本项目在每种排序算法的实现内部进行了“插桩”,以统计两个核心的性能指标:
- **比较次数 (Comparisons)**:算法执行过程中元素之间进行比较的总次数。
- **移动次数 (Moves)**:算法执行过程中数据的赋值、交换等移动操作的总次数。一个 `std::swap` 操作计为3次移动。
这些数据为理论复杂度分析提供了有力的实验验证。
### 3. 自动化的测试与分析框架
项目核心是一个位于 `main.cpp` 的自动化测试框架,其主要功能包括:
- **多算法支持**:能够自动运行所有已实现的排序算法。
- **多规模测试**:支持在一系列预设的数据规模(例如 100, 1000, ..., 500,000上进行测试。
- **结果可靠性**:通过对每个规模进行多次重复实验并计算平均值,有效消除了单次运行的随机性误差。
- **正确性校验**:在每次排序任务完成后,自动检查数组是否真正有序,确保了算法实现的正确性。
- **格式化输出**:将所有测试结果以清晰的表格形式输出到控制台,方便用户阅读和分析。
## 已实现的排序算法
本项目共实现了以下算法:
#### 基础排序算法
- **插入排序 (Insert Sort)**
- **冒泡排序 (Bubble Sort)**
- **希尔排序 (Shell Sort)**
- **归并排序 (Merge Sort)**
#### 快速排序 (Quick Sort) 变体
- **标准快速排序**:选择最后一个元素作为基准点。
- **三数取中优化快速排序 (QuickSortOptimized)**:通过“三数取中”策略选择基准点,以提高算法在特殊数据(如部分有序)下的稳定性。
- **三路快速排序 (QuickSort3Way)**:特别适用于处理含有大量重复元素的数组。
- **双基准快速排序 (Dual-Pivot Quick Sort)**:使用两个基准点将数组划分为三部分,理论上能减少比较次数,在现代处理器上性能优异。
## 如何编译与运行
项目提供了一个 `Makefile` 文件,极大地简化了编译、运行和清理流程。
### 1. 编译项目
在项目根目录下,执行以下命令来编译所有源代码:
```bash
make
```
该命令会自动调用 `g++` 编译器,并将所有 `.cpp` 文件编译链接成一个名为 `sorting_experiment` 的可执行文件。
### 2. 运行实验
编译成功后,执行以下命令来运行完整的性能分析测试:
```bash
make run
```
你也可以直接运行生成的可执行文件:
```bash
./sorting_experiment
```
程序启动后,将开始对所有算法和所有预设规模进行测试,并将结果实时输出到控制台。
### 3. 清理生成文件
如果你想删除所有编译生成的目标文件 (`.o`) 和可执行文件,可以运行:
```bash
make clean
```
## 输出结果解读
程序的输出是一个性能指标表格,每一行代表一种算法在一个特定数据规模下的平均测试结果。
| 列名 | 中文说明 |
| :--- | :--- |
| **Algorithm** | 被测试的排序算法的名称。 |
| **Size** | 当前测试的数组大小(即数据规模 `n`)。 |
| **Avg Time (s)** | 多次重复测试下的平均运行时间,单位为秒。 |
| **Avg Comparisons**| 平均比较操作的次数。 |
| **Avg Moves** | 平均移动(赋值或交换)操作的次数。 |
| **Correct?** | 排序结果是否正确,"Yes" 代表正确无误。 |

View File

@ -0,0 +1,77 @@
# Sorting Algorithm Performance Analysis
This project implements and analyzes the performance of several classic sorting algorithms in C++. It provides a framework to test each algorithm on randomly generated data of various sizes, measuring execution time, comparison counts, and move counts.
A detailed analysis and discussion of the results can be found in [REPORT.md](./REPORT.md).
## Implemented Algorithms
- **Basic Sorting Algorithms:**
- Insertion Sort
- Bubble Sort
- Shell Sort
- Merge Sort
- **Quick Sort Variants:**
- Standard Quick Sort (pivot is the last element)
- Quick Sort with Median-of-Three pivot optimization
- 3-Way Quick Sort (for handling duplicate keys)
- Dual-Pivot Quick Sort
## How to Build and Run
A `Makefile` is provided to simplify the build and execution process.
### 1. Build the Project
To compile all the source files and create the executable, run:
```bash
make
```
This will generate an executable file named `sorting_experiment`.
### 2. Run the Experiment
To run the performance analysis, execute the following command:
```bash
make run
```
Alternatively, you can run the executable directly:
```bash
./sorting_experiment
```
The program will output a formatted table with the performance metrics for each algorithm across different data sizes.
### 3. Clean Up
To remove the compiled object files and the executable, run:
```bash
make clean
```
---
<details>
<summary><strong>Original Experiment Requirements</strong></summary>
### 实验内容
对几种经典的排序算法进行分析,理解算法在不同输入时的表现,深入剖析算法优缺点及其根源。具体要求如下:
1. 实现常见排序算法至少要实现插入排序、冒泡排序、快速排序、归并排序、shell排序算法
2. 在排序算法中插桩,记录关键操作次数(如比较次数、移动次数等);
3. 以待排序文件的行数n为输入规模固定n随机产生多组测试样本统计算法的平均运行时间和关键操作次数改变n的规模重复多次实验并对结果进行统计
4. 改变数组规模,对不同规模问题下各算法的结果进行统计并绘制图表,与理论值进行对照分析;
5. 优化快速排序的中枢点选取,对优化前后的性能进行分析;
6. 对快速排序的三种实现进行性能比较。
### 附加:
- 实现BlockQuickSort就分支预测次数展开分析
- 实现DualPivotQuickSort就递归深度展开分析
- 在超大规模数据上如1亿个整数对比以上快排实现的性能。
### 编写实验文档:
要求对所实现算法的时间进行复杂度分析(结合程序统计关键步骤运行次数,以验证分析结果);程序运行指导,包括程序编译说明、输入输出示例等。如果输入、输出信息较多,建议采用文件进行格式化输入、输出。实验报告:解题思路;所实现算法的时间复杂度分析(结合程序统计关键步骤运行次数,以验证分析结果);程序运行指导,包括程序编译说明、输入输出示例等。如果输入、输出信息较多,建议采用文件进行格式化输入、输出。
</details>

View File

@ -0,0 +1,98 @@
# 排序算法实验报告
## 1. 解题思路
本实验旨在深入理解并分析多种经典排序算法的性能。为了达成此目标,实验遵循了以下设计思路:
1. **模块化实现**:将每种排序算法(插入、冒泡、希尔、归并、快速排序及其变体)分别实现在独立的 `.cpp` 文件中,并通过一个统一的 `algorithm.h` 头文件进行声明。这种结构使得代码清晰、易于扩展和维护。
2. **量化性能指标**:为了客观评估算法性能,除了记录运行时间外,还在算法实现中进行“插桩”,精确统计了两个关键操作:
* **比较次数 (Comparisons)**:元素之间的比较操作,是决定算法时间复杂度的核心因素之一。
* **移动次数 (Moves)**:元素的赋值或交换操作,反映了算法的数据搬运成本。
3. **自动化测试框架**:设计了一个灵活的测试框架 (`main.cpp`),能够:
* 自动化地对所有已实现的算法进行测试。
* 支持对多种不同的输入规模(例如 100, 1000, ..., 500,000进行测试。
* 通过多次重复实验并取平均值的方式,消除单次运行的随机误差,保证结果的可靠性。
* 在每次排序后进行正确性校验,确保算法实现无误。
* 以格式化的表格输出结果,便于阅读和后续分析。
4. **迭代优化与对比**:重点对快速排序进行了深度探索,实现了从基础版本到优化版本(三数取中、三路快排、双基准快排)的演进。通过将这些版本置于同一测试框架下进行性能对比,可以直观地展示不同优化策略带来的效果。
## 2. 算法复杂度分析与实验数据验证
### 理论分析
| 算法 | 平均时间复杂度 | 最好情况 | 最坏情况 | 空间复杂度 |
| :--- | :--- | :--- | :--- | :--- |
| **InsertSort** | O(n²) | O(n) | O(n²) | O(1) |
| **BubbleSort** | O(n²) | O(n) | O(n²) | O(1) |
| **ShellSort** | O(n log n) ~ O(n²) | O(n log n) | O(n²) | O(1) |
| **MergeSort** | O(n log n) | O(n log n) | O(n log n) | O(n) |
| **QuickSort** | O(n log n) | O(n log n) | O(n²) | O(log n) |
| **QuickSortOpt** | O(n log n) | O(n log n) | O(n²) | O(log n) |
| **QuickSort3Way**| O(n log n) | O(n) | O(n²) | O(log n) |
| **DualPivotSort**| O(n log n) | O(n log n) | O(n²) | O(log n) |
### 实验数据验证
通过运行实验程序,可以得到不同算法在不同规模下的平均运行时间、比较次数和移动次数。这些数据可以用来验证上述理论复杂度。
* **O(n²) 算法 (InsertSort, BubbleSort)**:
* **预期**:当输入规模 `n` 增大10倍时运行时间、比较和移动次数大约会增大100倍。
* **观察**:从实验数据中可以看到,当 `n` 从 1000 增加到 10000 时,`InsertSort``BubbleSort` 的运行时间急剧增加,远超线性增长,这与 O(n²) 的特征相符。由于性能问题,测试框架在 `n >= 50000` 时自动跳过了这些算法。
* **O(n log n) 算法 (MergeSort, QuickSort 变体)**:
* **预期**:当输入规模 `n` 增大时,性能增长平缓。比较次数大致在 `n * log(n)` 的量级。
* **观察**`MergeSort` 和各种 `QuickSort` 的运行时间随 `n` 的增长远比 O(n²) 算法要慢。例如,从 `n=10000``n=100000`10倍它们的运行时间增长远小于100倍符合 `n log n` 的趋势。`MergeSort` 的比较次数非常稳定,接近理论值。
* **快速排序变体对比**:
* `QuickSort` (基础版) 在随机数据下表现良好,但如果输入数据有序,其性能会退化到 O(n²)。
* `QuickSortOpt` (三数取中) 通过改进基准点选择,显著提高了在非完全随机数据下的稳定性,其比较和移动次数通常略优于基础版。
* `QuickSort3Way` 在处理含大量重复元素的数组时优势最大(本次实验为随机数据,优势不明显),其在最好情况下(所有元素相同)可达 O(n)。
* `DualPivotSort` (双基准) 在理论上可以减少比较次数。从实验数据看,在较大规模的数据集上(如 `n=100000` 及以上),它通常比单基准的快速排序更快,显示出其优化效果。
## 3. 程序运行指导
### 编译
所有相关的 `.cpp` 源文件需要一起编译。可以使用 g++ 编译器,命令如下:
```bash
g++ main.cpp InsertSort.cpp BubbleSort.cpp ShellSort.cpp MergeSort.cpp QuickSort.cpp QuickSortOptimized.cpp QuickSort3Way.cpp DualPivotQuickSort.cpp out.cpp -o sorting_experiment
```
此命令会生成一个名为 `sorting_experiment` 的可执行文件。
### 运行
直接在终端中运行生成的可执行文件:
```bash
./sorting_experiment
```
### 输出结果说明
程序会输出一个性能分析表格,每一行代表一个算法在一个特定输入规模下的测试结果。
| 列 | 说明 |
| :--- | :--- |
| **Algorithm** | 被测试的排序算法名称。 |
| **Size** | 输入数组的元素个数(即规模 `n`)。 |
| **Avg Time (s)** | 多次重复测试的平均运行时间,单位为秒。 |
| **Avg Comparisons**| 平均比较次数。 |
| **Avg Moves** | 平均移动(赋值/交换)次数。 |
| **Correct?** | 排序结果是否正确,"Yes" 表示正确。 |
## 4. 性能对比与结论
1. **算法类别差异**O(n²) 级别的算法插入排序、冒泡排序仅适用于小规模数据。当数据规模超过一万时其性能急剧下降无法在实际应用中使用。相比之下O(n log n) 级别的算法(归并、希尔、快速排序)则表现出卓越的性能和可扩展性。
2. **快速排序的优势与优化**:在所有 O(n log n) 算法中,快速排序及其变体通常在平均情况下的性能最好,这得益于其更少的常量因子和高效的缓存利用率。
* **基准点选择至关重要**:基础的快速排序在特定数据模式下存在性能退化的风险,而“三数取中”等优化策略能有效缓解此问题,增强算法的稳定性。
* **双基准快排的威力**`DualPivotQuickSort` 在大规模随机数据上展现了最佳性能,验证了其在现代计算环境下的理论优势。
3. **归并排序的稳定性**:虽然 `MergeSort` 在本次测试中的原始速度略逊于最优的快速排序,但它具有一个重要优点:其性能是稳定的 O(n log n),不受输入数据初始顺序的影响。此外,归并排序是一种稳定的排序算法,而快速排序不是。
**最终结论**:对于通用场景下的内部排序任务,经过优化的快速排序(特别是双基准快速排序)是性能上的首选。而当需要排序稳定性或对最坏情况有严格要求时,归并排序是更可靠的选择。

View File

@ -0,0 +1,32 @@
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
/*
* function : implement shell sort
* param nums : the vector to be sorted
* param comp_count : count of comparisons
* param move_count : count of moves
* return : ---
*/
void ShellSort(vector<int> &nums, long long &comp_count, long long &move_count)
{
int n = nums.size();
for (int gap = n / 2; gap > 0; gap /= 2)
{
for (int i = gap; i < n; i += 1)
{
int temp = nums[i];
int j;
for (j = i; j >= gap && (comp_count++, nums[j - gap] > temp); j -= gap)
{
nums[j] = nums[j - gap];
move_count++;
}
nums[j] = temp;
move_count++;
}
}
}

View File

@ -0,0 +1,20 @@
#include <iostream>
#include <vector>
#include <algorithm>
#include <ctime>
#include <cstdlib>
#include <stack>
#include <string>
#include <cstring>
using namespace std;
void InsertSort(vector<int>&, long long&, long long&);
void BubbleSort(vector<int>&, long long&, long long&);
void MergeSort(vector<int>&, long long&, long long&);
void QuickSort(vector<int>&, long long&, long long&);
void QuickSortOptimized(vector<int>&, long long&, long long&);
void QuickSort3Way(vector<int>&, long long&, long long&);
void DualPivotQuickSort(vector<int>&, long long&, long long&);
void ShellSort(vector<int>&, long long&, long long&);

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 152 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 147 KiB

View File

@ -0,0 +1,110 @@
#include <iostream>
#include <vector>
#include <algorithm>
#include <ctime>
#include <cstdlib>
#include <string>
#include <iomanip>
#include "algorithm.h"
#include "out.h"
using namespace std;
#define UP_BOUND 1000000
// Function pointer for sorting algorithms
typedef void (*SortFunction)(vector<int>&, long long&, long long&);
bool is_sorted(const vector<int>& vec)
{
for (size_t i = 0; i < vec.size() - 1; ++i)
{
if (vec[i] > vec[i + 1])
{
return false;
}
}
return true;
}
void run_performance_analysis()
{
cout << "--- Running Performance Analysis ---" << endl;
SortFunction algorithms[] = {InsertSort, BubbleSort, ShellSort, MergeSort, QuickSort, QuickSortOptimized, QuickSort3Way, DualPivotQuickSort};
string algo_names[] = {"InsertSort", "BubbleSort", "ShellSort", "MergeSort", "QuickSort", "QuickSortOpt", "QuickSort3Way", "DualPivotSort"};
int num_algorithms = sizeof(algorithms) / sizeof(algorithms[0]);
vector<int> sizes = {100, 1000, 10000, 50000, 100000, 500000};
int num_repeats = 10;
cout << left << setw(15) << "Algorithm"
<< setw(10) << "Size"
<< setw(20) << "Avg Time (s)"
<< setw(25) << "Avg Comparisons"
<< setw(25) << "Avg Moves"
<< setw(15) << "Correct?" << endl;
cout << string(110, '-') << endl;
for (int i = 0; i < num_algorithms; i++)
{
for (int size : sizes)
{
if (size >= 50000 && (algo_names[i] == "InsertSort" || algo_names[i] == "BubbleSort"))
{
cout << left << setw(15) << algo_names[i]
<< setw(10) << size << " -> Skipped" << endl;
continue;
}
double total_time = 0;
long long total_comps = 0;
long long total_moves = 0;
bool all_correct = true;
for (int j = 0; j < num_repeats; j++)
{
vector<int> vec(size);
for (int k = 0; k < size; k++)
vec[k] = rand() % UP_BOUND;
long long comp_count = 0;
long long move_count = 0;
vector<int> temp_vec = vec;
clock_t start_time = clock();
algorithms[i](temp_vec, comp_count, move_count);
clock_t end_time = clock();
if (!is_sorted(temp_vec))
{
all_correct = false;
}
total_time += (double)(end_time - start_time) / CLOCKS_PER_SEC;
total_comps += comp_count;
total_moves += move_count;
}
cout << left << setw(15) << algo_names[i]
<< setw(10) << size
<< fixed << setprecision(6) << setw(20) << total_time / num_repeats
<< setw(25) << total_comps / num_repeats
<< setw(25) << total_moves / num_repeats
<< setw(15) << (all_correct ? "Yes" : "No") << endl;
}
cout << endl;
}
cout << "------------------------------------" << endl;
}
int main()
{
srand(time(0)); // Seed for random number generation
run_performance_analysis();
return 0;
}

View File

@ -0,0 +1,16 @@
#include <iostream>
#include <vector>
using namespace std;
/*
* function : print a vector
* param nums : the vector to be printed
* return : ---
*/
void Out(vector<int> &nums)
{
for (int i = 0; i < static_cast<int>(nums.size()); i++)
cout << nums[i] << " ";
cout << endl;
}

View File

@ -0,0 +1,6 @@
#include <iostream>
#include <vector>
using namespace std;
void Out(vector<int> &);

View File

@ -0,0 +1,138 @@
import matplotlib.pyplot as plt
import pandas as pd
import io
output = """
--- Running Performance Analysis ---
Algorithm Size Avg Time (s) Avg Comparisons Avg Moves Correct?
--------------------------------------------------------------------------------------------------------------
InsertSort 100 0.000006 2628 2633 Yes
InsertSort 1000 0.000108 251895 251902 Yes
InsertSort 10000 0.010841 25002574 25002583 Yes
InsertSort 50000 -> Skipped
InsertSort 100000 -> Skipped
InsertSort 500000 -> Skipped
BubbleSort 100 0.000018 4891 7118 Yes
BubbleSort 1000 0.001527 498086 754888 Yes
BubbleSort 10000 0.147654 49984024 74752463 Yes
BubbleSort 50000 -> Skipped
BubbleSort 100000 -> Skipped
BubbleSort 500000 -> Skipped
ShellSort 100 0.000003 859 911 Yes
ShellSort 1000 0.000040 15404 15912 Yes
ShellSort 10000 0.000577 263303 268383 Yes
ShellSort 50000 0.003500 1883075 1908568 Yes
ShellSort 100000 0.007738 4329053 4379481 Yes
ShellSort 500000 0.047314 28756169 29008608 Yes
MergeSort 100 0.000005 541 1344 Yes
MergeSort 1000 0.000053 8710 19952 Yes
MergeSort 10000 0.000639 120467 267232 Yes
MergeSort 50000 0.003562 718133 1568928 Yes
MergeSort 100000 0.007466 1536366 3337856 Yes
MergeSort 500000 0.042618 8837077 18951424 Yes
QuickSort 100 0.000002 633 1146 Yes
QuickSort 1000 0.000027 11070 18485 Yes
QuickSort 10000 0.000340 156205 257969 Yes
QuickSort 50000 0.001971 938346 1488604 Yes
QuickSort 100000 0.004147 2013584 3293229 Yes
QuickSort 500000 0.023807 11835910 18761405 Yes
QuickSortOpt 100 0.000002 723 1306 Yes
QuickSortOpt 1000 0.000029 10853 19089 Yes
QuickSortOpt 10000 0.000359 152089 263599 Yes
QuickSortOpt 50000 0.002079 889846 1472662 Yes
QuickSortOpt 100000 0.004246 1894396 3081447 Yes
QuickSortOpt 500000 0.025016 10915240 17611893 Yes
QuickSort3Way 100 0.000003 756 693 Yes
QuickSort3Way 1000 0.000039 12003 9189 Yes
QuickSort3Way 10000 0.000479 168509 114458 Yes
QuickSort3Way 50000 0.002715 1013555 652409 Yes
QuickSort3Way 100000 0.005945 2123298 1376911 Yes
QuickSort3Way 500000 0.032901 12324462 7696985 Yes
DualPivotSort 100 0.000002 624 884 Yes
DualPivotSort 1000 0.000026 10635 12297 Yes
DualPivotSort 10000 0.000331 151783 170443 Yes
DualPivotSort 50000 0.001911 908308 974849 Yes
DualPivotSort 100000 0.004047 1926590 2053087 Yes
DualPivotSort 500000 0.022747 11189537 11712207 Yes
"""
# Use StringIO to treat the string data as a file
data = io.StringIO(output)
# Read the data, skipping the header and footer
df = pd.read_csv(data, sep='\s+', skiprows=3, skipfooter=1, engine='python',
names=['Algorithm', 'Size', 'Avg Time (s)', 'Avg Comparisons', 'Avg Moves', 'Correct?'])
# Drop the 'Correct?' column as it's not needed for plotting
df = df.drop(columns=['Correct?'])
# Remove rows with 'Skipped' values
df = df[~df.isin(['->', 'Skipped']).any(axis=1)]
# Convert columns to numeric types
for col in ['Size', 'Avg Time (s)', 'Avg Comparisons', 'Avg Moves']:
df[col] = pd.to_numeric(df[col])
# Get the list of algorithms
algorithms = df['Algorithm'].unique()
# Plotting
plt.style.use('ggplot')
# --- Plot 1: Average Time vs. Size ---
plt.figure(figsize=(12, 8))
for algo in algorithms:
subset = df[df['Algorithm'] == algo]
plt.plot(subset['Size'], subset['Avg Time (s)'], marker='o', linestyle='-', label=algo)
plt.title('Average Time vs. Input Size')
plt.xlabel('Input Size (n)')
plt.ylabel('Average Time (seconds)')
plt.xscale('log')
plt.yscale('log')
plt.legend()
plt.grid(True, which="both", ls="--")
plt.savefig('average_time_vs_size.png')
plt.close()
# --- Plot 2: Average Comparisons vs. Size ---
plt.figure(figsize=(12, 8))
for algo in algorithms:
subset = df[df['Algorithm'] == algo]
plt.plot(subset['Size'], subset['Avg Comparisons'], marker='o', linestyle='-', label=algo)
plt.title('Average Comparisons vs. Input Size')
plt.xlabel('Input Size (n)')
plt.ylabel('Average Comparisons')
plt.xscale('log')
plt.yscale('log')
plt.legend()
plt.grid(True, which="both", ls="--")
plt.savefig('average_comparisons_vs_size.png')
plt.close()
# --- Plot 3: Average Moves vs. Size ---
plt.figure(figsize=(12, 8))
for algo in algorithms:
subset = df[df['Algorithm'] == algo]
plt.plot(subset['Size'], subset['Avg Moves'], marker='o', linestyle='-', label=algo)
plt.title('Average Moves vs. Input Size')
plt.xlabel('Input Size (n)')
plt.ylabel('Average Moves')
plt.xscale('log')
plt.yscale('log')
plt.legend()
plt.grid(True, which="both", ls="--")
plt.savefig('average_moves_vs_size.png')
plt.close()
print("Plots saved as average_time_vs_size.png, average_comparisons_vs_size.png, and average_moves_vs_size.png")

Binary file not shown.

Binary file not shown.