解決拿蛋問題的時候,通過幾個shell指令碼運算速度對比,體會了演算法和編程最佳化的重要性

來源:互聯網
上載者:User

標籤:拿蛋問題   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/bashecho -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 in {1..100000}do  if [[ $i%1 -eq 0 ]] && [[ $i%2 -eq 0 ]] && [[ $i%3 -eq 0 ]] && [[ $i%4 -eq 2  ]] && [[ $i%5 -eq 4  ]] && [[ $i%6 -eq 0 ]] && [[ $i%7 -eq 5 ]] && [[ $i%8 -eq 2 ]] && [[ $i%9 -eq 0 ]]  then    echo $i    break  fidone

順便使用time命令測試了一下指令碼運行消耗的時間:

第一次:

第二次:

第三次:

測試通過,沒問題,只是這代碼寫的太無腦了,簡直不忍直視,有沒有最佳化的空間呢?答案是肯定的,但這時候就需要有一點點邏輯思維的能力了(學過小學數學即可)。


思考如何最佳化:

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############################################################### File Name: modified.sh# Version: V1.0# Author: Damon Pan# Blog: http://blog.51cto.com/oldpan# Created Time : 2018-04-17 15: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  fidone


同樣使用time命令測試指令碼運行消耗的時間:

第一次:

第二次:

第三次:


果然沒有對比,就沒有傷害!運行速度相差了30多倍!這還只是計算到1314就停止了的情況下。那麼如果把題目的要求修改一下呢?比如就像前文中我對這道題目的要求進行的第二種猜測一樣,把題目的要求改為:“如果這個筐裡最多能裝10萬個雞蛋,求這個筐裡分別有可能有多少個雞蛋。”


簡單粗暴型的代碼就是把前文中代碼1簡單粗暴版 裡的if迴圈中的break去掉即可,這裡不再重複列出代碼了,只把三次time運行指令碼的結果列在下面。

time測試第一次:

第二次:

第三次:


那麼最佳化後的指令碼運行速度如何呢?(代碼部分同樣只是把前文中代碼2最佳化版 裡的 if迴圈裡的break去掉,但是下面的測試證明這樣做其實犯了一個邏輯錯誤)

time測試:

這~~怎麼比沒有最佳化的指令碼用的時間還多了呢?而且算出來的數字已經超過10萬了。肯定是代碼有錯誤了,開始修改代碼

在 if 判斷中增加了一個條件,n不能大於10萬,這樣修改之後,輸出的結果倒是正確了,但是指令碼執行速度仍然很慢。

time測試第一次:

第二次:

第三次:


看來代碼還是有問題,回頭仔細查看代碼,發現雖然在if中增加了一條判斷,但是實際上for迴圈的運算次數一次都沒有減少,只是計算到10萬以後就不再顯示計算結果了。繼續修改代碼:


#!/bin/bash############################################################### File Name: finalVersion.sh# Version: V1.0# Author: Damon Pan# Blog: http://blog.51cto.com/oldpan# Created Time : 2018-04-17 16: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}  fidone


time測試第一次:

第二次:

第三次:


計算效率提高了40多倍,沒有預期的那麼大,可能我的程式邏輯還是有問題,應該還有可以進一步最佳化改進的地方。但是因本人數學和編程水平極其有限,再改就改不動了。。。還望有高手不吝賜教,在下先謝過了!


本次實驗通過同學提出的一個問題,驗證了演算法和編程最佳化的重要性,只是使用了一種最最簡單低級的最佳化,便把計算效率提高了40倍左右,如果需要計算的資料量非常大的時候,效率的提升還是很明顯的。

解決拿蛋問題的時候,通過幾個shell指令碼運算速度對比,體會了演算法和編程最佳化的重要性

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.