当前位置: 首页 > 编程日记 > 正文

写了六个相同功能的函数之后,我学到了什么

本文讲的是写了六个相同功能的函数之后,我学到了什么,

几周之前,一个社区在 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 倍

如果你想找我,我估计要去可汗学院学习数学了。





原文发布时间为:2016年11月17日

本文来自云栖社区合作伙伴掘金,了解相关信息可以关注掘金网站。

相关文章:

libevent介绍

libevent是一款事件驱动的网络开发包 由于采用 c 语言开发 体积小巧&#xff0c;跨平台&#xff0c;速度极快。 通常我们在建立服务器的处理模型的时候,主要是下面集中模型;(1) a new Connection 进来&#xff0c;用 fork() 产生一个 Process 处理。 (2) a new Connecti…

蓝色起源载人火箭7月首飞,贝索斯即将实现儿时愿望

整理 | 寇雪芹出品 | AI 科技大本营&#xff08;ID:rgznai100&#xff09;头图 | 下载于ICphoto美国当地时间6月7日早&#xff0c;亚马逊创始人、世界首富贝索斯&#xff08;Jeff Bezos&#xff09;在社交媒体上发帖表示&#xff0c;自己将在7月20日乘坐蓝色起源&#xff08;Bl…

使用jquery.more.js来实现点击底部更多后, 底部加载出新的数据

<div class"bus-nav-bar ft12"><div class"navt bor-r-c pos-rel {if $int 0}fwbold{/if}"><a href"portal.php?modmerchant&actionvoucherlist&int0">全部订单</a><em class"pos-abs"></…

ios开发学习-手势交互(Gesture)效果源码分享

qianqianlianmengios开发学习-手势交互&#xff08;Gesture&#xff09;效果源码分享 All Around Pull View 介绍&#xff1a;实现视图四个方向&#xff08;上下左右&#xff09;都能够拖动更新&#xff08;pull to refresh&#xff09;。 编译测试&#xff0c;测试环境…

通过C#实现集合类纵览.NET Collections及相关技术

概述&#xff1a;在真正的对象化开发项目中&#xff0c;我们通常会将常用的业务实体抽象为特定的类&#xff0c;如Employee、Customer、Contact等&#xff0c;而多数的类之间会存在着相应的关联或依存关系&#xff0c;如Employee和Customer通过Contact而产生关联、Contact是依赖…

TIOBE 6 月榜单: Python 有望超越 C 语言成为第一名

整理 | 苏宓出品 | CSDN&#xff08;ID&#xff1a;CSDNnews&#xff09;头图 | 下载于ICphotoTIOBE 官方最新发布了 6 月的编程语言榜单&#xff0c;这个月榜单中又有怎样的发展趋势&#xff1f;Python 有望成为第一名在本月榜单中&#xff0c;位居第二名的 Python 与第一名 C…

使用dom4j解析XML例子

包括三个文件&#xff1a;studentInfo.xml(待解析的xml文件), Dom4jReadExmple.java(解析的主要类), TestDom4jReadExmple.java(测试解析的结果) 代码运行前需先导入dom4j架包。 studentInfo.xml文件&#xff08;该文件放在本项目目录下&#xff09;内容如下&#xff1a; <?…

mkdir、rmdir命令、head、tail命令

mkdir-p 递归创建目录11里面都是空目录rmdir删除空目录 -p 当子目录被删除后使它也成为空目录的话&#xff0c;则一并删除步骤&#xff1a;先删除11/22/33 发现22目录空了&#xff0c;因为33删了&#xff0c;于是再删22&#xff0c;空了再删11head默认是前10行 –n指定几行tai…

Linux Find 命令精通指南

作者&#xff1a;Sheryl Calish Linux find 命令是所有 Linux 命令中最有用的一个&#xff0c;同时也是最混乱的一个。它很难&#xff0c;因为它的语法与其他 Linux 命令的标准语法不同。但是&#xff0c;它很强大&#xff0c;因为它允许您按文件名、文件类型、用户甚至是时间戳…

