幫醜程式整型

更新於 發佈於 閱讀時間約 7 分鐘

「天啊!這程式怎麼這麼醜!」瞪著螢幕上先前寫的程式,不禁從心底冒出這樣的一句話。

因為先去研究Pytorch,所以有好一陣子沒有寫Game of Life這支程式。現在回頭再來看先前寫的,還真是有種慘不忍睹的感覺,怎麼看都覺得不順眼。究竟問題出在哪呢?先前寫的時候,還覺得自己挺有才的,居然能想到挺妙的點子來處理一些問題,怎麼這些妙點子,現在看來卻是平凡無奇,沒啥特別的。仔細想想,或許是因為使用的方法不是很好,也沒有好好發揮Python的長處,所以整支程式看起來感覺醜醜的。說得有學問一點,就是設計出來的演算法實在不怎麼樣,所以沒法運用Python的特點,讓程式簡潔、有效率。

既然知道問題出在哪裡,那就來幫程式整型囉,把它整得美美的。之所以說是整型,而不是說化妝,是因為連演算法都要換。更換演算法,可不是改改變數名稱,或者把兩行併成一行,這樣子的表面功夫而已,而是從骨子裡頭改造,面子、裡子都要重新型塑的大工程。

先來看看原先的寫法有什麼缺陷。原先的寫法,是把universe當成黑白影像,然後用處理影像的方式,一個一個cell的去看看,演化到下一代時,cell是生或是死。這樣子的做法很直觀、很容易理解,反正就是去數周邊的鄰居有幾個是活著的而已。但是稍微觀察一下就可以發現,這樣子的做法非常沒有效率。因為在大部分的情況下,活著的cell只佔少數,整個universe中,存在著大片沒有任何cell活著的死寂區域,而這些死寂區域內的cell,是沒可能在演化到下一代時活過來的。所以囉,一個一個cell的去檢查,大部分都是在做白工,實在是沒必要。

既然原先的寫法有著做很多白工的缺陷,那有沒有辦法避免做白工呢?如果能把演化到下一代時,有可能會改變狀態的cell全部找出來,然後只處理這些cell,那就不會做白工了!所以現在的問題是,怎麼判斷某一個cell在演化到下一代時,有可能會改變狀態?

一個cell在演化到下一代時的狀態,完全取決於周遭的8個鄰居,有多少個是活著的。所以囉,換個角度看,只有活著的cell的周遭鄰居,才有可能會改變狀態。也就是說,在進行演化時,需要處理的,就只有活著的cell,以及它們周遭的鄰居。至於其他的cell,因為根本不可能改變狀態,所以就無視、忽視、不需理它們。

解決問題的辦法有了,那要怎麼寫呢?最關鍵的地方,就是要把所有活著的cell,以及它們的鄰居,全給收集起來。既然要收集東西,那就得找個東西來裝。在Python裡頭,可以拿來裝東西的容器有list、tuple、dictionary、set,哪個比較合用呢?

在收集的過程中,要一個一個的把cell給裝進容器中,所以tuple不合用,因為它是immutable,只能在一開始的時候把東西裝進去,之後就不能再裝了。

某一個cell有可能同時是兩個活著的cell的鄰居,所以要把它裝進容器時,有可能它已經在容器裡頭了。list和dictionary要自己檢查、排除,才能避免把同樣的東西裝進去,不太合用。set倒是沒這個問題,同樣的東西塞進去之後,它會像魔術師的袋子一樣,自動把多的變不見,只留下一個。所以在把cell裝進set時,不需檢查裡頭的東西會不會重複,儘管裝就是了。這樣看來,set是個不錯的選擇。

裝東西用的容器有了,要收集狀態有可能改變的cell,用(x, y)來表示在那個座標位置的cell,先把活著的cell放到cells_alive這個set裡頭,然後程式可以這麼寫:

cells_need_check = set()
for x0, y0 in cells_alive:
  cells_need_check |= {(x0+x, y0+y) for x in range(-1, 2)
              for y in range(-1, 2) if 0 <= x0+x < width
              if 0 <= y0+y < height}

這裡的width和height,是universe的寬和高。利用set 特有的union方法,以及comprehension,短短的幾行,就達到目的了!

接下來,要來看看在cells_need_check裡頭的cell,它周遭的鄰居,有幾個是活著的。假設現在要處理的cell是(x0, y0),程式就這麼寫:

for x0, y0 in cells_need_check:
  neighbors = {(x0+x, y0+y) for x in range(-1, 2) for y in range(-1, 2)} – {(x0, y0)}
  neighbors_alive = neighbors & cells_alive
n = len(neighbors_alive)

要知道周圍鄰居有幾個是活著的,不需要一個一個的去數,那太麻煩了。把周遭鄰居放在neighbors這個set裡頭,它和cells_alive的intersection所得到的set,在裡頭放著的,就是周圍鄰居中活著的cell,而它的數量,用len()這個函數就可以馬上知道,不用花時間去一個一個數。

