clock()计算程序运行的时间
- clock():捕捉从程序开始运行到clock()被调用所消耗的时间
- 时间单位:
clock tick
,即“时钟打点” - 常数
CLK_TCK
(或者CLOCKS_PER_SEC
):机器时钟每秒所走的时钟打点数
通用模型代码:
#include <stdio.h>
#include <time.h>
clock_t start, stop;
/*clock_t是clock()函数返回的变量类型*/
double duration;
/*记录被测函数运行时间,以秒为单位*/
int main(){
/*不在测试范围内的准备工作写在clock()调用之前*/
start = clock();
MyFunction();
stop = clock();
duration = ((double)(stop-start))/CLK_TCK;
/*计算运行时间*/
/*其他不在测试范围的处理写在后面,例如输出duration的值*/
}
对应实例(2种代码方法)
对于多项式求解,有俩种方法:
- 1、直接按照方程求解
/*字母p:记录的是f(x)式子的值,然后每循环i次,就解出i个式子的和*/
double f1(int n, double a[], double x){
int i;
double p = a[0];
for(i=1; i<=n; i++){
p += (a[i] * pow(x,i));
/*里面的a[i] * pow(x,i)就是式子anX^n*/
}
return p;
}
- 2、提取X进行求解
/*从里到外开始计算,p记录的是括号里面的值(与x进行相乘的值)*/
double f2(int n, double a[], double x){
int i;
double p = a[n];
for(i=n; i>0; i--){
p = a[i-1] + x*p;
}
return p;
}
虽然现在我们还没有讲到时间复杂度,但是先计算一下他们的时间复杂度
第一个代码:
在for循环里面,每一次都需要循环i次的乘积(解释:pow计算阶乘就需要i-1次乘积,前面一个乘积,一共就是i次)
一共循环n次,也就是循环的次数为:1-n次的加和
也就是:(1+n)* n/2
那么就是o(n^2)
第二个代码:
也是一个循环,但是循环里面只有一次乘法,也就是循环次数为n次
那么就是o(n)
所以很明显:第二个代码简便于第一个代码
但是!我们这样也只是进行口头计算,我们用clock函数进行运算,看看结果吧!
- 【注意】
由于一次循环对计算机而言,速度可以忽略不计,所以我们将代码进行多次循环,再除以循环的次数,就可以得到对应的时间
代码如下:(为了使代码简洁 -> 一个是.h文件,一个是.cpp文件)
命名为clock的.h文件
# include <stdio.h>
# include <time.h>
# include <math.h>
clock_t start, stop;
double duration;
# define MAXN 100/*多项式最大项数,即多项式阶数+1(99阶)*/
# define MAXK 1E5 /*循环的次数*/
//方法1
double f1(int n, double a[], double x){
int i;
double p = a[0];
for(i=1; i<=n; i++){
p += (a[i] * pow(x,i));
}
return p;
}
//方法2
double f2(int n, double a[], double x){
int i;
double p = a[n];
for(i=n; i>0; i--){
p = a[i-1] + x*p;
}
return p;
}
//输出方法1
void print1(double a[]){
start = clock();
for(int i=0; i<MAXK; i++){
f1(MAXN-1, a, 1.1);
}
stop = clock();
duration = ((double)(stop-start))/CLK_TCK/MAXN;
printf("ticks1 = %f\n", (double)(stop-start));
printf("duration1 = %6.2e\n",duration);
}
//输出方法2
void print2(double a[]){
start = clock();
//对代码循环MAXK次
for(int i=0; i<MAXK; i++){
f2(MAXN-1, a, 1.1);
}
stop = clock();
duration = ((double)(stop-start))/CLK_TCK/MAXN;
printf("ticks2 = %f\n", (double)(stop-start));
printf("duration2 = %6.2e\n",duration);
}
对应的.cpp文件
# include "clock.h"
int main(){
int i;
double a[MAXN];
//对代码循环MAXK次
for(i=0; i<MAXN; i++){
a[i] = (double)i;
}
print1(a);
print2(a);
return 0;
}
程序的好坏判定
判断一个程序的好坏,一般都是俩种方法计算,一个是空间复杂度,另一种是时间复杂度。
空间复杂度过大会导致内存直接爆炸,无法得到结果
时间复杂度会导致时间运行过慢,无法输出完结果
时间复杂度
一般在时间复杂度计算的时候,我们关注俩种复杂度
- 最坏情况复杂度
- 平均复杂度
但是由于平均复杂度不好估计,所以我们一般使用最坏情况复杂度
对俩段算法代码进行拼接:
- 相加:时间复杂度 = max(算法1时间复杂度,算法2时间复杂度)
- 相乘:时间复杂度 = 算法1时间复杂度 ✖ 算法2时间复杂度
一个for循环的时间复杂度 == 循环次数 ✖ 循环体代码的复杂度
if-else结构的复杂度 == if的条件判断复杂度和两个分枝部分的复杂度,总体复杂度取三者中最大(max)
最大子列和问题
题目实例化:
1,2,3,-1,-2,-3
这一串数字的最大值是6
一个专业的程序员写代码的时候,遇到时间复杂度过大的程序,就需要想尽一切办法将程序进行优化,比如说,遇到o(n^2)
的程序,就要有意识的转化为 o(n*log^n)
.cpp文件:
#include"max.h"
int main(){
int A[5] = {-1,-3,4,6,-2};
int N = 6;// n的值 == A[]的长度
int a = MaxSubseqSum1(A,N);
int b = MaxSubseqSum2(A,N);
int c = MaxSubseqSum4(A,N);
printf("%d,%d,%d",a,b,c);
return 0;
}
.h文件,命名为:max.h
# include<stdio.h>
/*算法1:暴力解法,T(N) = O(N^3)*/
/*每个子列都求出来,然后得到最大值*/
int MaxSubseqSum1(int A[], int N){
int ThisSum, MaxSum = 0;
int i, j, k;
for(i = 0; i < N; i++){
for(j = i; j < N; ++j){
ThisSum = 0;
for(k = i; k < j; k++)
ThisSum += A[k];
if(ThisSum>MaxSum){
MaxSum = ThisSum;
}
}
}
return MaxSum;
}
/*算法2:对算法1进行优化,T(N) = O(N^2)*/
/*每个子列都求出来,然后得到最大值*/
int MaxSubseqSum2(int A[], int N){
int ThisSum, MaxSum = 0;
int i, j;
for(i = 0; i < N; i++){
ThisSum = 0;
for(j = i; j < N; ++j){
ThisSum += A[j];
if(ThisSum>MaxSum){
MaxSum = ThisSum;
}
}
}
return MaxSum;
}
/*算法4:一个循环,T(N) = O(N)*/
int MaxSubseqSum4(int A[], int N){
int ThisSum, MaxSum;
int i;
ThisSum = MaxSum = 0;
for(i = 0; i < N; i++){
ThisSum += A[i];
if(ThisSum>MaxSum)
MaxSum = ThisSum;
else if(ThisSum < 0)
ThisSum = 0;
}
return MaxSum;
}
第三种代码:分而治之【有点难的想到】
中间隔断,左右寻找(没有贴进我的程序里面,这个解法会思路即可)
int Max3( int A, int B, int C )
{ /* 返回3个整数中的最大值 */
return A > B ? A > C ? A : C : B > C ? B : C;
}
int DivideAndConquer( int List[], int left, int right )
{ /* 分治法求List[left]到List[right]的最大子列和 */
int MaxLeftSum, MaxRightSum; /* 存放左右子问题的解 */
int MaxLeftBorderSum, MaxRightBorderSum; /*存放跨分界线的结果*/
int LeftBorderSum, RightBorderSum;
int center, i;
if( left == right ) { /* 递归的终止条件,子列只有1个数字 */
if( List[left] > 0 ) return List[left];
else return 0;
}
/* 下面是"分"的过程 */
center = ( left + right ) / 2; /* 找到中分点 */
/* 递归求得两边子列的最大和 */
MaxLeftSum = DivideAndConquer( List, left, center );
MaxRightSum = DivideAndConquer( List, center+1, right );
/* 下面求跨分界线的最大子列和 */
MaxLeftBorderSum = 0; LeftBorderSum = 0;
for( i=center; i>=left; i-- ) { /* 从中线向左扫描 */
LeftBorderSum += List[i];
if( LeftBorderSum > MaxLeftBorderSum )
MaxLeftBorderSum = LeftBorderSum;
} /* 左边扫描结束 */
MaxRightBorderSum = 0; RightBorderSum = 0;
for( i=center+1; i<=right; i++ ) { /* 从中线向右扫描 */
RightBorderSum += List[i];
if( RightBorderSum > MaxRightBorderSum )
MaxRightBorderSum = RightBorderSum;
} /* 右边扫描结束 */
/* 下面返回"治"的结果 */
return Max3( MaxLeftSum, MaxRightSum, MaxLeftBorderSum + MaxRightBorderSum );
}
int MaxSubseqSum3( int List[], int N )
{ /* 保持与前2种算法相同的函数接口 */
return DivideAndConquer( List, 0, N-1 );
}
追加更新:
由于最大子列和求解问题的代码是要吃饭的时候赶出来的,所以呢,手写了一个代码分析,也就是代码运行的手写体,这样子更方便自己理解代码
算法1,2
算法4: