C 语言 clock() 函数,例:计算多项式值

/**
* clock(): 捕捉从程序开始运行到 clock() 被调用时所耗费的时间。
* 这个时间单位是 clock tick, 即“时钟打点”。
* 常数 CLK_TCK: 机器时钟每秒所走的始终打点数。
* In Macintosh or C99, CLK_TCK == CLOCKS_PER_SEC
* http://www.cplusplus.com/reference/ctime/CLOCKS_PER_SEC/
*/

我把老师说的话都敲了一遍哈哈。为了测试两种算法,哪一种的效率更高,我们就需要有一个工具来记录这个算法做完这件事花费了多长时间。clock() 函数就是 C 语言所提供的工具,当然其他的语言也有,找找就能找到的。

例:写程序计算给定多项式在定点 \(x\) 处的值

\[f(x) = a_0 + a_1x + a_2x^2 + ... + a_{n-1}x^{n-1} + a_nx^n
\]

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;
}

传入阶数 n,系数放在数组 a 里面,和要计算的 x。先从这个常数项 a[0] 开始,令 p 等于 a[0],然后我们就做一个循环的累加求和的过程。每一次求和,就求和的是多项式里面的第 i 项,也就是 a[i] 乘以 x 的 i 次方。看上去这是一个特别简单的程序,但是呢,如果在正式的程序里面,你真的这么去实现这个功能的话,那会被专业的程序员严重鄙视的,我们不能这么写哈。

专业的程序员怎么处理这个问题呢?早在好几百年以前,我们有个老祖宗叫做秦九韶的,他就给出了一个非常聪明的算法。他是巧妙地利用了一下结合律,每一次把 x 当成一个公因子提出来,这样一层一层往里面提公因子。

\[f(x) = a_0 + x(a_1 + x(...(a_{n-1} + x(a_n))...))
\]

那我们在写程序计算的时候呢,这个程序是从里往外算的。这个是我们实现这个函数的标准程序

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;
}

令 p 从 a[n] 开始,然后每一次用 x 乘以这个括号里面已经算出来的这个 p,然后再加上前面那一项的系数 a[i-1]。

那,凭什么说第二个函数就比第一个函数实现得要好呢?凭什么第一个函数就要被鄙视呢?因为第一个函数慢好多。到底谁快谁慢呢?不服气的话,我们要来测一下

clock() 函数

那在 C 语言里头呢,提供了一个这样的工具。C 语言提供了一个函数,叫做 clock。这个函数可以捕捉从程序开始运行,一直到这个函数被调用那个时刻所耗费的时间,但是它这个时间的单位,是 clock tick,翻译成“时钟打点“。跟它配套的,我们还有一个常数,叫 CLOCKS_PER_SEC (在 C99 以前,叫 CLK_TCK,实际上就是 clock tick 的一个缩写),给出的是这个机器时钟每秒钟走的时钟打点的数。那这个数到底等于多少呢?不同的机器可能都不一样。这两个东西配合在一起我们就可以算出来一个函数到底跑了多少秒钟。

#include <stdio.h>
#include <time.h> /* for clock() */ clock_t start, stop;
/* clock_t 是 clock() 函数返回的变量类型 */
double duration;
/* 记录被测函数运行时间,以秒为单位 */ int main()
{
/* 不在测试范围内的准备工作写在 clock() 调用之前 */
start = clock(); /* 开始计时 */
foo(); /* 把被测函数加在这里 */
stop = clock(); /* 停止计时 */
duration = ((double)(stop-start))/CLOCKS_PER_SEC;
/* 其他不在测试范围的处理写在后面,例如输出 duration 的值 */
return 0;
}

foo() 函数,就是你要测的函数。这里的命名呢涉及到历史遗留问题,在很多的例子代码中,有的变量或者函数的命名是 foo。foobar 是计算机程序领域里的术语,并无实际用途和参考意义。在计算机程序设计与计算机技术的相关文档中,术语 foobar 是一个常见的无名氏化名。

