在程式開發中,演算法扮演著至關重要的角色,影響著程式的效率和效能。本文將探討數個經典演算法的 Python 實作,並提供詳細的程式碼解析,幫助讀者深入理解其運作原理。涵蓋的演算法包括尋找滿足特定條件的最小七零數、字尾表示式求值、保加利亞紙牌遊戲的穩定狀態分析,以及計算曼哈頓天際線中矩形塔的面積。每個演算法都將結合程式碼範例進行說明,以加強讀者的理解和應用能力。這些演算法在不同領域都有廣泛的應用,例如數學運算、遊戲設計和圖形處理等。
3.11 最小七零數的尋找
本挑戰要求對於給定的正整數n,找到最小的僅由7和0組成的數字(稱為七零數),使得該數字是n的倍數。
演算法步驟
- 建立一個字典
remainder_positions來記錄每次迭代中的餘數及其位置,初始的current_remainder設為0。 - 設定搜尋範圍從1到10,表示要找的七零數字的位數範圍。
- 使用一個while迴圈來不斷擴大搜尋範圍,直到找到一個七零數是n的倍數。
- 在迴圈內,對於每一個可能的數字位數,計算下一個餘數,使用公式
(current_remainder * 10 + 7) % n。 - 如果某個餘數重複出現,表示找到了符合條件的七零數。
Python 程式碼實作與解析
def smallest_seven_zero(n):
remainder_positions = {0: -1}
current_remainder = 0
digits_min, digits_max = 1, 10
while True:
for num_digits in range(digits_min, digits_max):
current_remainder = (current_remainder * 10 + 7) % n
if current_remainder in remainder_positions:
# 組成七零數並傳回
seven_zero_num = '7' * num_digits
return int(seven_zero_num + '0' * (remainder_positions[current_remainder] - num_digits))
remainder_positions[current_remainder] = num_digits
digits_min, digits_max = digits_max, digits_max * 10
# 示例使用
print(smallest_seven_zero(25)) # 輸出:700
print(smallest_seven_zero(49)) # 輸出:777777
程式碼解析:
- 使用字典
remainder_positions來記錄餘數及其對應的數字位數。 - 不斷擴大搜尋範圍(從1到10,然後是10到100等),直到找到符合條件的七零數。
- 在每次迭代中,計算新的餘數,如果該餘數已經出現過,表示找到了符合條件的七零數,組成該數字並傳回。
字尾表示式求值
在這個任務中,目標是將字尾表示法(postfix notation)的數學表示式轉換為中綴表示法(infix notation),並對其進行求值。中綴表示法是指運算子寫在運算元對之間的數學表示式,如 a + b。字尾表示法則是指運算子寫在運算元之後的數學表示式,如 ab+。
演算法
為了評估給定的字尾表示式,使用了以下步驟的演算法:
- 遍歷給定字尾表示式中的每個專案。
- 如果專案是整數,則將其追加到儲存列表(Storage list)中。
- 如果專案是運算子(+、-、*、/),則從儲存列表中彈出最近的兩個專案(運算元)。
- 對這兩個運算元執行相應的運算(+、-、*、/),並將結果儲存在變數 result 中,然後將 result 追加到儲存列表中。
- 重複步驟 3-4,直到處理完字尾表示式中的所有專案。
- 最後,透過彈出儲存列表中的最後一個專案傳回求值結果。
Python 程式碼實作
def pop_last_item(input_list):
# 取得並移除輸入列表中的最後一個專案
list_length = len(input_list)
last_item = input_list[list_length - 1]
del input_list[list_length - 1]
return last_item
def postfix_evaluate(Postfix):
'''
建立一個空列表來儲存運算元和結果
'''
Storage = []
'''
遍歷字尾表示式中的每個專案
'''
for item in Postfix:
'''
如果專案是整數,將其追加到儲存列表
'''
if isinstance(item, int):
Storage.append(item)
else:
'''
如果專案是運算子,從儲存列表中彈出最近的兩個運算元
'''
Number0, Number1 = pop_last_item(Storage), pop_last_item(Storage)
'''
根據運算子對兩個運算元執行相應的運算
'''
if item == '-':
result = Number1 - Number0
elif item == '+':
result = Number1 + Number0
elif item == '*':
result = Number1 * Number0
elif item == '/':
result = Number1 / Number0
'''
將運算結果追加到儲存列表
'''
Storage.append(result)
'''
傳回儲存列表中的最後一個專案作為求值結果
'''
return pop_last_item(Storage)
內容解密:
pop_last_item函式:該函式用於取得並移除輸入列表中的最後一個專案。它透過取得列表長度,存取最後一個元素,並使用del陳述式刪除該元素來實作。postfix_evaluate函式:該函式遍歷給定的字尾表示式。如果遇到整數,則將其追加到儲存列表。如果遇到運算子,則彈出儲存列表中的最近兩個運算元,執行相應的運算,並將結果追加回儲存列表。- 運算邏輯:對於每個運算子,從儲存列表中彈出兩個運算元,執行運算後將結果存回儲存列表。這樣,最終儲存列表中的最後一個元素即為整個字尾表示式的求值結果。
- 錯誤處理:該實作假設輸入的字尾表示式是有效的,沒有進行錯誤處理。在實際應用中,應新增檢查以處理無效輸入。
範例使用
print(postfix_evaluate([5, 6, '+', 7, '*'])) # 輸出:77
print(postfix_evaluate([3, 7, 9, '*', '+'])) # 輸出:66
print(postfix_evaluate([3, 7, 9, '/', '+'])) # 輸出:3.0(注意:除法結果為浮點數)
print(postfix_evaluate([8, 2, '+'])) # 輸出:10
程式碼重點分析:
isinstance(item, int)用於檢查專案是否為整數,以區分運算元和運算子。pop_last_item函式 用於安全地從列表中移除並傳回最後一個元素,避免直接使用list.pop()可能引起的錯誤。- 運算結果處理:所有運算結果都追加到
Storage列表,最終結果透過再次呼叫pop_last_item取得。
穩定狀態的保加利亞紙牌遊戲分析
在數學問題中,保加利亞紙牌遊戲是一種特殊的紙牌遊戲,其目標是計算達到穩定狀態所需的步驟數。本文將深入分析該問題的演算法和實作細節。
問題描述
給定一個正整數列表和一個整數 $k$,判斷是否能夠達到穩定狀態,並計算所需的步驟數。穩定狀態的定義是列表中的元素與前一個列表相同,但順序可以不同。
演算法分析
該問題的解決方案涉及兩個主要演算法:is_stable_state 和 Stable_State_in_Bulgarian_Solitaire。
is_stable_state 演算法
該演算法用於判斷當前狀態是否穩定。其步驟如下:
- 建立一個長度為 $k+1$ 的計數陣列
count,並初始化為零。 - 遍歷輸入列表中的每個元素 $p$,如果 $p$ 小於或等於 $k$,則將對應的
count[p]加一。 - 遍歷
count陣列中的每個元素,如果任何元素的值不等於一,則傳回False,表示狀態不穩定。 - 如果遍歷完成後沒有傳回
False,則傳回True,表示狀態穩定。
Stable_State_in_Bulgarian_Solitaire 演算法
該演算法用於計算達到穩定狀態所需的步驟數。其步驟如下:
- 初始化變數
number_of_moves為零。 - 計算輸入列表的總和,並與三角數 $k \times (k + 1) / 2$ 進行比較。如果不相等,則傳回零,表示輸入無效。
- 進入迴圈,直到達到穩定狀態:
- 將列表中的每個元素減一。
- 移除列表中的零元素。
- 將列表的長度新增到列表末尾。
- 將
number_of_moves加一。 - 使用
is_stable_state演算法檢查當前狀態是否穩定。
- 傳回最終的
number_of_moves值。
Python 程式碼實作
def Stable_State_in_Bulgarian_Solitaire(points, k):
number_of_moves = 0
equation = k * (k + 1) // 2
def is_stable_state(points, k):
count = [0] * (k + 1)
for p in points:
if p <= k:
count[p] += 1
for i in range(1, k + 1):
if count[i] != 1:
return False
return True
if sum(points) == equation:
while not is_stable_state(points, k):
points = [p - 1 for p in points]
points = [p for p in points if p > 0]
points.append(len(points) + sum(1 for p in points if p == 0))
number_of_moves += 1
return number_of_moves
else:
return 0
程式碼解析:
- 該函式首先檢查輸入列表的元素總和是否等於三角數,如果不等於則傳回0。
- 在主迴圈中,將列表中的每個元素減一,然後移除結果為零的元素,並在列表末尾新增新的元素,其值為移除的元素數量。
- 使用
is_stable_state函式檢查每次迭代後的列表是否達到穩定狀態,如果達到則傳回累計的步數。
範例與輸出
| 輸入 | 輸出 |
|---|---|
[3], 2 | 1 |
[3, 7, 8, 14], 4 | 0 |
[10, 10, 10, 10, 10, 5], 10 | 74 |
[3000, 2050], 100 | 0 |
計算曼哈頓天際線中矩形塔的面積
給定一系列矩形塔的列表,以 (s, e, h) 的形式表示,其中 s、e 和 h 分別代表在 x 座標上的起始位置、結束位置和高度。請撰寫一個 Python 函式,輸入為 (s, e, h) 的元組,輸出為矩形塔的面積。注意,共同區域的塔不應被計算兩次。對於某些輸入,預期的輸出如表 3.14 所示。
演算法
為了計算給定塔的面積,使用了兩種演算法:divide 和 Manhattan_Skyline。divide 演算法的步驟如下:
- 它接受兩個引數
start和end。如果start等於end,則傳回一個包含兩個元素的列表:[towers[start][0], towers[start][2]]和[towers[start][1], 0]。 - 否則,計算
start和end之間的中點,並遞迴呼叫左半部分和右半部分的塔列表。將左半部分和右半部分的結果合併到一個新的列表中,步驟如下:- 計算中點位置
- 初始化一個空列表
merged,以及兩個變數i和j為 0。 - 設定
h1和h2變數為 None。 - 對
merged列表中的每個元素進行迭代,遍歷範圍為len(left) + len(right)。- 若
i大於或等於left的長度,將[right[j][0], right[j][1] if h1 is None else max(h1, right[j][1])]新增到merged,並將j加 1。 - 若
j大於或等於right的長度,將[left[i][0], left[i][1] if h2 is None else max(h2, left[i][1])]新增到merged,並將i加 1。 - 若左邊元素在索引
i處的 x 座標小於或等於右邊元素在索引j處的 x 座標,將[left[i][0], left[i][1] if h2 is None else max(h2, left[i][1])]新增到merged,並將h1設定為左邊元素在索引i處的 y 座標,然後將i加 1。 - 否則,將
[right[j][0], right[j][1] if h1 is None else max(h1, right[j][1])]新增到merged,並將h2設定為右邊元素在索引j處的 y 座標,然後將j加 1。
- 若
- 傳回合併後的列表。
Python 程式碼
def rectangular_towers_in_manhattan_skyline(towers):
'''
divide_and_conquer computes the skyline for a given range of towers
'''
def divide_and_conquer(start, end):
'''
If there is only one tower in the range, return its two corners
'''
if start == end:
return [[towers[start][0], towers[start][2]], [towers[start][1], 0]]
'''
Divide the range into two halves and recursively compute the skylines for each half
'''
mid = (start + end) // 2
left = divide_and_conquer(start, mid)
right = divide_and_conquer(mid + 1, end)
'''
Merge the two skylines using a merge sort-like approach
'''
merged = []
i, j = 0, 0
h1, h2 = None, None
for _ in range(len(left) + len(right)):
if i >= len(left):
merged.append([right[j][0], right[j][1] if h1 is None else max(h1, right[j][1])])
j += 1
elif j >= len(right):
merged.append([left[i][0], left[i][1] if h2 is None else max(h2, left[i][1])])
i += 1
elif left[i][0] <= right[j][0]:
merged.append([left[i][0], left[i][1] if h2 is None else max(h2, left[i][1])])
h1 = left[i][1]
i += 1
else:
merged.append([right[j][0], right[j][1] if h1 is None else max(h1, right[j][1])])
h2 = right[j][1]
j += 1
return merged
n = len(towers)
result = divide_and_conquer(0, n - 1)
'''
Compute the area enclosed by the skyline using the trapezoidal rule
'''
area = sum((result[i + 1][0] - result[i][0]) * result[i][1] for i in range(len(result) - 1))
return area
程式碼解析:
- 定義了一個名為
rectangular_towers_in_manhattan_skyline的函式來計算曼哈頓天際線中矩形塔的面積。 - 使用了遞迴函式
divide_and_conquer將塔列表分成更小的部分,並計算每個部分的輪廓。 - 將左右兩個部分的輪廓合併成一個新的輪廓,使用類別似歸併排序的方法進行合併。
- 使用梯形法則計算由輪廓所圍成的面積。
- 最終傳回計算出的面積。
使用範例
towers = [(2, 6, 98), (1, 0, 9)]
print(rectangular_towers_in_manhattan_skyline(towers)) # 輸出:383
towers = [(3, 7, 1)]
print(rectangular_towers_in_manhattan_skyline(towers)) # 輸出:4
towers = [(-8, 6, 3), (6, 14, 11), (0, 4, -5)]
print(rectangular_towers_in_manhattan_skyline(towers)) # 輸出:130
towers = [(2, 550, 222), (1, 0, 4)]
print(rectangular_towers_in_manhattan_skyline(towers)) # 輸出:121652