【安全运维】 linux 系统账户,网络,简易安全加固方案(第一部分),经测试可行...

前言讲到linux系统账户的管理以及安全&#xff0c;就必须涉及 /etc/passwd /etc/shadow 这2个文件这里以截图中文字说明的方式&#xff0c;来分析这2个文件的内容&#xff0c;并且给出一些实用的安全加固方案注意&#xff0c;本文会持续更新&#xff0c;后续加入的内容都以直…

不用深度学习,怎么提取图像特征?

来源 | 小白学视觉头图 | 下载于ICphoto图像分类是数据科学中最热门的领域之一&#xff0c;在本文中&#xff0c;我们将分享一些将图像转换为特征向量的技术&#xff0c;可以在每个分类模型中使用。VATboxVATbox&#xff0c;作为n一个我们所暗示的&#xff0c;涉及增值税问题&a…

课程第五天内容《基础交换 五》

2019独角兽企业重金招聘Python工程师标准>>> 以太网/LAN(local area network )的相关概念&#xff1a; 问题&#xff1a; 信号传输距离有限&#xff1b; 解决方案&#xff1a; 中继器 - 放大电信号&#xff0c;延长信息的传输距离&#xff1…

C#综合揭秘——Entity Framework 并发处理详解

引言 在软件开发过程中&#xff0c;并发控制是确保及时纠正由并发操作导致的错误的一种机制。从 ADO.NET 到 LINQ to SQL 再到如今的 ADO.NET Entity Framework&#xff0c;.NET 都为并发控制提供好良好的支持方案。 并发处理方式一般分为乐观必并发与悲观必并发两种&#xff0…

@2021高考生,用 Python 分析专业“钱景”

来源 | 关于数据分析于可视化头图 | 下载于ICphoto2021年的高考在昨日拉开帷幕&#xff0c;十年的寒窗苦读&#xff0c;终于到了最后见分晓的时候了。在这么一场关键的考试当中&#xff0c;除了考试前努力奋斗&#xff0c;考场上认真答题&#xff0c;考后的志愿填报也是极其的重…

Linux下C语言的fgets与fputs

使用的是 CentOS gcc编译下面程序 显示warning: the gets function is dangerous and should not be used.问题出在程序中使用了 gets Linux 下gcc编译器不支持这个函数&#xff0c;解决办法是使用 fgets fgets()函数的基本用法为&#xff1a; fgets(char * s,int size,FILE * …

linux发行版的用户交互

1 cli&#xff0c;即command line interface 纯命令行的交互方式&#xff0c;该命令行界面是由shell提供的。 linux内核本身也自带了一个console&#xff0c;即linux console&#xff0c;它是基于frame buffer的。 cli的界面都是基于ncurses库开发的。 2 GUI&#xff0c;graphi…

C#中在应用程序和DLL使用消息

在C#中采用的是事件驱动方式&#xff0c;但在我们使用的过程中&#xff0c;有时候通过调用系统原有的消息&#xff0c;处理起来会比较简单一些&#xff0c;特别是在处理与DLL文件的交互时&#xff0c;的确是非常的方便。 在C#中使用自定义消息 在C#中使用自定义消息非常简单&…

倪光南院士:openEuler与全球开发者共同推动计算产业发展

今日&#xff0c;以“创造最好的OS&#xff0c;成就更好的我们”为主题的 openEuler Developer Day 2021在北京成功举行。本次大会由openEuler社区发起&#xff0c;产业组织专家、学者、企业领袖和开发者们围绕多样性计算、云原生全栈、全场景协同等技术方向共同探讨和创新。大…

jhello框架-ajax

2019独角兽企业重金招聘Python工程师标准>>> 现在的web开发没有ajax都没法活&#xff0c;所以在jhello中实现了一个简单的ajax交互&#xff0c;使用json作为载体。 在上篇《交互》中讲到一种交互的方式是把数据放入Model中&#xff0c;通过ModelAndView类返回到视图…

