使用概率论和数理统计知识来比较C++函数的优劣(应用概率统计方法的函数测试评价方案)

代码成品:https://github.com/user4898426/CppTiming

 一、问题引入:

比较使用两种不同算法写出的相同效果的函数孰优孰劣、或者是只是探究编程语言的性质单纯比较两种代码写法谁快谁慢,这是很常见的需求。显然,最简单的方法是重复两个函数足够多的次数(比如一百万次),然后单纯地比较耗时的多与少。

但是存在对于某些比较精细的问题,两种的耗时可能相差非常之小,以至于直观感觉很可能误以为是误差的情况,比如,当两者的差距只有,假设是0.2%的情况下,直觉会让我们忽略这一差距,或者归类为误差,或者认为其差距不足以产生定性影响。

但究竟能否分出高下呢?考虑这种情况:差距是0.2%,但在测试一千次的情况下,函数A总比函数B稳定地快出0.2%,很显然,这时虽然差距很小,但是我们还是可以有把握地说,函数A大概率比函数B有独到之处,函数A更优,尽管幅度不大

由此可见,这时候,简单直观的方法就失效了,而概率论和数理统计方法为解决这一问题提供了强大的工具。本文试图将概率论和数理统计方法应用到这一问题上,本人所学有限,如有任何错误、疏漏,请不吝赐教。

二、理论依据:

正态分布、t分布、假设检验

三、具体思路:

方法一:双正态总体模型

1.基本假设:

设函数f_{1}f_{2}的计时结果分别为X_{1}X_{2}

  1. 设对于一个单一函数,计时的误差服从以0为中心的正态分布,即 \Delta \sim N\left ( 0,\sigma ^{2} \right )
  2. 设函数的运行时长为一常数\mu,则测量值 X=\mu + \Delta,则 X \sim N\left ( \mu,\sigma ^{2} \right )
  3. 设两函数计时结果的方差相等,即 \left ( X_{1} \sim N \left( \mu_{1}, \sigma_{1}^{2} \right ) \right ) \wedge \left ( X_{2} \sim N \left( \mu_{2}, \sigma_{2}^{2} \right ) \right ) \wedge \left ( \sigma_{1} = \sigma_{2} \right )

由于在同一机器上测试,有理由认为假设3是对的,另外,如果没有假设3,我就不会算了哈哈。

2.假设检验:

由于所研究的问题是函数f_{1}f_{2} “孰优孰劣”的问题,不是“是否有差别”的问题,所以选择单侧检验。在实际实验中,通常存在一方比另一方快的假设(如在一次测量结果后,我们总可以提出假设,假设平均值较少的函数更快),所以在这里不妨假设f_{1}f_{2}快。

则对于显著性水平\alpha

  • 原假设:f_{1}不比f_{2}快,即 H_{0} : \mu_{1} \geq \mu_{2}
  • 备择假设:f_{1}f_{2}快,即 H_{1} : \mu_{1} < \mu_{2}

取检验统计量 t = \frac{\left ( \bar{X_{1}} - \bar{X_{2}} \right ) - \left ( \mu_{1} - \mu_{2} \right )}{S \sqrt{ \frac{1}{n_{1}} + \frac{1}{n_{2}}}},其中 S = \sqrt{\frac{\left ( n_{1} - 1 \right ) S_{1}^{2} + \left ( n_{1} - 1 \right ) S_{1}^{2}}{m+n-2}}

t服从t分布,即 t \sim t \left ( n_{1} + n_{2} - 2 \right )

所以拒绝域为 t \leq - t_{\alpha}\left ( n_{1}+n_{2}-2 \right )

所以,如果让计算机自动判断,只需求出这个t和t分布的分位点值,就可以做出定性结论。

方法二:配对检验模型

当我想出这个方法时,我自己也很吃惊,但是我自己又找不出漏洞,这个方法的可靠性还需高人进一步论证。

1.基本假设:

