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

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

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

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

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

  1. double f1(int n, double a[], double x)
  2. {
  3. int i;
  4. double p = a[0];
  5. for (i = 1; i <= n; i++)
  6. p += (a[i] * pow(x, i));
  7. return p;
  8. }

传入阶数 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))...))
\]

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

  1. double f2(int n, double a[], double x)
  2. {
  3. int i;
  4. double p = a[n];
  5. for (i = n; i > 0; i--)
  6. p = a[i-1] + x*p;
  7. return p;
  8. }

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

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

clock() 函数

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

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

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 好了。然后我们来跑一下,看看它们分别跑了多少时间。

  1. #include <stdio.h>
  2. #include <math.h> /* for pow() */
  3. #include <time.h> /* for clock() */
  4. #define MAXN 10 /* 多项式最大项数,即多项式阶数+1 */
  5. int main()
  6. {
  7. int i;
  8. double a[MAXN]; /* 存储多项式的系数 */
  9. for (i = 0; i < MAXN; i++)
  10. a[i] = i;
  11. start = clock();
  12. f1();
  13. stop = clock();
  14. duration = ((double)(stop-start))/CLOCKS_PER_SEC;
  15. printf("ticks1 = %f\n", (double)(stop-start));
  16. printf("duration1 = %f\n", duration);
  17. start = clock();
  18. stop = clock();
  19. duration = ((double)(stop-start))/CLOCKS_PER_SEC;
  20. printf("ticks2 = %f\n", (double)(stop-start));
  21. printf("duration2 = %f\n", duration);
  22. return 0;
  23. }

当然你现在看起来呢,说这真是一个很傻的程序。因为没有一个很专业的程序员可以容忍说,我一段代码里头,居然有两个片段几乎是一模一样的。如果你看到这种情况,重复的情况,你应该怎么去做一个更好的处理呢?显然你应该会写一个函数去做这件事情。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. 相当有用的react+redux渲染性能优化原理

    学习地址:http://foio.github.io/react-redux-performance-boost/

  2. jquery中stop停止动画笔记

    jQuery stop() 方法用于停止动画或效果,在它们完成之前. stop() 方法适用于所有 jQuery 效果函数,包括滑动.淡入淡出和自定义动画. 语法: $(selector).stop( ...

  3. php之laravel学习

    http://laravel-china.github.io/php-the-right-way/#composer_and_packagist laravel 添加 dingoapi路由插件 并运用 ...

  4. 在<img src="..." title="..."> 中使title的内容换行的方法

    在<img src="..." title="...">中要使TITILE的内容换行,不能使用html标签,只能用ASCII码,方法如下: < ...

  5. Erlang pool management -- RabbitMQ worker_pool

    在RabbitMQ中,pool 是以worker_pool 的形式存在的, 其主要用途之一是对Mnesia transaction 的操作. 而在RabbitMQ 中, pool 中的worker 数 ...

  6. [转载]Linux C 字符串函数 sprintf()、snprintf() 详解

    一.sprintf() 函数详解 在将各种类 型的数据构造成字符串时,sprintf 的强大功能很少会让你失望. 由于 sprintf 跟 printf 在用法上几乎一样,只是打印的目的地不同而已,前 ...

  7. Runnable、Callable、Future和FutureTask之一:基本用法

    Java从发布的第一个版本开始就可以很方便地编写多线程的应用程序,并在设计中引入异步处理.Thread类.Runnable接口和Java内存管理模型使得多线程编程简单直接.但正如之前提到过的,Thre ...

  8. spring 学习二 @RequestMapping

    RequestMapping是一个用来处理请求地址映射的注解,可用于类或方法上.用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径. RequestMapping注解有六个属性,下面我们把她 ...

  9. javascript——事件处理模型(DOM 和 IE)

    javascript的事件处理模型分为 DOM事件处理模型和 IE事件处理模型. 一.DOM事件流模型 DOM事件流分为三个阶段:捕获阶段.目标阶段.冒泡阶段. 捕获阶段:自上而下,由document ...

  10. JDBC批处理数据

    JDBC3.0  的增强支持BLOB,CLOB,ARRAY,REF数据类型.的ResultSet对象UPDATEBLOB(),updateCLOB(),updateArray()和updateRef( ...