解决拿蛋问题的时候,通过几个shell脚本运算速度对比,体会了算法和编程优化的重要性

前端之家收集整理的这篇文章主要介绍了解决拿蛋问题的时候,通过几个shell脚本运算速度对比,体会了算法和编程优化的重要性前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

前几天,一位同学在群里提出一个拿蛋的问题,原题如下:

有一筐鸡蛋,

1个1个拿,正好拿完

2个2个拿,正好拿完

3个3个拿,正好拿完

4个4个拿,剩下2个

5个5个拿,剩下4个

6个6个拿,正好拿完

7个7个拿,剩下5个

8个8个拿,剩下2个

9个9个拿,正好拿完

求:筐里一共有多少鸡蛋?

请使用脚本方式,计算鸡蛋总数!


个人感觉这个题目写的不严谨,因为至少我没看明白,这道题问的到底是“这个筐里最少有多少鸡蛋?”还是“筐里鸡蛋总数在某一范围之内(比如这个筐里最多能装100000个蛋的前提条件下),求筐里鸡蛋有可能是多少个?”暂且先按前一种猜测解答这道题吧,第二种猜测后续做为变种扩展一下。


其实这个问题貌似比较常见了,在网上随随便便就能搜出答案,但是不经思考拿来就用不理解消化成自己的知识的话,下次遇到相同或者类似的问题仍然不会,再加上正好培训班的老师这段时间也正在讲Shell高级编程,就拿这个题目来练习一下Shell编程吧。


首先想到的是简单粗暴暴力破解型的脚本:一个数一个数算呗!有啥难的?

代码1(简单粗暴版):

#!/bin/bash

echo-e"有一筐鸡蛋,\n1个1个拿,正好拿完\n2个2个拿,正好拿完\n3个3个拿,正好拿完\n4个4个拿,剩下2个\n5个5个拿,剩下4个\n6个6个拿,正好拿完\n7个7个拿,剩下5个\n8个8个拿,剩下2个\n9个9个拿,正好拿完\n求:筐里一共有多少鸡蛋?\n请使用脚本方式,计算鸡蛋总数!\n"

foriin{1..100000}
do
if[[$i%1-eq0]]&&[[$i%2-eq0]]&&[[$i%3-eq0]]&&[[$i%4-eq2]]&&[[$i%5-eq4]]&&[[$i%6-eq0]]&&[[$i%7-eq5]]&&[[$i%8-eq2]]&&[[$i%9-eq0]]
then
echo$i
break
fi
done

顺便使用time命令测试了一下脚本运行消耗的时间:

第一次:

第二次:

第三次:

测试通过,没问题,只是这代码写的太无脑了,简直不忍直视,有没有优化的空间呢?答案是肯定的,但这时候就需要有一点点逻辑思维的能力了(学过小学数学即可)。


思考如何优化:(优化的核心思想可以通过【减少if判断的条件】和【减少for循环的次数】两个方面,最终来达到减少代码计算所需总时间的目的。所以下面对代码进行的各种优化,都是围绕这两个核心思想来进行的。)

1. 任何数都能被1整除,这个不用做判断。

2. 可以被2、3、6、9同时整除,那么因为2、3、6、9的最小公倍数是18,所以直接用18的倍数判断能否除4余2,除5余4,除7余5,除8余2即可,这里稍微需要注意是求余不是除。

3. 隐藏条件:5个5个拿,剩下四个,那么说明这筐鸡蛋总数量的个位数要么是4,要么是9,但是由条件2(2个2个拿,正好拿完)得知,此数必为偶数,故个位数不能为9。结合此数必为18的倍数这个条件,那么当此数为18的3+5×N倍时,即可同时满足既是“18的倍数”且“个位数是4”这两个条件。

思考结束后,就开始研究如何循环计算18的3+5×N倍能否除4余2,除7余5,除8余2

代码2(优化版):

