iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 13
3
Software Development

活用python- 路遙知碼力,日久練成精系列 第 13

Day13- 第二屠龍刀二式- 列表生成式(二) 搭配if使用達到過濾的效果

  • 分享至 

  • xImage
  •  

路遙知碼力,日久練成精-只要在程式之路鑽研的夠深,便能夠充分發揮程式碼的力量; 練習的日子夠久,便能夠練成寫出精簡代碼的能力。

首先公佈昨天課後練習解答:
(還沒看過題目的朋友歡迎點昨日題目傳送門)

def findMin(N, W):
    return min([min(w) for w in W])

min()函數無法直接取得二維list的最小值,
所以你可以這樣做,
用列表生成式取得每列list的最小值再取整體的最小值,
你答對了嗎?
(其實參數N是可以不用的,別被題目誤導囉)

搭配if使用達到過濾的效果

大家好,今天進入「列表生成式」語法第二招囉。
除了昨天說的可以單純用一個列表生成另一個新列表之外,
有時候我們會想要取出列表內滿足條件的元素,
而非取出所有元素,
這時可以直接在「列表生成式」後面加上if語句,
決定篩選的條件。
語法大致上如下:

新列表 = [something for x in 舊列表(或可迭代物件) if 條件判斷句] 

直接看幾個例子比較容易理解。

取出列表中的偶數

>>> L=[18, 73, -24, 6, 34, -87, -17, -98, -53, 16]
>>> [x for x in L if x%2==0]
[18, -24, 6, 34, -98, 16]

取出列表中的負數

>>> [x for x in L if x<0]
[-24, -87, -17, -98, -53]

取出列表中的字串型態

(註: 內建函數isinstance(x, type)可以判斷前者是否為後者的變數型態)

>>> M = ["Horse","Tiger",25,6.2,"Tomato"]
>>> [x for x in M if isinstance(x, str)]
['Horse', 'Tiger', 'Tomato']

複習: 三元運算子- 簡化的if-else 語句

還記得我們在Day7- 如果你願意一層一層一層 的剝開繁瑣邏輯這篇中教過如何化簡if-else嗎?
這邊快速的幫大家複習一下:
當我們用if-else語句時,
普通的寫法表達為:

if 條件成立:
   變數= 值1
else:
   變數= 值2

必須要寫四行,實在有點繁瑣。
從前人「一目十行」,
形容人的閱讀速度很快。
現代人「螢幕十行」,
形容電腦螢幕太小,
程式碼行數太多都要上下捲動螢幕才能看到完整的程式碼。

還好在python中可以將if-else濃縮成一行,
程式語法可以簡化為:

變數 = 值1 if 條件成立 else值2

舉例來說,當我們想要判斷使用者輸入的數字是否為3的倍數,
我們便可以這樣寫:

num = int(input())
print(f"{num} 是 3 的倍數" if num % 3 ==0 else f"{num} 不是 3 的倍數")

注意使用三元運算子語法時,if-else一定要成對出現
如果是加在列表生成式for迴圈後面的if語句則不會有else出現

緊接著看幾個應用幫助了解使用情境吧。

範例13-1 阿明買蛋糕

阿明的女朋友生日要到了,
阿明決定到蛋糕店買兩個小蛋糕給女朋友慶生,
給定一個陣列表示所有可以買的蛋糕價格。
為了表達滿滿的心意。

阿明希望買的蛋糕總價格愈高愈好,
但由於阿明還是學生,打工收入不高,預算有限。
請幫阿明計算他在不超過預算的前提下,
可以買到的兩個小蛋糕價錢最高為何?

請實作函數 mostValue(nums, K)
參數nums代表所有可以購買的蛋糕價錢(一定全是正整數),
參數K 是一個正整數代表阿明的預算。
回傳陣列兩個數的最大總和但要小於K。
(如果買不起蛋糕則回傳 -1)

例子1:

nums = [100,200,500,2000]
K = 1000
Output: 700

例子1表示店內有賣100,200,500,2000元的蛋糕各一個,
但阿明預算只有1000元,
故最高只能買200+500=700元的蛋糕。
(注意不能買2個500元的蛋糕,因為500元的蛋糕只有一個。)

例子2:

nums = [100,200,500,2000]
K = 200
Output: -1

例子2表示店內有賣100,200,500,2000元的蛋糕各一個,
但阿明預算只有200元,
阿明是買不起兩個蛋糕的,故函數應回傳 -1。

函數實作如下:

def mostValue(nums, K):
    L=[nums[i]+nums[j] for i in range(len(nums)) for j in range(i+1,len(nums)) if nums[i]+nums[j]<=K]
    return max(L) if len(L)>0 else -1

函數內第一行表示窮舉陣列所有相異索引(index)的兩個數相加,
並過濾出所有不超出預算的金額。
第二行運用了三元運算子,
如果L不為空陣列(即阿明買的起兩個蛋糕),
回傳最高的金額,
否則回傳 -1。

利用以下程式進行測試:
測試程式如下:

nums = [100,200,500,2000]
print(mostValue(nums, 1000))
print(mostValue(nums, 200))