发微信模版消息换行用\n

发微信模版消息换行用\n发微信模版消息换行用\n发微信模版消息换行用\n发微信模版消息换行用\n发微信模版消息换行用\n转载于:https://blog.51cto.com/xuqin/1974131

二次元会让人脸识别失效吗?

来源 | PyTorch 开发者社区责编 | 寇雪芹头图 | 下载于ICphoto人脸识别也遇到坑了&#xff0c;识别得了三次元&#xff0c;却对二次元无效。迪士尼的技术团队&#xff0c;正在开发这一算法&#xff0c;以帮助动画制作者进行后期搜索。团队利用 PyTorch&#xff0c;效率得到很大…

基于第四层交换技术的负载均衡

摘 要 本文介绍了第四层交换技术的概念&#xff0c;技术原理以及如何使用第四层交换技术实现远程教育系统中的应用服务器负载均衡。 作者&#xff1a; 凌仲权&#xff0c;现就读于西安电子科技大学&#xff0c;2001级硕士研究生。主要的科研方向为计算机网络技术研究以及远程教…

開始Unity3D的学习之旅

前言&#xff1a;这个系列的文章纯属对自己学习的整理&#xff0c;非高手之作。但确实的记载了我作为一个没接触过3D游戏编程的大学生的心路历程。争取每周整理一次吧。之所以会開始学Unity3D&#xff0c;最基本的原因是由于在快放暑假的时候&#xff0c;我找了一家做iPhone游戏…

linux 定时任务crond

1.定时任务crond 1.1 crond是什么 crond是linux系统中用来定期执行命令或指定程序任务的一种服务或软件。特殊需求&#xff1a;&#xff08;秒级别&#xff09;crond服务就无法搞定了&#xff0c;一般工作中写脚本守护进程执行。 为什么要使用crond定时任务&#xff1a; linux系…

用C#去除代码的SourceSafe管理

经常看一些的程序&#xff0c;有些一个解决方案带有多个项目&#xff0c;由于代码比较多&#xff0c;多人开发&#xff0c;所以好多vs.net下的工程是用source safe进行版本控制的。而用source safe进行版本控制需要局域网路径共享&#xff0c;因此好多项目换一台机器打开会出现…

用jarsigner对android apk进行签名

以前对apk重新打包签名都是用的apktool里面的Auto-sign 工具&#xff0c;后来发现有时候利用该工具对一些apk签名会失败&#xff0c;所以后来就使用 jarsigner这个工具&#xff0c;现记录一下这个工具的使用方法1 首先要生成一个自己的keyD:\>keytool -genkey -alias myKey…

利用java反射机制 读取配置文件 实现动态类载入以及动态类型转换

作者&#xff1a;54dabang 在spring的学习过程之中&#xff0c;我们能够看出通过配置文件来动态管理bean对象的优点&#xff08;松耦合 能够让零散部分组成一个总体&#xff0c;而这些总体并不在意之间彼此的细节&#xff0c;从而达到了真正的物理上的疏散耦合&#xff0c;而非…

C#操作消息队列

public class QueueManage { /// /// 发送对象到队列中 /// /// 队列名称&#xff0c;因为队列名称在一个应用中应该不改变的&#xff0c;所以大家最好写在配置文件中 /// 要发出去的对象 public static void SendQueue(string QueuePath,MyBase.SmsQueue sq) { Syste…

2021《程序员》数字科技企业研发实力榜TOP50

互联网的盛行带来了众多数字科技企业的崛起&#xff0c;但如何客观地衡量每家企业的技术实力&#xff1f;研发投入、研发人才的数量和人才密度是关键指标。2021年5月,《程序员》根据相关企业披露的财报数据及市场调研数据&#xff0c;整理发布“2021数字科技企业研发实力榜TOP5…