clock() 函数返回的是从这个程序开始运行,直到调用的那个时刻所耗费的时间。所以在函数开始之前调用 clock() 存到 start 这个变量里面,在函数执行结束之后,紧接着又调用一次 clock(),存到 stop 这个变量里边。那么 stop 里边存的是什么呢?就是 main() 函数开始执行,一直到这次 clock 被调用的时候,一共走了多少个 ticks。所以我们用 stop - start 就得到了 foo() 的执行过程中间一共经历了多少个 ticks,最后我们再除一下这个常数,就得到了以秒为单位的这个 duration。

下面我们就一个具体的多项式来看一下,计算 \(x\) = 1.1 处的值 \(f(1.1)\)

\[f(x) = \sum_{i=0}^{9}i \cdot x^i
\]

它的系数呢,我们就让第 i 个系数就等于 i 好了。然后我们来跑一下,看看它们分别跑了多少时间。

#include <stdio.h>
#include <math.h> /* for pow() */
#include <time.h> /* for clock() */ #define MAXN 10 /* 多项式最大项数,即多项式阶数+1 */
int main()
{
int i;
double a[MAXN]; /* 存储多项式的系数 */
for (i = 0; i < MAXN; i++)
a[i] = i; start = clock();
f1();
stop = clock();
duration = ((double)(stop-start))/CLOCKS_PER_SEC;
printf("ticks1 = %f\n", (double)(stop-start));
printf("duration1 = %f\n", duration); start = clock();
stop = clock();
duration = ((double)(stop-start))/CLOCKS_PER_SEC;
printf("ticks2 = %f\n", (double)(stop-start));
printf("duration2 = %f\n", duration); return 0;
}

当然你现在看起来呢,说这真是一个很傻的程序。因为没有一个很专业的程序员可以容忍说,我一段代码里头,居然有两个片段几乎是一模一样的。如果你看到这种情况,重复的情况,你应该怎么去做一个更好的处理呢?显然你应该会写一个函数去做这件事情。Anyway (无论如何),先跑一下。跑出来的结果有可能都是 0,这是因为这两个函数跑得实在是太快了,它们的运行时间都不到一个 tick,所以 clock() 函数根本捕捉不到它的区别。那这怎么办呢?如何测出不到1个tick的程序运行时间?重复

跑一次捕捉不到,跑 10 次、跑 100 次、跑 1000 次、跑 10000 次,积累一点运行时间,一直跑到间隔的时间能够被 clock() 捕捉到。最后计算单次时间的时候,你只要把总时间除以重复的次数,你就得到了这个函数一次运行的时间。

你将看到这两组数据的相对大小,应该都是差了一个数量级这个样子。所以我们就可以看到,为什么说第一个算法比较傻呢,它比第二个算法要慢了差不多一个数量级的样子。这个故事告诉我们,解决问题方法的效率,跟算法的巧妙程度有关。

再试一个多项式

给定另一个100阶多项式 \(f(x) = 1 + \sum_{i=1}^{100} \frac{x^i}{i}\),用不同方法计算 \(f(1.1)\) 并且比较一下运行时间?