设函数f_{1}f_{2}的计时结果分别为X_{1}X_{2}

  1. 设对于一个单一函数,计时的误差服从以0为中心的正态分布,即 \Delta \sim N\left ( 0,\sigma ^{2} \right )
  2. 设函数的运行时长为一常数\mu,则测量值 X=\mu + \Delta,则 X \sim N\left ( \mu,\sigma ^{2} \right )

2.问题转化 :

由基本假设得,\left ( X_{1} \sim N \left( \mu_{1}, \sigma_{1}^{2} \right ) \right ) \wedge \left ( X_{2} \sim N \left( \mu_{2}, \sigma_{2}^{2} \right ) \right )

则取 Y = X_{1} - X_{2},则 Y \sim N \left ( \mu_{1} - \mu_{2} , \sigma_{1}^{2} + \sigma_{2}^{2} \right ),即Y也服从正态分布

显然,Y的观测值 y = x_{1} - x_{2}

3.假设检验:

这里仍然采用单侧检验,原理同方法一,不妨假设f_{1}f_{2}快。

则对于显著性水平\alpha

  • 原假设:f_{1}不比f_{2}快,即 H_{0} : \mu_{1} \geq \mu_{2} \Leftrightarrow \mu_{1} - \mu_{2} \geq 0
  • 备择假设:f_{1}f_{2}快,即 H_{1} : \mu_{1} < \mu_{2} \Leftrightarrow \mu_{1} - \mu_{2} < 0

此时,检验就变成了对一个单正态总体Y均值的检验。

取检验统计量 t = \frac{\bar{Y} - 0}{S / \sqrt{n}},其中SY的样本标准差

t \sim t \left ( n - 1 \right )

拒绝域为 t \leq - t_{\alpha} \left ( n - 1 \right )

同理,只要求出这个t和t分布的分位点值,就可以做出定性结论。

四、两种方法的比较

比较程序源代码同见附件

基本原理是,通过人为制造两个快慢不同的函数,然后分别用两种方法进行验证。

万次测验结果如下:

test time: 10000
test 10000 times
method1:        9620 right
method2:        8071 right

可见方法一准确率在96%左右,方法二在80%左右,方法一更可靠一点。

五、附:

我已经按照上文的思路写好了一整套完全基于标准库的源程序,姑且称之为库,这个库从最基础的高精度函数计时到最后结果报告的打印,全部已经写好代码,既是自用,也是拿出来分享,详细内容见GitHub库的主页:

https://github.com/user4898426/CppTiming

下面是一次测试以及打印效果示例:

#include "timing.h"

void foo1()
{
	int a = 0;
	for (int i = 0; i < 10000; ++i)
	{
		a += i;
	}
}

void foo2()
{
	int a = 0;
	for (int i = 0; i < 11000; ++i)
	{
		a += i;
	}
}

int main()
{
	auto_compare(foo1, foo2);
}

效果如下:

+-------------- Comparision Report -------------+
|                                               |
|   Sample Records:                             |
|     index      f1(002515DC)    f2(002515E1)   |
|       0      1.7869e-05      1.93277e-05      |
|       1      1.7869e-05      1.93277e-05      |
|       2      1.7869e-05      1.93277e-05      |
|       3      1.85983e-05     1.8963e-05       |
|       4      1.75043e-05     1.93277e-05      |
|                                               |
+-----------------------------------------------+
|                                               |
|   Analysis:                                   |
|     mean:    1.79419e-05     1.92547e-05      |
|     vari:    1.59584e-13     2.65973e-14      |
|     socm:    1.27667e-13     2.12778e-14      |
|     ival(0.95):                               |
|              [1.87249e-05,    [1.95744e-05,   |
|                1.71589e-05]     1.89351e-05]  |
|                                               |
+-----------------------------------------------+
|                                               |
|   Significance Level : 0.05                   |
|   Faster By:     7.317%                       |
|   Time Saved:    -6.818%                      |
|   Conclusion:                                 |
|          ******************************       |
|          * f1 is significantly faster *       |
|          ******************************       |
|                                               |
+-----------------------------------------------+
|                                               |
|   Method II :                                 |
|          ******************************       |
|          * f1 is significantly faster *       |
|          ******************************       |
|                                               |
+-----------------------------------------------+

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值