結果印出
700
-1
是我們要的預期結果。

範例13-2 聽說好課值得一修再修?

給定一個列表表示班上同學的分數,
例如:

scores = [20,30,50,60,25,70]

希望把每個人的分數換算成是否及格的結果,
期望結果: ['需要重修', '需要重修', '需要重修', '及格', '需要重修', '及格']
若分數在60分以上,換成「及格」字串,
否則換成「需要重修」字串。

其實這是三元運算子在列表生成式中的運用,
程式如下:

scores = [20,30,50,60,25,70]
passed = ["及格" if s>=60 else "需要重修" for s in scores]
print(passed) #['需要重修', '需要重修', '需要重修', '及格', '需要重修', '及格']

範例13-3 判斷質數

質數(prime) 是只能被1和自己兩個數整除的正整數 (注意1不算質數)。
判斷一個數是否是質數是數學上的經典問題,
也是程式設計課常常拿來做為入門的一個問題。
但大概少有程式語言能寫的像python一樣精簡的吧?
我們希望實作一個判斷質數的函數:

def isPrime(n):
    pass

輸入n是一個正整數,
如果n是質數,函數回傳True;
如果n不是質數,函數回傳False

目前我想到兩種簡潔的語法來做,
各自練習到在列表生成式後加上if判斷條件,
以及三元運算子應用在列表中兩種情況,
就看大家比較喜歡哪一種囉。

解法一:

第一種方法,
可以檢查所有在1~n的數字,
如果該數字可以整除n的話,
就存在列表中。
例如6的因數有1,2,3,6,
我們希望得到一張列表[1,2,3,6]。
由於質數表示恰好有兩個因數,
只要檢查這個列表是否恰有兩個元素即可。
程式碼如下:

def isPrime(n):
    return len([i for i in range(1,n+1) if n%i==0])==2

解法二:

解法二同樣去檢查所有在1~n的數字,
但是我們存一張長度為n的列表,
如果該數字可以整除n的話,
列表的對應位置為1,否則為0。
例如6的因數有1,2,3,6,
我們希望得到一張列表[1,1,1,0,0,1],
表示6可以被1, 2, 3, 6整除,
但不能被4, 5整除。
只要檢查這個列表的總和是否為2,
即知道n是否恰好有兩個因數。
程式碼如下:

def isPrime(n):
    return sum([1 if n%i==0 else 0 for i in range(1,n+1)])==2

可以簡單測試一下程式:

for i in range(1,10):
    print(f'{i}是質數?: {isPrime(i)}')

結果為:

1是質數?: False
2是質數?: True
3是質數?: True
4是質數?: False
5是質數?: True
6是質數?: False
7是質數?: True
8是質數?: False
9是質數?: False

結果正確。
但是目前解法一,二都有個待改善的問題: 效率不佳。
譬如說你在你的電腦上用這兩個解法測一下10的8次方(即一億)是否為質數:

print(isPrime(10 ** 8))

估計沒有跑個好幾秒應該是跑不出結果的。
(時間因電腦效能而異,跑個好幾十秒可能也是正常的)

為什麼我們認為算法效率不佳呢?
因為一億這個數是一個偶數呀,
當我們檢查到一億可以被2整除時,
一億就不可能是質數了,
接下來是不需要再把3到一億這麼大量的數檢查完才能判斷的。

那麼如何提升判斷質數的效率呢?
除了你可能想的到的用while-break語法的語法來解,
在檢查到一億可以被2整除時提早跳出迴圈。
我們還有python風格的語法,
同時兼顧執行效率又維持精簡的程式,
預計在後續篇章談到any(), all()函數時會探討到,敬請期待。


上一篇
Day12- 第二屠龍刀一式- 列表生成式(一) 精簡的語法,利用一個列表生出另一個
下一篇
Day14- Python 高階函數map, filter, reduce 介紹
系列文
活用python- 路遙知碼力,日久練成精30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

2 則留言

0
chitklaus
iT邦新手 5 級 ‧ 2022-05-26 18:33:57

你好
def mostValue(nums, K):
L=[nums[i]+nums[j] for i in range(len(nums)) for j in range(i+1,len(nums)) if nums[i]+nums[j]<=K]
return max(L) if len(L)>0 else -1

nums = [100,200,500,2000]
print(mostValue(nums, 1000))
print(mostValue(nums, 200))

我不太懂這裡 for j in range(i+1,len(nums))
for i in range(len(nums)) 在這裡 i = 0,1,2,3
那 j 是= 什麼? i+1 是指 0+1, 1+1, 2+1, 3+1嗎? 另外第2個參數為什麼是len(nums)? 謝謝

0
max912131
iT邦新手 4 級 ‧ 2022-10-17 10:36:40

您好,想請問課後練習題目,我的解答如下,會有可能產生其他答案上的錯誤嗎?

def findMin(N, W):
    return min(min(W[:]))

if __name__ == '__main__':
    print(findMin(N=2, W=[[740, 516, 725, 718, 861, 634, 723],
               [914, 747, 580, 593, 722, 877, 595]]))

我要留言

立即登入留言