1、上机名称
算法复杂度计算
2、上机要求
学习领会算法的时间复杂度和空间复杂度的计算方法
3、上机环境
Visual studio 2022 12th Gen Intel® Core™ i7-12700H 2.30 GHz,16G ram
4、程序清单
头文件head.h内容如下:
#pragma once
#include<stdio.h>
#include<time.h>
#include<stdlib.h>
//四个算法
int MaxIntervalSum_1(int a[], int n);
int MaxIntervalSum_2(int a[], int n);
int MaxIntervalSum_3(int a[], int begin, int end);
int MaxIntervalSum_4(int a[], int n);
//四个时间测试
double Holdingtime_1(int a[], int n);
double Holdingtime_2(int a[], int n);
double Holdingtime_3(int a[], int begin, int end);
double Holdingtime_4(int a[], int n);
实现文件head.cpp内容如下:
#include"head.h"
//穷举法三层循环
int MaxIntervalSum_1(int a[], int n) {
int max = 0; int sum = 0;
for (int i = 0; i < n; i++) { //确定一个头部
for (int j = i; j < n; j++) { //确定一个尾部
sum = 0; //记得将和置零
for (int k = i; k <= j; k++) { //求和
sum += a[k];
}
if (sum > max) max = sum; //判断并修改结果
}
}
return max;
}
//改进穷举法两层循环
int MaxIntervalSum_2(int a[], int n) {
int max = 0; int sum = 0;
for (int i = 0; i < n; i++) { //确定一个头部
sum = 0;
for (int j = i; j <= n; j++) { //不断改变尾部
sum += a[j]; //利用之前计算过的加
if (sum > max); max = sum; //上尾部来减少工作量
}
}
return max;
}
//分治法 有点类似于二分法
int MaxIntervalSum_3(int a[], int begin, int end) {
if (begin == end) {
return a[end] > 0 ? a[end] : 0;
}
int mid = (begin + end) / 2; //找中间元素分割
int lmax = MaxIntervalSum_3(a, begin, mid); //计算左半边最大值
int rmax = MaxIntervalSum_3(a, mid + 1, end); //计算右半边最大值
int sum = 0;
//从中间向开头计算能达到的最大值
int leftmax = 0;
for (int i = mid; i >= begin; i--) {
sum += a[i];
if (sum > leftmax)leftmax = sum;
}
//从中间向尾端计算能达到的最大值
sum = 0;
int rightmax = 0;
for (int i = mid + 1; i < end; i++) {
sum += a[i];
if (sum > rightmax)rightmax = sum;
}
int mmax = leftmax + rightmax; //并不确定如果从中间开始往两边会不会更大
int max = (lmax > rmax) ? (lmax > mmax ? lmax : mmax) : (rmax > mmax ? rmax : mmax);
return max; //每段里面都会返回一个最大值
}
//动态规划 用sum来试探可能的最大值,用maxsum来记录确定已经得到的最大值
int MaxIntervalSum_4(int a[], int n) {
int sum = 0; int maxsum = 0;
for (int i = 0; i < n; i++) {
sum += a[i];
if (maxsum < sum) maxsum = sum; //一旦有新的最大值,将其保留
if (sum < 0) sum = 0; //一旦发现某一段做出负贡献将其删除
}
return maxsum;
}
//重复执行并测试时间
double Holdingtime_1(int a[], int n) {
int repeat = 32;
clock_t starttime = clock();
for (int i = 0; i < repeat; i++) {
MaxIntervalSum_1(a, n);
}
clock_t endtime = clock();
double during = difftime(endtime, starttime)/repeat;
printf("算法“MaxIntervalSum_1”数组大小:%d,用时: %.9f毫秒\n", n, during);
return during;
}
double Holdingtime_2(int a[], int n) {
int repeat = 32;
clock_t starttime = clock();
for (int i = 0; i < repeat; i++) {
MaxIntervalSum_2(a, n);
}
clock_t endtime = clock();
double during = difftime(endtime, starttime)/repeat;
printf("算法“MaxIntervalSum_2”数组大小:%d,用时: %.9f毫秒\n", n, during);
return during;
}
double Holdingtime_3(int a[], int begin, int end) {
int repeat = 128 * 64*32;
clock_t starttime = clock();
for (int i = 0; i < repeat; i++) {
MaxIntervalSum_3(a, begin, end);
}
clock_t endtime = clock();
double during = difftime(endtime, starttime)/repeat;
printf("算法“MaxIntervalSum_3”数组大小:%d,用时: %.9f毫秒\n", end, during);
return during;
}
double Holdingtime_4(int a[], int n) {
int repeat = 128 * 64*32;
clock_t starttime = clock();
for (int i = 0; i < repeat; i++) {
MaxIntervalSum_4(a, n);
}
clock_t endtime = clock();
double during = difftime(endtime, starttime)/repeat;
printf("算法“MaxIntervalSum_4”数组大小:%d,用时: %.9f毫秒\n", n, during);
return during;
}
源文件main.cpp内容如下:
#include"head.h"
#define maxNum 1024*64
#pragma warning(disable:6011)
int main() {
for (int N = 32; N < maxNum; N *= 2) {
int* a = (int*)malloc(N * sizeof(int));
srand((unsigned)time(NULL));
for (int i = 0; i < N; i++) {
a[i] = rand() % 100 - 50; //填入-50到50的随机数
}
//对每个算法进行测试
Holdingtime_1(a, N);
Holdingtime_2(a, N);
Holdingtime_3(a, 1, N);
Holdingtime_4(a, N);
putchar('\n');
}
}
时间复杂度说明:
算法1时间复杂度O(n3); 三层for循环,暴力运算
算法2时间复杂度O(n2); 两层for循环,简化暴力运算
算法3时间复杂度O(nlog2(n)); 类似于二分法分治汇总,递归
算法4时间复杂度O(n); 一层for循环,动态规划
在数组大小为8192时,算法1在一小时后仍然没跑出结果(76分钟才跑出来),对已经获得的数据制作excel进行分析,(一些内容在多次编译之后数据有所改变,主要针对算法二的多个数组大小重复值)
在电脑配置为HP laptop16-d1xxx 操作系统为win11家庭版,12th Gen Intel® Core™ i7-12700H 2.30 GHz,16G ram其性能测试:
空间复杂度计算:
#include<stdio.h>
#include<stdlib.h>
void print1(long long int n) {
for (long long int i = 1; i <= n; i++) {
printf("%I64d\n", i);
}
}
void print2(long long int n) {
if (n == 1) {
printf("%I64d\n", n);
return;
}
print2(n - 1);
printf("%I64d\n", n);
return;
}
int main(int argc,char*argv[]) {
long long int n;
scanf("%d",&n);
printf("迭代打印1到%I64d\n", n);
print1(n);
printf("递归打印1到%I64d\n", n);
print2(n);
}
在43160左右 系统分配的内存资源不足 优点是可以套娃,一条逻辑可以顺延到底,缺点是内存难以释放,占用资源量大 适用于函数需要直接或间接调用自身的情况,需要设计基本情况作为函数出口和递归情况继续调用自身。
上机体会:
算法不同,则对应时间复杂度和空间复杂度不同。
在时间复杂度方面,设计了四种算法在针对整形数组中间连续部分求最大值问题,在不同输入规模的情况下的运行时间,将实验结果和分析结果进行比较,发现随着数组规模的扩大,不同时间复杂度算法运行的时间差异有着天壤之别,进而理解研究算法时间复杂度,在设计程序之初确定好一个优良的算法能为程序运行时间带来大规模改善。
对于空间复杂度,主要是通过打印 1 到N个整数来研究算法的空间复杂度,打印采用直接循环打印和递归打印两种方式进行,在打印次数N增加的过程中,循环打印效果良好,递归打印在N值达到4300左右时出现了中断,并且在运行过程中,占用了较大的CPU资源,(这主要是由于但是,相比较迭代法,递归法在进行函数调用过程中,会使用栈保存当前的运行环境,因此会产生需要空间,这也造成了N较大时,不能运行的情况,因为占用的空间过大。)认识到缩小空间复杂度对于程序规模的缩小,运行效率的提升有着重要帮助。
其他收获:在时间复杂度计算的过程中,由于程序运行过快,甚至达不到毫秒级别,故可以循环多次(10的指数级)再除下去来进行十分小跨度的时间计算,然而,运行时间具备一定的偶然性,需要多次统计,(在第一次计算中,算法二执行结果就出奇的离谱)。