當前位置:
首頁 > 最新 > 一道簡單的程序題?-Python高手成長路

一道簡單的程序題?-Python高手成長路

本篇摘要

在學習程序語言基礎知識的時候,大概會找書上、教學視頻上的一些練習題來練手。有些題它看似簡單,實際上實現起來確實是挺簡單的,初學者一般確定好一個思路,做完題目運行一下,看到結果正確,就接著練下一題了。但有的練習題雖然簡單,卻可以因為有更好的思路,做出不一樣的解答,達到易讀、運行效率高的效果。

在編程的領域裡,沒有標準答案,只有更好的答案。通過一道題舉一反三,學習更多的知識,積累更豐富的經驗。

文末會附上新的作業題,歡迎在留言區踴躍參與。

往期精選

>>>Python從哪裡開始學好呢?

先來看看這道簡單的程序題是怎樣的。

· (題目) 從1到9共9個數字,計算並輸出所有可能組成的n位數(3

輸入:n

輸出:所有符合條件的n位數(如n=3時,123、124、986、987都符合條件,而988、999不符合條件)

如果這道題要求的是「從1到4共4個數字,組成每位不重複的3位數」,初學者第一思路可能是這樣子的,寫四個嵌套的for循環,先把這種3位數拼出來,然後輸出。正常情況下調試好輸出的結果並沒有什麼意外,畢竟4個數字拼成3位數,效率再低的代碼風格在普通的PC上怎樣都是秒出結果的。

但看到「從1到9共9個數字」「組成的n位數」的時候,可能就一臉懵了,懷疑人生式地問自己:這難道是要叫我寫n個for循環嵌套來計算輸出結果嗎?

在這道題上,嵌套for循環的方式並不是最佳的,寫起來啰嗦,難維護,而且很難控制它輸入n之後就做n個嵌套for循環。

這可如何是好?

這裡有兩個思路提供參考,一個是推導式與生成器,一個是「遞歸」演算法。

01

思路1:推導式與生成器

先來看看酷友 @skywet 的這段代碼答案,如圖。

他的思路是這樣的:

① 讓用戶循環輸入n,直到n符合要求時開始計算;

② 構造一個生成器對象g,該生成器用來產生n位數的所有可能的數字(並未進行實際生成數字);

③ 循環檢查生成器生成的每一個數,檢查時先轉換成字元串,然後判斷這個數是否沒有出現數字「0」,且通過轉換成set(集合)類型判斷是否沒有出現重複數字,這兩點都符合要求的,放到列表裡面;

④ 輸出列表裡面所有數字,即題目答案;

QA問答環節

Q1:「生成器」是啥子?

A:簡單地說,在Python裡面為了節約系統資源、提高運行效率,有一種可以邊循環邊計算的機制,稱為「生成器」。也就是說,思路第②原本是可以通過循環產生所有可能的n位數的,用「生成器」優化後,程序運行效率更高。使用方法是,用推導式的形式寫,把外層括弧改成圓括弧,產生的對象就變成了「生成器」對象,再用for .. in .. 循環取出作二次處理,圖中例子 g = (i for i in range(10**(n-1), 10**n)) 就是產生「生成器」對象的語句,而下面 for element in g 就是從生成器g中逐一取出element的意思。(優化技能got √)

Q2:它是n位數,為什麼要先把數字轉換成字元串,而不是通過數學計算把每一位拆出來呢?

A:在這個具體問題以及答案中,把數字轉換成字元串類型,可以利用Python中各種字元串處理方法的遍歷,來解決判斷是否出現「0」、是否出現重複數字位的問題,假設有字元串值的變數s,通過"0" in s便能直接判斷s中是否有0;

而代碼中出現的set(s)的形式,是把字元串s看做是一種特殊的list(列表),把它直接轉換成一個集合對象,利用集合中的元素都唯一的特點,只要集合set(s)中的元素個數跟字元串s的長度數字一致,便說明字元串s中的每一位都是不重複的。(基礎技能活用got √)

Q3:這段程序的思路很不錯,代碼上還有改進空間嗎?

A:還是有的,按照思路,最終生成結果放到list中,可以改進為直接用列表推導式把結果list生成出來,再比如把具體計算輸出過程包裝成一個函數,把輸入部分包裝到主程序中,再增加異常處理,等等;這裡篇幅關係暫時不講列表推導式,對它有疑問的朋友,可以在評論區里提出。(經驗積累學習方式got √)

經過改進,新的代碼答案如圖。

02

思路2:「遞歸」演算法

這一段是筆者提供的代碼答案,如圖。

筆者的思路是這樣的:

① 用戶輸入n;

② 把n傳入具體計算結果的函數,在函數內判斷n是否符合要求,不符合則拋出錯誤;

③ 在函數中寫出生成一個位數字的方法 for in range() ,利用「遞歸」演算法,每進入一層函數,則生成的數字增加一位,到最後一層生成符合條件的n位數時,把這個n位數用print直接輸出;

QA問答環節

Q1:isinstance()是什麼?3

A:isinstance()是用來判斷對象是否為指定類型的實例,簡單來說,isinstance(n, int)是判斷n是否為int(整數)類型,那麼isinstance(s, str)便是判斷s是否為str(字元串)類型,isinstance(d, dict)判斷d是否為dict(字典)類型,以此類推;

在Python中,支持類似數學形式的條件判斷,可以寫成3

Q2:「遞歸」演算法是什麼?這個答案為什麼可以這樣寫「遞歸」?

A:「遞歸」的概念很簡單,寫一個函數,讓它能調用自身,一層一層地深入下去,這個函數就是遞歸函數,但是單純讓它調用自身,不是會陷入一個死胡同裡面嗎?所以「遞歸」演算法重要的一點是,需要給它設計一個「停止條件」,讓它在適當的時候停下來,完成我們需要的計算;另一點重要的是,設計它在每一層調用時做什麼。

在這個具體例子裡面,output函數有兩個參數n和num,n是我們輸入n後傳入的,num是生成出來的數字,是字元串的形式,一開始什麼數字都沒有,所以初始值是""(空字元串);output(n, num+str(i)) 是調用自身的關鍵語句,這個遞歸函數每進入一層,數字就在最右邊增加一位,停止條件就是當數字位數跟輸入的n相等的時候,也就是 if len(num) == n 這句。

也就是說,我們把num看成一個箱子,可以放進數字的那種,假設n=3,一開始num="" 即箱子空空如也,每進入一層函數時,用循環 for i in range(1, 10) 每次把1到9中的某一個數字放進箱子 num+str(i),然後通過 output(n, num+str(i)) 這一句進入下一層,也是同樣方式用循環 for i in range(1, 10) 每次把1到9中的某一個數字放進箱子……當放進箱子的數字數量是n的時候,就可以把這個數字輸出了。這裡需要留意的是,作了判斷 if str(i) in num,如果數字i還沒放進過箱子里,才把它放進去的。

編寫遞歸函數應用「遞歸」演算法時,特別需要留意上述的「停止條件」和「每一層調用時做什麼」這兩點,還是不太理解的朋友,可以在評論區里再次提出。(基礎演算法知識got √)

筆者認為,新手在學習時更應該關註解決問題的思路上,而非運行出來的結果,通過勤奮的練習和舉一反三的學習方式,與高手之間的差距便能很快地縮短。

· (作業題) 編寫一個函數,實現把一個字元串中的字元(ASCII可見字元範圍,如字母A-Z、a-z、0-9等)循環右移n位,在主程序中實現輸入輸出。

輸入:一個字元串,以及右移位數n (例如"abcdefghi",n=2)

輸出:一個字元串(上面輸入對應的結果為"hiabcdefg")

希望本篇可以給感興趣或者想要學習的朋友們帶來幫助哈。

有其他疑問也請在下方留言提出,會儘力解答~


喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 Python 的精彩文章:

Python編程語言學習得怎麼樣,看你是否正真入行Python領域
Python基礎知識——序列對象

TAG:Python |