160 lines
5.9 KiB
C++
160 lines
5.9 KiB
C++
#include <iostream>
|
||
#include <vector>
|
||
#include <numeric>
|
||
#include <algorithm>
|
||
#include <chrono>
|
||
#include <random>
|
||
|
||
struct Item {
|
||
int weight; // 物品重量
|
||
int value; // 物品价值
|
||
int count; // 物品数量(用于多重背包问题)
|
||
};
|
||
|
||
// 全局操作计数器,用于性能分析
|
||
long long ops_count = 0;
|
||
|
||
// =================================================================
|
||
// 完全背包问题算法实现
|
||
// =================================================================
|
||
|
||
// 算法1:朴素动态规划(三层循环)
|
||
int complete_knapsack_v1(const std::vector<Item>& items, int capacity) {
|
||
ops_count = 0; // 重置操作计数器
|
||
int n = items.size();
|
||
if (n == 0) return 0;
|
||
std::vector<std::vector<int>> dp(n + 1, std::vector<int>(capacity + 1, 0));
|
||
|
||
// 遍历每个物品
|
||
for (int i = 1; i <= n; ++i) {
|
||
int w = items[i - 1].weight;
|
||
int v = items[i - 1].value;
|
||
// 遍历每个容量
|
||
for (int j = 0; j <= capacity; ++j) {
|
||
dp[i][j] = dp[i-1][j]; // 不选择第i个物品
|
||
ops_count++;
|
||
// 尝试选择k个第i个物品
|
||
for (int k = 1; k * w <= j; ++k) {
|
||
ops_count++;
|
||
if (dp[i-1][j - k * w] + k * v > dp[i][j]) {
|
||
dp[i][j] = dp[i-1][j - k * w] + k * v;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
return dp[n][capacity];
|
||
}
|
||
|
||
// 算法2:优化的二维动态规划(两层循环)
|
||
int complete_knapsack_v2(const std::vector<Item>& items, int capacity) {
|
||
ops_count = 0; // 重置操作计数器
|
||
int n = items.size();
|
||
if (n == 0) return 0;
|
||
std::vector<std::vector<int>> dp(n + 1, std::vector<int>(capacity + 1, 0));
|
||
|
||
// 遍历每个物品
|
||
for (int i = 1; i <= n; ++i) {
|
||
int w = items[i - 1].weight;
|
||
int v = items[i - 1].value;
|
||
// 遍历每个容量
|
||
for (int j = 0; j <= capacity; ++j) {
|
||
ops_count++;
|
||
if (j < w) {
|
||
dp[i][j] = dp[i - 1][j]; // 容量不足,不能选择第i个物品
|
||
} else {
|
||
// 选择不选第i个物品或选择第i个物品的最大值
|
||
dp[i][j] = std::max(dp[i - 1][j], dp[i][j - w] + v);
|
||
}
|
||
}
|
||
}
|
||
return dp[n][capacity];
|
||
}
|
||
|
||
// 算法3:空间优化的动态规划(一维数组)
|
||
int complete_knapsack_v3(const std::vector<Item>& items, int capacity) {
|
||
ops_count = 0; // 重置操作计数器
|
||
std::vector<int> dp(capacity + 1, 0); // 一维DP数组
|
||
|
||
// 遍历每个物品
|
||
for (const auto& item : items) {
|
||
// 从物品重量开始遍历容量(完全背包)
|
||
for (int j = item.weight; j <= capacity; ++j) {
|
||
ops_count++;
|
||
dp[j] = std::max(dp[j], dp[j - item.weight] + item.value);
|
||
}
|
||
}
|
||
return dp[capacity];
|
||
}
|
||
|
||
|
||
// =================================================================
|
||
// 基准测试运行器
|
||
// =================================================================
|
||
void run_experiments(int min_n, int max_n, int step_n, int trials, int capacity) {
|
||
// 初始化随机数生成器
|
||
std::random_device rd;
|
||
std::mt19937 gen(rd());
|
||
std::uniform_int_distribution<> weight_dist(1, 40); // 重量范围:1-40
|
||
std::uniform_int_distribution<> value_dist(1, 100); // 价值范围:1-100
|
||
|
||
// 输出CSV格式的表头
|
||
std::cout << "n,algo,time_us,ops\n";
|
||
|
||
// 对不同物品数量进行测试
|
||
for (int n = min_n; n <= max_n; n += step_n) {
|
||
if (n==0) continue;
|
||
long long total_time_v1 = 0, total_ops_v1 = 0;
|
||
long long total_time_v2 = 0, total_ops_v2 = 0;
|
||
long long total_time_v3 = 0, total_ops_v3 = 0;
|
||
|
||
// 进行多次试验取平均值
|
||
for (int t = 0; t < trials; ++t) {
|
||
// 生成随机物品
|
||
std::vector<Item> items(n);
|
||
for (int i = 0; i < n; ++i) {
|
||
items[i] = {weight_dist(gen), value_dist(gen), 0};
|
||
}
|
||
|
||
// 测试算法1的运行时间
|
||
auto start_v1 = std::chrono::high_resolution_clock::now();
|
||
complete_knapsack_v1(items, capacity);
|
||
auto end_v1 = std::chrono::high_resolution_clock::now();
|
||
total_time_v1 += std::chrono::duration_cast<std::chrono::microseconds>(end_v1 - start_v1).count();
|
||
total_ops_v1 += ops_count;
|
||
|
||
// 测试算法2的运行时间
|
||
auto start_v2 = std::chrono::high_resolution_clock::now();
|
||
complete_knapsack_v2(items, capacity);
|
||
auto end_v2 = std::chrono::high_resolution_clock::now();
|
||
total_time_v2 += std::chrono::duration_cast<std::chrono::microseconds>(end_v2 - start_v2).count();
|
||
total_ops_v2 += ops_count;
|
||
|
||
// 测试算法3的运行时间
|
||
auto start_v3 = std::chrono::high_resolution_clock::now();
|
||
complete_knapsack_v3(items, capacity);
|
||
auto end_v3 = std::chrono::high_resolution_clock::now();
|
||
total_time_v3 += std::chrono::duration_cast<std::chrono::microseconds>(end_v3 - start_v3).count();
|
||
total_ops_v3 += ops_count;
|
||
}
|
||
|
||
// 输出每个算法的平均结果
|
||
std::cout << n << ",v1," << total_time_v1 / trials << "," << total_ops_v1 / trials << "\n";
|
||
std::cout << n << ",v2," << total_time_v2 / trials << "," << total_ops_v2 / trials << "\n";
|
||
std::cout << n << ",v3," << total_time_v3 / trials << "," << total_ops_v3 / trials << "\n";
|
||
}
|
||
}
|
||
|
||
int main(int argc, char* argv[]) {
|
||
// 实验参数:最小物品数、最大物品数、步长、每个物品数的试验次数、背包容量
|
||
// 为保持算法1的合理运行时间,使用较小的容量和物品数量
|
||
int min_n = 5; // 最小物品数
|
||
int max_n = 25; // 最大物品数
|
||
int step_n = 5; // 物品数步长
|
||
int trials = 10; // 每个物品数的试验次数
|
||
int capacity = 100; // 背包容量
|
||
|
||
run_experiments(min_n, max_n, step_n, trials, capacity);
|
||
|
||
return 0;
|
||
}
|