【Python】競程相關模組、函數、方法等整理 - part 1 === 因為最近又要比賽了,所以趕緊做一篇整理複習一下,然後備註一下:這是個人筆記。 目錄(Table of Contents): [TOC] IO優化 --- ### 輸入優化 --- 使用前引入模組:`import sys` 或 `from sys import stdin`,這樣方法就不用多打 sys。 ```python= from sys import stdin line = stdin.readline() ``` 若讀到 EOF 會回傳空字串。 建議搭配 strip() 函數使用: ```python= from sys import stdin line = stdin.readline().strip() ``` * `stdin.readline()`:每次讀取一行資料,直到 `\n` 為止。由於回傳的字串通常包含 `\n`,若不需要,用 strip() 去除。 * `stdin.readlines()`:一次讀完 stdin,會回傳 list。 * `stdin.read()`:讀取整個輸入流的所有資料,直到 EOF。適合一次性讀取大量資料。 以下使用迭代的方式處理 stdin 檔: ```python= from sys import stdin for line in stdin: pass ``` 不用特地處理 EOF,迭代完即退出迴圈。 ### 輸出優化 --- 使用前引入模組:`from sys import stdout` `stdout.write()`: * 將字串輸出至標準輸出流。 * 不會自動幫你加 `\n`,要加記得加。 * 除回傳字串外,也會回傳所寫的字元總數。 ```python= from sys import stdout stdout.write("Hello, World!\n") ``` output: ``` Hello, World! 14 ``` --- `stdout.writelines()`: * 參數必為 iterable 物件。 * 迭代這個物件的每一個元素並寫入 stdout 檔。 * 無回傳值(None)。 * 若傳入的參數是字串,則 `stdout.writelines()` 的速度遠慢於 `stdout.write()`。 ### 相關應用 --- 有個 list a,然後遍歷他所有的元素。 ```python= from sys import stdout a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] stdout.write(" ".join(map(str, a))) ``` output: ``` 1 2 3 4 5 6 7 8 9 10 ``` 使用 `str.join()` 方法,搭配 `map()` 函數可減少輸出時間。 如果使用 for 迭代方式: ```python= from sys import stdout a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] for i in a: stdout.write(str(i), " ") ``` 會慢很多且無效率。 ### 多個變數輸入處理 --- ```python= from sys import stdin a, b, c, d = list(map(int, stdin.readline().split())) print(a, b, c, d) ``` math 模組 --- 須引入模組 `import math`。 引入特定方法:`from math import <要用的方法>` * `math.gcd(*integers)` , Python 3.5 回傳指定整數引數的最大公因數。若存在任一非零引數(Arguments),回傳值為所有引數共有因數中最大的正整數。若所有引數皆為零,則回傳值為 0。`gcd()` 若未傳入任何引數也將回傳 0。 ```python= from math import gcd gcd(12, 20) ``` output: ``` 4 ``` --- * `math.lcm(*integers)` , Python 3.9 回傳指定整數引數的最小公倍數。若所有引數值皆非零,回傳值為所有引數共有倍數中最小的正整數。若存在任一引數值為零,則回傳值為 0。`lcm()` 若未傳入任何引數將回傳 1。 ```python= from math import lcm lcm(12, 20) ``` output: ``` 60 ``` 若因為 Python 版本問題,可使用公式 `GCD (a, b) = (a × b)/ LCM (a, b)`,直接求 `lcm()`。 --- * `math.factorial(n)` 以整數回傳 n 的階乘。若 n 非整數型別或其值為負會引發 ValueError。 ```python= from math import factorial factorial(5) ``` output: ``` 120 ``` 5! = 1 * 2 * 3 * 4 * 5 = 120 --- * math.ceil(x):向上取整 * math.floor(x):向下取整 `floor(x)` 實則為高斯函數 `f(x) = [x]` (參見高三上數甲)。 ```python= from math import ceil ceil(2.3) ``` output: ``` 3 ``` ```python= from math import floor floor(2.3) ``` output: ``` 2 ``` eval() --- 註:這是神兵利器,但比賽有可能會 ban 這個函數。 eval() 函數可用來執行一個字串運算式,並回傳運算式的值。 ```python eval(expression[, globals[, locals]]) ``` expression:運算式 globals:變數作用域,若提供,則為 dictionary 物件。 locals:變數作用域,若提供,可為任何被 mapping 的物件。 以下範例在 Python Shell 裡執行: ```python= >>> x = 7 >>> eval( '3 * x' ) 21 >>> eval('pow(2,2)') 4 >>> eval('2 + 2') 4 >>> n=81 >>> eval("n + 4") 85 ``` ord() --- ```python ord(c) ``` c:character 字元。 將字元轉成十進位 Unicode 字元。 ```python= >>>ord('a') 97 >>> ord('b') 98 >>> ord('c') 99 ``` 註:與其相反的就是 chr() 函數,用法相同,chr() 將 Unicode 字元輸出為字母。 sum() --- ```python sum(iterable[, start]) ``` iterable:可迭代物件,如:list、tuple、set 等。 start:指定相加的參數,如果沒有設定這個值,預設為 0。 功能:求和用。 ```python= >>>sum([0,1,2]) 3 >>> sum((2, 3, 4), 1) 10 >>> sum([0,1,2,3,4], 2) 12 ``` 進制轉換 --- ### bin():轉二進位函數 --- ```python bin(x) ``` x:int 或 long int 數字 該函數會回傳字串。 ```python= >>> >>> bin(10) '0b1010' >>> bin(20) '0b10100' ``` 要去掉 0b,可做以下動作: ```python= >>> bin(10)[2:] '1010' ``` `[2:]` 將字串做切片擷取,從 2 開始擷取至結束。(因為這是字串所以可以這樣做) ### oct():轉八進位函數 --- ```python oct(x) ``` x:整數。 回傳 8 進位字串。 ```python= >>> oct(10) '0o12' >>> oct(20) '0o24' >>> oct(15) '0o17' ``` 要去掉 '0o' 的話就跟上面作法一樣。 ### hex():轉十六進位函數 --- ```python hex(x) ``` x:整數。 回傳 16 進位字串。 ```python= >>>hex(255) '0xff' >>> hex(-42) '-0x2a' >>> hex(12) '0xc' >>> type(hex(12)) <class 'str'> # 字串 ``` 排序方法 --- 結論:用 sort() 比較快。 ### sorted() --- ```python sorted(iterable, cmp=None, key=None, reverse=False) ``` iterable:可迭代物件。 cmp:比較函數,具有兩參數,參數的值從可迭代物件中取出,此函數必須遵守的規則為:大於則傳回 1,小於則傳回 -1,等於則傳回 0。 key:主要用來比較的元素,只有一個參數,具體的函數的參數就是取自於可迭代物件中,指定可迭代物件中的一個元素來進行排序。 reverse:排序規則,reverse = True 降序,reverse = False 升序(預設)。 不過建議用 key,cmp 太慢。 回傳 list。 ```python= >>> a = [5,7,6,3,4,1,2] >>> b = sorted(a) >>> a [5, 7, 6, 3, 4, 1, 2] >>> b [1, 2, 3, 4, 5, 6, 7] ``` ```python= a = [('b',1),('a',2),('c',4),('d',3)] sorted(a, key = lambda x:x[0]) // 依照字母排序 [('a', 2), ('b', 1), ('c', 4), ('d', 3)] sorted(a, key = lambda x:x[1]) // 依照數字排序 [('b', 1), ('a', 2), ('d', 3), ('c', 4)] ``` ### sort() --- ```python list.sort(cmp=None, key=None, reverse=False) ``` 參數說明同上。 此方法沒有回傳值,但會對列表的物件進行排序。 ```python= >>> a = [5,4,2,7,1,3,466,77,0] >>> a.sort() >>> print(a) [0, 1, 2, 3, 4, 5, 7, 77, 466] ``` ### sorted() v.s sort() --- 兩者差在哪? sort() 專門用在 list 資料型態。 sorted() 則是所有可迭代物件皆可用。 zip() --- ```python zip([iterable, ...]) ``` 可將可迭代物件之對應的元素打包成一個元組。 回傳值:list 包著 tuple(元組列表) ```python= >>> a = [1,2,3] >>> b = [4,5,6] >>> c = [4,5,6,7,8] >>> zipped = zip(a,b) # 打包為元組的列表 [(1, 4), (2, 5), (3, 6)] >>> zip(a,c) # 元素個數與最短的列表一致 [(1, 4), (2, 5), (3, 6)] >>> zip(*zipped) # 與 zip 相反,*zipped 可理解為解壓,回傳二維矩陣形式 [(1, 2, 3), (4, 5, 6)] ``` Tip:看列表直行,就是要打包的元組,像是 a、b 的 a[0]、b[0] 會被一起打包 -> (1,4)。 enumerate() --- ```python enumerate(sequence, [start=0]) ``` sequence:序列。迭代器或可迭代物件。 start:index 從何處開始列舉。 回傳值為:enumerate(列舉)物件。 反正這東西可列舉序列中的元素位置(index),如下: ```python= >>> seasons = ['Spring', 'Summer', 'Fall', 'Winter'] >>> list(enumerate(seasons)) [(0, 'Spring'), (1, 'Summer'), (2, 'Fall'), (3, 'Winter')] >>> list(enumerate(seasons, start=1)) # 從 1 開始列舉 [(1, 'Spring'), (2, 'Summer'), (3, 'Fall'), (4, 'Winter')] ``` 以下展示了使用 `for` 迴圈跟結合 `enumerate()` 函數的區別: ```python= >>> # 一般用法 >>> i = 0 >>> seq = ['one', 'two', 'three'] >>> for element in seq: ... print i, seq[i] ... i += 1 ... 0 one 1 two 2 three ``` ```python= >>> # 使用 enumerate() >>> seq = ['one', 'two', 'three'] >>> for i, element in enumerate(seq): ... print i, element ... 0 one 1 two 2 three ``` 生成式(Comprehension) --- 參見:https://hackmd.io/@LukeTseng/SJ36tHV1A deque(雙端佇列) --- 使用前引入模組:`from collections import deque`。 初始化一個 deque 可:`a = deque()` deque 物件有以下方法: * `append(x)`:將 x 丟到 deque 的末端。 * `appendleft(x)`:將 x 丟到 deque 的左端。 * `clear()`:清除所有 deque 元素且長度為 0。 * `copy()`:建立一個 deque 的淺複製。 * `count(x)`:計算 deque 內元素為 x 的個數 * `extend(iterable)`:將可迭代物件(如 list 等)擴展在 deque 末端。 * `extendleft(iterable)`:將可迭代物件(如 list 等)擴展在 deque 左端。 * `index(x[, start[, stop]])`:回傳 deque 中 x 的位置(或在索引 start 之後、索引 stop 之前的位置)。回傳第一個匹配的位置,如果沒找到就引發 ValueError。 * `insert(i, x)`:在 deque 位置 i 中插入 x。 * `pop()`:移除並回傳 deque 的末尾元素。 * `popleft()`:移除並回傳 deque 的最左端元素。 * `remove(value)`:移除第一個出現的 value,如果沒找到的話就引發一個 ValueError。 * `reverse()`:將 deque 中的元素原地 (in-place) 倒序排列並回傳 None。 那這可以幹嘛?用在 BFS、DFS。 bisect(二分搜) --- 使用前引入模組:`import bisect` 二分搜前置條件是要已排序的序列才可以二分搜。 函數如下: * `bisect.bisect_left(a, x, lo=0, hi=len(a), key=None)`:回傳序列 a 中 x 出現的第一個位置(輸出 index 值)。 * `bisect.bisect_right(a, x, lo=0, hi=len(a))`:與上相反,回傳 x 出現的最後一個位置的索引再 + 1。 * `bisect.bisect(a, x, lo=0, hi=len(a))`:等同於 `bisect.bisect_right`。 ```python= import bisect a = [1, 3, 3, 3, 5, 7, 9] x = 3 # bisect_left 回傳 x 第一次出現的位置 index_left = bisect.bisect_left(a, x) print("bisect_left:", index_left) # 1 # bisect_right 回傳 x 最後一個相同元素之後的位置 index_right = bisect.bisect_right(a, x) print("bisect_right:", index_right) # 4 ``` 二分搜時間複雜度:$$O(nlogn)$$ 排列組合好幫手:itertools --- 使用前引入模組:`import itertools` * itertools.permutations(iterable, r=None):排列,回傳 iterable 中連續且長度為 r 的元素排列。r 若無設定則預設為序列長度。 這東西就是:$$P^{n}_{m} = \frac{n!}{(n-m)!}$$ m = r + 1,假設 n = 5,r = 2 就會是以下數學式:$$P^{5}_{3} = 5\cdot4\cdot3=60$$ ```python= import itertools print(list(itertools.permutations("ABCD", 2))) ``` 以上的意思就等同:$$P^{4}_{3} = 4\cdot3 = 12$$ 然後會列出 12 種排列可能性。 ``` [('A', 'B'), ('A', 'C'), ('A', 'D'), ('B', 'A'), ('B', 'C'), ('B', 'D'), ('C', 'A'), ('C', 'B'), ('C', 'D'), ('D', 'A'), ('D', 'B'), ('D', 'C')] ``` * itertools.combinations(iterable, r):組合,從輸入 iterable 中回傳長度為 r 的元素的子序列。 公式:$$C^{n}_{m} = \frac{P^{n}_{m}}{m!} = \frac{n!}{m!(n - m)!}$$ ```python= import itertools print(list(itertools.combinations("ABCD", 2))) ``` ``` [('A', 'B'), ('A', 'C'), ('A', 'D'), ('B', 'C'), ('B', 'D'), ('C', 'D')] ``` 寫成數學式就是:$$C^{4}_{2} = \frac{4!}{2!\cdot2!} = 6$$ 跟輸出一樣,具有六種可能性。 lambda應用:三大常用函數 --- ### map() --- 語法: ```python map(function, iterable, ...) ``` function:函數。 iterable:可迭代物件。 回傳一個迭代器物件。 map 就是映射的意思,把 function 映射到 iterable 上面去,如下: ```python= a = ['1', '2', '3', '4'] print(list(map(int, a))) ``` 輸出: ``` [1, 2, 3, 4] ``` 以上範例為將 `int()` 函數全部套用在 a 列表內的所有元素內。 ### filter() --- ```python= filter(function, iterable) ``` function:函數。 iterable:可迭代物件。 回傳一個迭代器物件。 這東西就是用 function 去過濾 iterable 的一些元素,把它篩選掉,如下範例: ```python= >>> list(filter(lambda x: x%2==0, [1,2,3,4,5,6,7,8,9,10])) [2, 4, 6, 8, 10] ``` 把不是偶數的都篩掉了,這個意思就是把列表所有元素丟進去函數裡面運算,如果 x % 2 == 0 運算出來是 true 的話就留著,不是就把它丟掉。 然後這程式碼可以更簡化寫成: ```python= >>> list(filter(lambda x: x%2==0, range(1,11))) [2, 4, 6, 8, 10] ``` range(1, 11) 自動生成 1 ~ 10 的 list。 ### reduce() --- Python 3.x 已移到 functools,請引入:`from functools import reduce` 語法: ```python reduce(function, iterable[, initializer]) ``` function:帶有兩個參數。 該函數 reduce() 回傳函數計算結果。 reduce() 的運算原理:先將序列中兩個元素當作 function 的參數,運算完之後,function 的結果再跟下一個元素繼續丟 function 運算,算到不能再算為止。 ```python= >>> from functools import reduce >>> reduce(lambda x, y: x + y, range(1,6)) 15 ``` 以上這個輸出結果其實就是列表元素的和:$$1+2+3+4+5 = 15$$ 但要用這個去做求和,不如用:`sum(range(1,6))`。 然後如果我們用 reduce() 去解釋,就像: 先將 1, 2 拉出來當參數,然後 1 + 2 = 3 結果出來後,3, 3 再當參數,依此類推,因此最後可以得到 15。 參考資料 --- [collections — Container datatypes — Python 3.13.2 documentation](https://docs.python.org/3/library/collections.html) [Built-in Functions — Python 3.13.2 documentation](https://docs.python.org/3.13/library/functions.html) [bisect — Array bisection algorithm — Python 3.13.2 documentation](https://docs.python.org/3.13/library/bisect.html) [itertools — Functions creating iterators for efficient looping — Python 3.13.2 documentation](https://docs.python.org/3/library/itertools.html) [math --- 數學函式 — Python 3.13.2 說明文件](https://docs.python.org/zh-tw/3.13/library/math.html#math.lcm) [【筆記】Python I/O 優化 – Yui Huang 演算法學習筆記](https://yuihuang.com/python-io-optimize/) [[I/O 優化] PYTHON 篇](https://happylearningcoding.blogspot.com/2020/07/io-python.html)