#!/bin/bash
##############################################################
#FileName:modified.sh
#Version:V1.0
#Author:DamonPan
#Blog:http://blog.51cto.com/oldpan
#CreatedTime:2018-04-1715:00:28
#Description:null
##############################################################

echo-e"有一筐鸡蛋,\n1个1个拿,正好拿完\n2个2个拿,正好拿完\n3个3个拿,正好拿完\n4个4个拿,剩下2个\n5个5个拿,剩下4个\n6个6个拿,正好拿完\n7个7个拿,剩下5个\n8个8个拿,剩下2个\n9个9个拿,正好拿完\n求:筐里一共有多少鸡蛋?\n请使用脚本方式,计算鸡蛋总数!\n"

for((i=0;i<=100000;i++))
do
((n=18*(3+5*${i})))
if((${n}%4==2))&&((${n}%7==5))&&((${n}%8==2))
then
echo${n}
break
fi
done


同样使用time命令测试脚本运行消耗的时间:

第一次:

第二次:

第三次:


果然没有对比,就没有伤害!运行速度相差了30多倍!这还只是计算到1314就停止了的情况下。那么如果把题目的要求修改一下呢?比如就像前文中我对这道题目的要求进行的第二种猜测一样,把题目的要求改为:“如果这个筐里最多能装10万个鸡蛋,求这个筐里分别有可能有多少个鸡蛋。”


简单粗暴型的代码就是把前文中代码1简单粗暴版里的if循环中的break去掉即可,这里不再重复列出代码了,只把三次time运行脚本的结果列在下面。

time测试第一次:

第二次:

第三次:


那么优化后的脚本运行速度如何呢?(代码部分同样只是把前文中代码2优化版里的 if循环里的break去掉,但是下面的测试证明这样做其实犯了一个逻辑错误

time测试:

这~~怎么比没有优化的脚本用的时间还多了呢?而且算出来的数字已经超过10万了。肯定是代码错误了,开始修改代码

在 if 判断中增加了一个条件,n不能大于10万,这样修改之后,输出的结果倒是正确了,但是脚本执行速度仍然很慢。

time测试第一次:

第二次:

第三次:


看来代码还是有问题,回头仔细查看代码,发现虽然在if中增加了一条判断,但是实际上for循环的运算次数一次都没有减少,只是计算到10万以后就不再显示计算结果了。继续修改代码


#!/bin/bash
##############################################################
#FileName:finalVersion.sh
#Version:V1.0
#Author:DamonPan
#Blog:http://blog.51cto.com/oldpan
#CreatedTime:2018-04-1716:06:27
#Description:null
##############################################################

echo-e"有一筐鸡蛋,\n1个1个拿,正好拿完\n2个2个拿,正好拿完\n3个3个拿,正好拿完\n4个4个拿,剩下2个\n5个5个拿,剩下4个\n6个6个拿,正好拿完\n7个7个拿,剩下5个\n8个8个拿,剩下2个\n9个9个拿,正好拿完\n求:筐里一共有多少鸡蛋?\n请使用脚本方式,计算鸡蛋总数!\n"

for((i=0;i<=100000;i++))
do
((n=18*(3+5*${i})))
if((${n}>100000))
then
break
elif((${n}%4==2))&&((${n}%7==5))&&((${n}%8==2))
then
echo${n}
fi
done


time测试第一次:

第二次:

第三次:


计算效率提高了40多倍,没有预期的那么大,可能我的程序逻辑还是有问题,应该还有可以进一步优化改进的地方。但是因本人数学和编程水平极其有限,再改就改不动了。。。还望有高手不吝赐教,在下先谢过了!


本次试验通过同学提出的一个问题,验证了算法和编程优化的重要性,只是使用了一种最最简单低级的优化,便把计算效率提高了40倍左右,如果需要计算的数据量非常大的时候,效率的提升还是很明显的。

猜你在找的Bash相关文章