換個處理的方式,再加上好好利用Python提供的功能,「核心計算」部分的程式碼,減少了將近30%。當然啦,這只是參考用,程式寫得好不好,可不能單純用長度來衡量。

除了程式碼長度外,看起來執行速度應該也可提高不少,因為不用一網打盡地處理所有的cell,而只需要處理少數可能改變狀態的cell。當然啦,這只是就理論上來看,沒有真正實際去測。測量程式改寫後能快多少,實在是有點無聊。

至於使用的記憶體,應該少很多,因為原先用來記錄每個cell狀態的list,現在已經不需要了。現在只需要紀錄活著的cell和他們的鄰居,而這些只佔所有cell數量的少數。當然啦,這只是想當然耳的這麼認為,真正的情況是怎樣,實際去測才知道。

整型後的程式,看起來漂亮多了!

留言
avatar-img
留言分享你的想法!
avatar-img
ysf的沙龍
18會員
146內容數
寫點東西自娛娛人
ysf的沙龍的其他內容
2025/04/14
花了些時間,靜下心來,仔仔細細地研究了一番,總算把Python呼叫函數時引數的傳遞方式給徹底搞清楚了。
2025/04/14
花了些時間,靜下心來,仔仔細細地研究了一番,總算把Python呼叫函數時引數的傳遞方式給徹底搞清楚了。
2024/05/08
呼!折騰了好久,終於徹底搞清楚pygame的各個blend mode所用的計算式,到底是長啥樣子了。
2024/05/08
呼!折騰了好久,終於徹底搞清楚pygame的各個blend mode所用的計算式,到底是長啥樣子了。
2023/12/20
在寫《The Nature of Code閱讀心得筆記——使用Python實作》的[第四章]4.3節時,原書提到,在使用Java的ArrayList時,如果用迴圈一面走訪一面又移除其中的元素,那會有難以察覺的問題存在。寫個小程式測試的結果發現,Python的list也會有一樣的問題。
Thumbnail
2023/12/20
在寫《The Nature of Code閱讀心得筆記——使用Python實作》的[第四章]4.3節時,原書提到,在使用Java的ArrayList時,如果用迴圈一面走訪一面又移除其中的元素,那會有難以察覺的問題存在。寫個小程式測試的結果發現,Python的list也會有一樣的問題。
Thumbnail
看更多
你可能也想看
Thumbnail
每年4月、5月都是最多稅要繳的月份,當然大部份的人都是有機會繳到「綜合所得稅」,只是相當相當多人還不知道,原來繳給政府的稅!可以透過一些有活動的銀行信用卡或電子支付來繳,從繳費中賺一點點小確幸!就是賺個1%~2%大家也是很開心的,因為你們把沒回饋變成有回饋,就是用卡的最高境界 所得稅線上申報
Thumbnail
每年4月、5月都是最多稅要繳的月份,當然大部份的人都是有機會繳到「綜合所得稅」,只是相當相當多人還不知道,原來繳給政府的稅!可以透過一些有活動的銀行信用卡或電子支付來繳,從繳費中賺一點點小確幸!就是賺個1%~2%大家也是很開心的,因為你們把沒回饋變成有回饋,就是用卡的最高境界 所得稅線上申報
Thumbnail
全球科技產業的焦點,AKA 全村的希望 NVIDIA,於五月底正式發布了他們在今年 2025 第一季的財報 (輝達內部財務年度為 2026 Q1,實際日曆期間為今年二到四月),交出了打敗了市場預期的成績單。然而,在銷售持續高速成長的同時,川普政府加大對於中國的晶片管制......
Thumbnail
全球科技產業的焦點,AKA 全村的希望 NVIDIA,於五月底正式發布了他們在今年 2025 第一季的財報 (輝達內部財務年度為 2026 Q1,實際日曆期間為今年二到四月),交出了打敗了市場預期的成績單。然而,在銷售持續高速成長的同時,川普政府加大對於中國的晶片管制......
Thumbnail
重點摘要: 6 月繼續維持基準利率不變,強調維持高利率主因為關稅 點陣圖表現略為鷹派,收斂 2026、2027 年降息預期 SEP 連續 2 季下修 GDP、上修通膨預測值 --- 1.繼續維持利率不變,強調需要維持高利率是因為關稅: 聯準會 (Fed) 召開 6 月利率會議
Thumbnail
重點摘要: 6 月繼續維持基準利率不變,強調維持高利率主因為關稅 點陣圖表現略為鷹派,收斂 2026、2027 年降息預期 SEP 連續 2 季下修 GDP、上修通膨預測值 --- 1.繼續維持利率不變,強調需要維持高利率是因為關稅: 聯準會 (Fed) 召開 6 月利率會議
Thumbnail
寫程式最怕碰到的,就是信心滿滿地寫好程式後,發現結果不如預期,而且完全看不出問題出在哪裡。這種慘況可以分成兩種:一種是程式很長、很複雜,想要把心機很重,躲在幽暗深處的臭蟲給抓出來,即使有功能強大的除錯工具,都不是件簡單的事;另一種是程式沒幾行,一切看起來都很清楚正常,臭蟲根本沒地方躲藏,可是結果就是
Thumbnail
寫程式最怕碰到的,就是信心滿滿地寫好程式後,發現結果不如預期,而且完全看不出問題出在哪裡。這種慘況可以分成兩種:一種是程式很長、很複雜,想要把心機很重,躲在幽暗深處的臭蟲給抓出來,即使有功能強大的除錯工具,都不是件簡單的事;另一種是程式沒幾行,一切看起來都很清楚正常,臭蟲根本沒地方躲藏,可是結果就是
Thumbnail
軟體開發是在虛擬的空間重新描述並解決現時的問題,多數時候並不存在正確答案。如何穿越這些不確定及未知就體現了開發者的功力以及對事物的把握度。 標題有點聳動,但且以這篇短文紀錄幾個印象比較深的、飛一陣後發現什麼節論都沒得到的可能作法(? 所以其實是要反著看 … 以下列舉三個常碰到的情況跟大家分享
Thumbnail
軟體開發是在虛擬的空間重新描述並解決現時的問題,多數時候並不存在正確答案。如何穿越這些不確定及未知就體現了開發者的功力以及對事物的把握度。 標題有點聳動,但且以這篇短文紀錄幾個印象比較深的、飛一陣後發現什麼節論都沒得到的可能作法(? 所以其實是要反著看 … 以下列舉三個常碰到的情況跟大家分享
Thumbnail
「天啊!這程式怎麼這麼醜!」瞪著螢幕上先前寫的程式,不禁從心底冒出這樣的一句話。
Thumbnail
「天啊!這程式怎麼這麼醜!」瞪著螢幕上先前寫的程式,不禁從心底冒出這樣的一句話。
Thumbnail
或許就如官網文件中所說的,lambda function就只是syntactic sugar而已,所以也就沒特別在意,直到在設計Game of Life的輸入介面時,因為需要用到,兜兜轉轉,費了好些功夫和時間,總算對它的用途和用法有比較完整的認識。
Thumbnail
或許就如官網文件中所說的,lambda function就只是syntactic sugar而已,所以也就沒特別在意,直到在設計Game of Life的輸入介面時,因為需要用到,兜兜轉轉,費了好些功夫和時間,總算對它的用途和用法有比較完整的認識。
Thumbnail
Game of Life的輸出結果,也就是每代演化後universe的長相,程式要怎麼寫呢?現在有個二維list,裡頭放的是一堆0和1,要怎麼樣用西洋棋盤式的方式來顯示呢?這看來勢必得用到別人寫好的module來做,才能省時、省力又漂亮。第一個想到的,當然就是matplotlib這個科學繪圖用的mo
Thumbnail
Game of Life的輸出結果,也就是每代演化後universe的長相,程式要怎麼寫呢?現在有個二維list,裡頭放的是一堆0和1,要怎麼樣用西洋棋盤式的方式來顯示呢?這看來勢必得用到別人寫好的module來做,才能省時、省力又漂亮。第一個想到的,當然就是matplotlib這個科學繪圖用的mo
Thumbnail
Game of Life的「核心計算」部分寫好了,短短的沒幾行,畢竟也就那麼幾條判斷規則而已,沒什麼太複雜的東東要處理。說是寫好了,但到底能不能跑、跑出來的結果對不對,那可還是在未定之天哩。
Thumbnail
Game of Life的「核心計算」部分寫好了,短短的沒幾行,畢竟也就那麼幾條判斷規則而已,沒什麼太複雜的東東要處理。說是寫好了,但到底能不能跑、跑出來的結果對不對,那可還是在未定之天哩。
Thumbnail
寫程式時經常會有卡關的現象,這時候不要一味蠻幹,轉換一下看問題的角度,說不定就能過關。
Thumbnail
寫程式時經常會有卡關的現象,這時候不要一味蠻幹,轉換一下看問題的角度,說不定就能過關。
Thumbnail
comprehension應該可說是Python的絕學之一吧。不過既然是絕學,總是會有讓人容易在運氣時一個不小心走錯經脈的地方。現在我需要一個二維的list,利用comprehension來造一個,應該是再好不過的選擇。只是這地方,就是個容易出錯的地方。
Thumbnail
comprehension應該可說是Python的絕學之一吧。不過既然是絕學,總是會有讓人容易在運氣時一個不小心走錯經脈的地方。現在我需要一個二維的list,利用comprehension來造一個,應該是再好不過的選擇。只是這地方,就是個容易出錯的地方。
Thumbnail
蝦蜜?!Python沒有內建array?!剛發現這件事時,還真的有點傻眼,怎麼會沒有array這麼好用的data type呢?
Thumbnail
蝦蜜?!Python沒有內建array?!剛發現這件事時,還真的有點傻眼,怎麼會沒有array這麼好用的data type呢?
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News