写了六个相同功能的函数之后,我学到了什么
几周之前,一个社区在 Free Code Camp’s Forum 上发起了非官方的算法大赛。
这个题目看似很简单:返回小于数字 N 的所有 3 或者 5 的倍数的和,N 是函数的参数。
但不是简单的找到解决办法,P1xt 的竞赛要求参赛者把重点放在效率上,它鼓励你自己来写测试用例,并且用它们来评估你方案的性能。
以下是我写出并测试过的每个函数的评估,包括我的测试用例和评估脚本。最后,我将展示最终的赢家,就是那个将我所有的作品杀的片甲不留然后狠狠地给我上了一课的函数。
给自己的代码做测试,真的是超乎寻常地痛苦啊…… 来自:The Simpsons, 在这里 Giphy
函数 1 :数组,Push 方法,累加
function arrayPushAndIncrement(n) {var array = [];var result = 0;for (var i = 1; i < n; i ++) {if (i % 3 == 0 || i % 5 == 0) {array.push(i);}}for (var num of array) {result += num;}return result;
}module.exports = arrayPushAndIncrement; // this is necessary for testing
对于这类问题,我的大脑直接闪现:创建一个数组,然后对这个数组进行操作。
这个函数创建了一个数组,并且将符合条件(能够被 3 或者 5 整除)的数字压入数组,之后遍历得到所有单元的和。
开始测试
这是该函数的自动测试,运行在 NodeJS 环境下,用到了 Mocha 和 Chai 测试工具。
如果你想了解更多关于 Mocha 和 Chai 的安装等信息,可以参考我在自由代码营社区(Free Code Camp's forum)写的一份Mocha 和 Chai 测试入门
我依照 P1xt 提供的标准写了一份简单的测试脚本,需要注意的是在下面这份脚本中,该函数是被封装在模块中的。
// testMult.jsvar should = require( 'chai' ).should();
var arrayPushAndIncrement = require( './arrayPushAndIncrement' );describe('arrayPushAndIncrement', function() {it('should return 23 when passed 10', function() {arrayPushAndIncrement(10).should.equal(23);})it('should return 78 when passed 20', function() {arrayPushAndIncrement(20).should.equal(78);})it('should return 2318 when passed 100', function() {arrayPushAndIncrement(100).should.equal(2318);})it('should return 23331668 when passed 10000', function() {arrayPushAndIncrement(10000).should.equal(23331668);})it('should return 486804150 when passed 45678', function() {arrayPushAndIncrement(45678).should.equal(486804150);})
})
当我用 mocha testMult.js
进行测试的时候,返回了如下结果:
我们认为本文中所有的函数都已经通过测试,在你的代码中,请给你想要试验的函数添加测试用例。
函数 2 :数组,Push 方法,Reduce 方法
function arrayPushAndReduce(n) {var array = [];for (var i = 1; i < n; i ++) {if (i % 3 == 0 || i % 5 == 0) {array.push(i);}}return array.reduce(function(prev, current) {return prev + current;});
}module.exports = arrayPushAndReduce;
这个函数使用了跟前者相似的方法,但是它没有使用 for
循环,而是使用了更加精妙的 reduce
方法来得到结果。
开始执行效率评估测试
现在我们来比较以上两个函数的效率。再次感谢 P1xt 在往期主题中提供的这份脚本。
// performance.jsvar Benchmark = require( 'benchmark' );
var suite = new Benchmark.Suite;var arrayPushAndIncrement = require( './arrayPushAndIncrement' );
var arrayPushAndReduce = require( './arrayPushAndReduce' );// add tests
suite.add( 'arrayPushAndIncrement', function() {arrayPushAndIncrement(45678)
})
.add( 'arrayPushAndReduce', function() {arrayPushAndReduce(45678)
})
// add listeners
.on( 'cycle', function( event ) {console.log( String( event.target ));
})
.on( 'complete', function() {console.log( 'Fastest is ' + this.filter( 'fastest' ).map( 'name' ));
})
// run async
.run({ 'async': true });
如果你在 node performance.js
模式下运行测试,将得到以下输出:
arrayPushAndIncrement x 270 ops/sec ±1.18% (81 runs sampled)
arrayPushAndReduce x 1,524 ops/sec ±0.79% (89 runs sampled)
Fastest is arrayPushAndReduce
事实证明,还是后者更快!来自 Giphy
所以,我们用 reduce
方法能够得到一个快 5 倍的函数!
如果这还不够激动人心,如果这还不足以激励我们继续进行下去,那我也真的是没谁了!
函数 3 :While 循环,数组,Reduce 方法
既然我总是对 for
循环情有独钟,所以我觉得我有必要用 while
循环试一下:
function whileLoopArrayReduce(n) {var array = [];while (n >= 1) {n--;if (n%3==0||n%5==0) {array.push(n);}}return array.reduce(function(prev, current) {return prev + current;});
}module.exports = whileLoopArrayReduce;
那么结果怎样呢?稍微有一点慢:
whileLoopArrayReduce x 1,504 ops/sec ±0.65% (88 runs sampled)
函数 4 :While 循环,求和,没有数组
我发现不同的循环并没有多大的区别,于是我另辟蹊径,用一个没有数组的方法会怎样呢?
function whileSum(n) {var sum = 0;while (n >= 1) {n--;if (n%3==0||n%5==0) {sum += n;}}return sum;
}module.exports = whileSum;
当我沿着这个思路勇往直前的时候,我意识到一直以来第一选择使用数组是多么错误的行为……
whileSum x 7,311 ops/sec ±1.26% (91 runs sampled)
又一项宏伟的提升:将近是上一个的 5 倍快,并且是第一个函数的 27 倍快!
函数 5 :For 循环,求和
当然,我们已经知道 for 循环会快一点:
function forSum(n) {n = n-1;var sum = 0;for (n; n >= 1 ;n--) {(n%3==0||n%5==0) ? sum += n : null;}return sum;
}
这次我用了三元运算符来做条件判断,但是测试结果表明其他版本表现的同样高效。
forSum x 8,256 ops/sec ±0.24% (91 runs sampled)
速度又得到了提升。
我最后一个函数以快 28 倍的速度完爆第一个函数。
我感觉我要夺冠了。
我要上天了。
我将摘得桂冠从容小憩。
进入数学的世界
学着热爱数学:来自 Giphy (Originally, this music video)
一周很快过去了,每个人的最终答案都被发布、测试、校验。最快的那个函数没有使用循环,而是用了一种代数公式来操作数字:
function multSilgarth(N) {var threes = Math.floor(--N / 3);var fives = Math.floor(N / 5);var fifteen = Math.floor(N / 15);return (3 * threes * (threes + 1) + 5 * fives * (fives + 1) - 15 * fifteen * (fifteen + 1)) / 2;
}module.exports = multSilgarth;
测试结果马上出来……
arrayPushAndIncrement x 279 ops/sec ±0.80% (83 runs sampled)
forSum x 8,256 ops/sec ±0.24% (91 runs sampled)
maths x 79,998,859 ops/sec ±0.81% (88 runs sampled)
Fastest is maths
数学最快
最终获胜的那个函数大概地比我最好的作品快 9690 倍,比我最初的作品快 275,858 倍。
如果你想找我,我估计要去可汗学院学习数学了。
写了六个相同功能的函数之后,我学到了什么相关推荐
- C语言编程>第二十六周 ⑥ 请补充fun函数,该函数的功能是:按 “0”到 “9”统计一个字符串中的奇数数字字符各自出现的次数,结果保存在数组num中。注意:不能使用字符串库函数。
例题:请补充fun函数,该函数的功能是:按 "0"到 "9"统计一个字符串中的奇数数字字符各自出现的次数,结果保存在数组num中.注意:不能使用字符串库函数. ...
- 【c语言写计算器】利用函数写一个计算器 包括菜单功能和加减乘除四个功能
/*利用函数写一个计算器 包括菜单功能和加减乘除四个功能 作者:NBDR_YL*/ #include<stdio.h>int mean(void); //声明菜单的函数 float add ...
- 课后习题5.13 编写一程序,将两个字符串连接起来,结果取代第一个字符串。 (1)用字符数组,不用stract函数(即自己写一个具有stract函数功能的函数); (2)用标准库中的stract函数;
课后习题5.13 编写一程序,将两个字符串连接起来,结果取代第一个字符串. (1)用字符数组,不用stract函数(即自己写一个具有stract函数功能的函数): (2)用标准库中的stract函数: ...
- MySQL(六)——存储过程和存储函数
前言 今天简单的介绍一下"存储函数"和"存储过程",平时在工作中用到的时间不多,时间长了难免会忘记.在这里简单的做个回忆总结,方便自己以后复习回忆,当然能帮到需 ...
- 从零开始实现一个简易的Java MVC框架(六)--加强AOP功能
前言 在前面从零开始实现一个简易的Java MVC框架(四)--实现AOP和从零开始实现一个简易的Java MVC框架(五)--引入aspectj实现AOP切点这两节文章中已经实现了AOP功能并且引用 ...
- python中高阶函数map怎么用_python六十课——高阶函数之map
1.高阶函数: 特点:函数的形参位置必须接受一个函数对象 分类学习: 1).map(fn,lsd1,[lsd2...]): 参数一:fn --> 函数对象 参数二:lsd1 --> 序列对 ...
- Linux 探索之旅 | 第五部分第六课:一朝 Shell 函数倾,斗转星移任我行
-- 作者 谢恩铭 转载请注明出处 内容简介 前言 函数的作用 函数的定义 传递参数 返回值 变量作用范围 重载命令 函数的设计 总结 第五部分第七课预告 1. 前言 上一课 Linux探索之旅 | ...
- 第六十九章 Caché 函数大全 $WCHAR 函数
文章目录 第六十九章 Caché 函数大全 $WCHAR 函数 大纲 参数 描述 第六十九章 Caché 函数大全 $WCHAR 函数 返回与识别代理项对的数字代码对应的字符. 大纲 $WCHAR(e ...
- 微信公众平台开发(六) 翻译功能开发
转载自:http://www.php100.com/html/php/api/2013/0909/6130.html 微信公众平台开发(六) 翻译功能开发 来源:David Camp 时间:201 ...
最新文章
- 芬兰阿尔托大学人工智能实验室程路组博士生招聘-肠道菌群进化与人类疾病等方向...
- 涨姿势 |你所不知道的5 个AR增强现实新趋势
- 配置spring-mvc + simple-spring-memcached
- c语言错误 xef代表什么,单片机C语言代码手册 含100多个经典C程序
- openstack数据库获取一个虚机的floating_ip, fix_ip, project_name, user_name, hostname, host
- Python—实训day12—汽车用户消费投诉案例-分析及可视化
- 莫陷入点击和评论陷阱
- 第七届蓝桥杯省赛--四平方和
- html静态网页制作天天生鲜,天天生鲜 前后台资源
- linux搭建dlna媒体服务器,Serviio:一款功能强大的DLNA媒体服务器软件
- oracle detele,Oracle中,一个Delete操作的流程
- 扫描机一直显示连接服务器,扫描仪通过SMTP中继服务器发送通知邮件失败
- 无需软件,使用copy命令合并FLV文件
- Python 自学笔记(教程)(七)
- 基于TIA博途的顺序队列(FIFO)先进先出SCL算法程序(V15版本)
- 三款ActiveX图表控件对比评测 Pro ActiveX、ProEssentials、ChartDirector
- 直扩同步的跟踪 matlab,基于FPGA的猝发式直扩载波同步技术研究与实现
- 【收藏】经典shell十三问
- 末日搜索神器2.0发布
- 萧乾升:3.25黄金白银TD实时最新行情走势分析