C语言计时器笔记
题目描述:
计算某给定多项式在点x处的值: f ( x ) = a 0 + a 1 x + a 2 x 2 + ⋯ + a n − 1 n n − 1 + a n x n f(x)=a_0+a_1x+a_2x^2+\cdots+a_{n-1}n^{n-1}+a_nx^n f(x)=a0+a1x+a2x2+⋯+an−1nn−1+anxn。
暴力破解法
分别计算多项式的每一个加式,即 a n x n a_nx^n anxn。
double f1(int n, double a[], double x) {
double p = a[0];
for (int i = 1; i <= n; ++i) {
p += (a[i] * pow(x, i)); //a_n*x^n
}
return p;
}
该算法具有较高的时间复杂度,每轮循环中,都需要进行 1 + i 1+i 1+i次乘法和一次加法,加法通过硬件直接运算,时间几乎可以忽略不记,因此主要讨论乘法的运算次数。
i i i从 1 1 1逐渐递增至 n n n,故 n n n轮共进行了 n + n ( n + 1 ) 2 n+\frac{n(n+1)}{2} n+2n(n+1)次乘法运算。
综上所述:
该算法的时间复杂度为 O ( n 2 + n ) = O ( n 2 ) O(n^2+n)=O(n^2) O(n2+n)=O(n2)。
秦九韶算法
运用结合律将x作为公因子提出,构成形如:
f ( x ) = a 0 + x ( a 1 + x ( a 2 + ⋯ + x ( a n − 1 + x ( a n ) ) … ) ) f(x)=a_0+x(a_1+x(a_2+\dots+x(a_{n-1}+x(a_n))\dots)) f(x)=a0+x(a1+x(a2+⋯+x(an−1+x(an))…))
的公式。由内而外的进行多项式的计算,无须多次乘法即可完成。
double f2(int n, double a[], double x) {
double p = a[n];
for (int i = n; i > 0; i--) {
p = a[i - 1] + x * p;
}
return p;
}
每轮循环通过计算内层 a n − 1 + x ∗ a n a_{n-1}+x*a_n an−1+x∗an不断进行一次加和 和 一次乘法便可完成 f ( x ) f(x) f(x)的计算。
同上,只讨论乘法的运算次数。根据分析可知,每次循环只有一次乘法,故而时间复杂度为 O ( n ) O(n) O(n)。
但是,虽然秦九韶算法的时间复杂度很低,但是当多次循环累加之后数值增大,乘法运算速度大幅增大。不过总体而言时间复杂度相比于暴力破解法有大约一个数量级的提升。
不足
两种算法共同的不足之处在于,当数值较大时,如超过Double能表示的最大范围时,程序将崩溃;
或 n n n越大,整体数值就越大,导致乘法速度大幅增加。以至于程序崩溃。
由于double所占字节的限制及其乘法运算本身的难度,应将算法改进为通过字符模拟加法及乘法操作,再通过秦九韶算法可将时间复杂度实质性的大幅降低,并且理论上数值大小可以趋于 + ∞ +\infty +∞。
计时器简笔
-
引入头文件
#include <time.h>
time.h
头文件中规定了每秒的时钟打点数CLK_TCK,一般为1000,即表示毫秒级别。- 通过clock_t分别定义两个开始和结束的打点计时变量,如:
clock_t start, stop; //定义时钟打点计数器变量
- 测试函数执行时间时,先将函数所需的各种变量及其相关条件准备好,随后通过两次clock()函数进行时钟打点,如:
start = clock(); //开始时刻的时钟打点 fun(); //某函数或程序段 stop = clock(); //结束时刻的时钟打点
- 计算时间间隔:
//法一:通过difftime函数进行计算时间间隔。 duration = difftime(stop, start) / CLK_TCK; //法二:直接计算。 duration = (double) (stop - start) / CLK_TCK;
完整代码C语言描述如下:
//
// Created by Ss1Two on 2023/1/2.
//
#include <stdio.h>
#include <time.h>
#include <math.h>
#define Maxn 10 //定义多项式项数
#define Maxk 1e7 //定义测试函数时间时循环体次数
#define CT CLK_TCK //重命名Clock Tick(一秒内的时钟打点数)
double f1(int n, double a[], double x);
double f2(int n, double a[], double x);
void printTime(double n, double a[], double x, int num);
clock_t start, stop;
double duration;
int main() {
double a[Maxn], x = 123;
for (int i = 0; i < Maxn; i++) {
a[i] = (double) i;
}
printTime(Maxn - 1, a, x, 1);
printTime(Maxn - 1, a, x, 2);
return 0;
}
//暴力破解法,直接求每一项的a~n~x^n^
double f1(int n, double a[], double x) {
double p = a[0];
for (int i = 1; i <= n; ++i) {
p += (a[i] * pow(x, i)); //a_n*x^n
}
return p;
}
//秦九韶算法,运用结合律将x作为公因子提出,计算内层即可。详见笔记
double f2(int n, double a[], double x) {
double p = a[n];
for (int i = n; i > 0; i--) {
p = a[i - 1] + x * p;
}
return p;
}
void printTime(double n, double a[], double x, int num) {
printf("f(%d)各参数如下:\n", num);
start = clock(); //开始时刻的时钟打点
if (num == 1) {
for (int i = 0; i < Maxk; ++i)
f1(n, a, x);
} else {
for (int i = 0; i < Maxk; ++i)
f2(n, a, x);
}
stop = clock(); //结束时刻的时钟打点
duration = difftime(stop, start) / CT;
printf("循环Maxk次时间:duration=%.3fs\n", duration);
double per = duration / Maxk * 1e9;
printf("单次执行时间:per=%.2fps\n", per);
printf("时钟打点数:stop-start=%d\n", (int) (stop - start));
printf("时钟打点数:difftime()=%d\n", (int) difftime(stop, start));
}
运行结果为
f(1)各参数如下:
循环Maxk次时间:duration=0.862s
单次执行时间:per=86.20ps
时钟打点数:stop-start=862
时钟打点数:difftime()=862
f(2)各参数如下:
循环Maxk次时间:duration=0.103s
单次执行时间:per=10.30ps
时钟打点数:stop-start=103
时钟打点数:difftime()=103