C 语言 clock() 函数,例:计算多项式值的更多相关文章

  1. Go语言示例-函数返回多个值

    Go语言中函数可以返回多个值,这和其它编程语言有很大的不同.对于有其它语言编程经验的人来说,最大的障碍不是学习这个特性,而是很难想到去使用这个特性. 简单如交换两个数值的例子: package mai ...

  2. C语言pow()函数的计算精度问题

    编程计算 a+aa+aaa+-+aa-a(n个a)的值,n和a的值由键盘输入.例如,当n=4,a=2,表示计算2+22+222+2222的值. 程序运行结果示例: Input a,n: 2,4↙ su ...

  3. .net 调用R语言的函数(计算统计值pvalue 对应excel :ttest)

    Pvalue 计算 项目设计pvalue计算,但是由于.net 没有类似的公式或者函数,最终决定使用.net 调用R语言 采用.net 调用r语言的公用函数 需要安装 r语言环境 https://mi ...

  4. Clock函数用法

    clock()是C/C++中的计时函数,而与其相关的数据类型是clock_t.在MSDN中,查得对clock函数定义如下: clock_t clock(void) ; 这个函数返回从“开启这个程序进程 ...

  5. C语言的函数类型

    C语言的函数类型与返回值类型不一致时出现,是以函数类型为标准; 而如果在java与c#语言中上述情况是编译错误的;

  6. CUDA学习(二)之使用clock()函数

    clock()函数是C/C++中的计时函数,相关的数据类型是clock_t,使用clock函数可以计算运行某一段程序所需的时间,如下所示程序计算从10000000逐渐减一直到0所需的时间. #incl ...

  7. 用clock()函数计算多项式的运行时间

    百度百科中定义clock():clock()是C/C++中的计时函数,而与其相关的数据类型是clock_t.在MSDN中,查得对clock函数定义如下: clock_t clock(void) ; 简 ...

  8. 【C语言入门教程】5.1 函数说明 与 返回值

    C 语言是结构化语言,它的主要结构成分是函数.函数被作为一种构件,用以完成程序中的某个具体功能.函数允许一个程序的各个任务被分别定义和编码,使程序模块化.本章介绍 C 语言函数的设计,如何用函数分解程 ...

  9. clock()函数的返回值精度问题

    clock()函数返回值为1毫秒,就是0.001秒.clock函数功 能: 返回处理器调用某个进程或函数所花费的时间.用 法: clock_t clock(void);说明:clock_t其实就是lo ...

随机推荐

  1. Python collections系列之可命名元组

    可命名元组(namedtuple)  根据nametuple可以创建一个包含tuple所有功能以及其他功能的类 1.创建一个坐标类 import collections # 创建类, defaultd ...

  2. mysql 查找表的auto_increment和修改

    1.查看最大的AUTO_INCREMENT SELECT AUTO_INCREMENT from  information_schema.tables where table_schema='cont ...

  3. Process使用

    最近在一个项目中,需要在C#中调用cmd窗口来执行一个命令,使用到了Process这个类,使用过程中遇到不少问题,好在终于解决了.赶紧记录下来. Process process = new Proce ...

  4. RSA公钥,私钥和数字签名通用理解

    一.公钥加密 假设一下,我找了两个数字,一个是1,一个是2.我喜欢2这个数字,就保留起来,不告诉你们(私钥),然后我告诉大家,1是我的公钥. 我有一个文件,不能让别人看,我就用1加密了.别人找到了这个 ...

  5. GWT嵌入纯HTML页面

    众所周知,gwt页面是java代码所写,不存在html页面直接作用于gwt面板中.不过gwt也倒是提供了一些可用的功能,比如frame,这个是UI中的一个,内部可以设置URL,但是经过我测试后发现,这 ...

  6. 机器学习:SVM(非线性数据分类:SVM中使用多项式特征和核函数SVC)

    一.基础理解 数据:线性数据.非线性数据: 线性数据:线性相关.非线性相关:(非线性相关的数据不一定是非线性数据) 1)SVM 解决非线性数据分类的方法 方法一: 多项式思维:扩充原本的数据,制造新的 ...

  7. java继承如何理解呢??

    总结:我把他弄的无语了.他是诺基亚公司的软件开发师,大学毕业就可以进那么好的公司.实力 package com.bc; //普通类 class yt { public void price() { S ...

  8. Celery-4.1 用户指南: Configuration and defaults (配置和默认值)

    这篇文档描述了可用的配置选项. 如果你使用默认的加载器,你必须创建 celeryconfig.py 模块并且保证它在python路径中. 配置文件示例 以下是配置示例,你可以从这个开始.它包括运行一个 ...

  9. 如何边遍历集合边删除元素--使用Iterator中的remove()方法

    在遍历集合时,想将符合条件的某些元素删除,开始是用了下面的方法 public static void main(String[] args) throws UnsupportedEncodingExc ...

  10. JAVA基础知识总结13(同步)

    好处:解决了线程安全问题. 弊端:相对降低性能,因为判断锁需要消耗资源,还容易产生了死锁. 定义同步是有前提的: 1,必须要有两个或者两个以上的线程,才需要同步. 2,多个线程必须保证使用的是同一个锁 ...