返回文章列表

技術演算法程式碼解析與實作

本文探討多種演算法的 Python 程式碼實作與解析,包含尋找最小七零數、字尾表示式求值、穩定狀態的保加利亞紙牌遊戲分析,以及計算曼哈頓天際線中矩形塔的面積。文章提供詳細的演算法步驟、程式碼說明和範例,旨在幫助讀者理解並應用這些演算法。

演算法 Python

在程式開發中,演算法扮演著至關重要的角色,影響著程式的效率和效能。本文將探討數個經典演算法的 Python 實作,並提供詳細的程式碼解析,幫助讀者深入理解其運作原理。涵蓋的演算法包括尋找滿足特定條件的最小七零數、字尾表示式求值、保加利亞紙牌遊戲的穩定狀態分析,以及計算曼哈頓天際線中矩形塔的面積。每個演算法都將結合程式碼範例進行說明,以加強讀者的理解和應用能力。這些演算法在不同領域都有廣泛的應用,例如數學運算、遊戲設計和圖形處理等。

3.11 最小七零數的尋找

本挑戰要求對於給定的正整數n,找到最小的僅由7和0組成的數字(稱為七零數),使得該數字是n的倍數。

演算法步驟

  1. 建立一個字典remainder_positions來記錄每次迭代中的餘數及其位置,初始的current_remainder設為0。
  2. 設定搜尋範圍從1到10,表示要找的七零數字的位數範圍。
  3. 使用一個while迴圈來不斷擴大搜尋範圍,直到找到一個七零數是n的倍數。
  4. 在迴圈內,對於每一個可能的數字位數,計算下一個餘數,使用公式(current_remainder * 10 + 7) % n
  5. 如果某個餘數重複出現,表示找到了符合條件的七零數。

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

程式碼解析:

  1. 使用字典remainder_positions來記錄餘數及其對應的數字位數。
  2. 不斷擴大搜尋範圍(從1到10,然後是10到100等),直到找到符合條件的七零數。
  3. 在每次迭代中,計算新的餘數,如果該餘數已經出現過,表示找到了符合條件的七零數,組成該數字並傳回。

字尾表示式求值

在這個任務中,目標是將字尾表示法(postfix notation)的數學表示式轉換為中綴表示法(infix notation),並對其進行求值。中綴表示法是指運算子寫在運算元對之間的數學表示式,如 a + b。字尾表示法則是指運算子寫在運算元之後的數學表示式,如 ab+。

演算法

為了評估給定的字尾表示式,使用了以下步驟的演算法:

  1. 遍歷給定字尾表示式中的每個專案。
  2. 如果專案是整數,則將其追加到儲存列表(Storage list)中。
  3. 如果專案是運算子(+、-、*、/),則從儲存列表中彈出最近的兩個專案(運算元)。
  4. 對這兩個運算元執行相應的運算(+、-、*、/),並將結果儲存在變數 result 中,然後將 result 追加到儲存列表中。
  5. 重複步驟 3-4,直到處理完字尾表示式中的所有專案。
  6. 最後,透過彈出儲存列表中的最後一個專案傳回求值結果。

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)

內容解密:

  1. pop_last_item 函式:該函式用於取得並移除輸入列表中的最後一個專案。它透過取得列表長度,存取最後一個元素,並使用 del 陳述式刪除該元素來實作。
  2. postfix_evaluate 函式:該函式遍歷給定的字尾表示式。如果遇到整數,則將其追加到儲存列表。如果遇到運算子,則彈出儲存列表中的最近兩個運算元,執行相應的運算,並將結果追加回儲存列表。
  3. 運算邏輯:對於每個運算子,從儲存列表中彈出兩個運算元,執行運算後將結果存回儲存列表。這樣,最終儲存列表中的最後一個元素即為整個字尾表示式的求值結果。
  4. 錯誤處理:該實作假設輸入的字尾表示式是有效的,沒有進行錯誤處理。在實際應用中,應新增檢查以處理無效輸入。

範例使用

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_stateStable_State_in_Bulgarian_Solitaire

is_stable_state 演算法

該演算法用於判斷當前狀態是否穩定。其步驟如下:

  1. 建立一個長度為 $k+1$ 的計數陣列 count,並初始化為零。
  2. 遍歷輸入列表中的每個元素 $p$,如果 $p$ 小於或等於 $k$,則將對應的 count[p] 加一。
  3. 遍歷 count 陣列中的每個元素,如果任何元素的值不等於一,則傳回 False,表示狀態不穩定。
  4. 如果遍歷完成後沒有傳回 False,則傳回 True,表示狀態穩定。

Stable_State_in_Bulgarian_Solitaire 演算法

該演算法用於計算達到穩定狀態所需的步驟數。其步驟如下:

  1. 初始化變數 number_of_moves 為零。
  2. 計算輸入列表的總和,並與三角數 $k \times (k + 1) / 2$ 進行比較。如果不相等,則傳回零,表示輸入無效。
  3. 進入迴圈,直到達到穩定狀態:
    • 將列表中的每個元素減一。
    • 移除列表中的零元素。
    • 將列表的長度新增到列表末尾。
    • number_of_moves 加一。
    • 使用 is_stable_state 演算法檢查當前狀態是否穩定。
  4. 傳回最終的 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], 21
[3, 7, 8, 14], 40
[10, 10, 10, 10, 10, 5], 1074
[3000, 2050], 1000

計算曼哈頓天際線中矩形塔的面積

給定一系列矩形塔的列表,以 (s, e, h) 的形式表示,其中 seh 分別代表在 x 座標上的起始位置、結束位置和高度。請撰寫一個 Python 函式,輸入為 (s, e, h) 的元組,輸出為矩形塔的面積。注意,共同區域的塔不應被計算兩次。對於某些輸入,預期的輸出如表 3.14 所示。

演算法

為了計算給定塔的面積,使用了兩種演算法:divideManhattan_Skylinedivide 演算法的步驟如下:

  1. 它接受兩個引數 startend。如果 start 等於 end,則傳回一個包含兩個元素的列表:[towers[start][0], towers[start][2]][towers[start][1], 0]
  2. 否則,計算 startend 之間的中點,並遞迴呼叫左半部分和右半部分的塔列表。將左半部分和右半部分的結果合併到一個新的列表中,步驟如下:
    • 計算中點位置
    • 初始化一個空列表 merged,以及兩個變數 ij 為 0。
    • 設定 h1h2 變數為 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。
  3. 傳回合併後的列表。

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