Python AtCoder入門 第9講 関数と組み込み関数の活用 第2部の最後の講です。 ここまでで、リスト・タプル・文字列・グリッドと、データを扱う道具は一通り揃いました。 1
今回のテーマ この講では、次の2つを学びます。 書いた処理に名前を付けて整理する方法 Pythonが最初から用意している便利な道具 つまり、 関数 と 組み込み関数 です。 2
この講の目的 新しいことを解く力が一気に増える講ではありません。 しかし、 同じことをもっと楽に、間違えずに書けるようになる 講です。 コンテスト中の速度に効いてきます。 3
コードファイル名の方針 この講でも、コード例ごとにファイル名を付けます。 function_basic.py is_prime.py divmod_abs.py any_all.py gcd_lcm.py main_template.py answer_9_1.py 4
9-1 関数の定義 同じような処理を何度も書くのは面倒です。 さらに、同じ処理を何度も書くとミスの原因になります。 処理をひとまとめにして名前を付けたものが、 関数 です。 5
defで処理に名前を付ける 関数は def で定義します。 def 関数名(引数): 処理 return 戻り値 関数の中身は、インデントで表します。 6
double.py def double(x): return x * 2 print(double(5)) print(double(7)) 出力: 10 14 x が引数、 return が戻り値です。 7
関数の用語 def double(x): return x * 2 :関数を定義する合図 x :引数 return :結果を返して関数を終える return に到達した時点で、関数は終わります。 def 8
returnを書かない関数 を書かない関数も作れます。 その場合は、何も返しません。 正確には、 return None という「何もない」値が返ります。 9
function_basic.py # Return the area of a rectangle def area(w, h): return w * h # Return the larger of two values def larger(a, b): if a > b: return a else: return b print(area(3, 4)) print(larger(10, 7)) 10
function_basic.py の出力 12 10 引数は複数書けます。 if で分岐して、別々の値を返すこともできます。 11
returnで関数は終わる def larger(a, b): if a > b: return a else: return b が実行されたら、その時点で関数は終わります。 第4講の break に少し似ています。 return a 12
関数に切り出すメリット 関数にすると、次の利点があります。 1. 同じ処理を再利用できる 2. 本体が読みやすくなる 3. 単体で試せる 短いコードなら関数にしないこともありますが、複雑な処理では強力です。 13
① 同じ処理を何度も使うとき たとえば、 素数かどうか 回文かどうか 条件を満たすかどうか を複数の場所で使うなら、関数にすると便利です。 14
② 判定条件が複雑なとき 長い条件式を if に直接書くと、読みにくくなります。 関数にすると、 if is_prime(x): のように書けます。 名前が説明になります。 15
③ 単体で試せる 関数にしておけば、 print(is_prime(7)) print(is_prime(9)) のように単独で動作確認できます。 バグの原因を切り分けやすくなります。 16
is_prime.py # Return True if n is a prime number def is_prime(n): if n < 2: return False i = 2 while i * i <= n: if n % i == 0: return False i += 1 return True # Read N and the list of N integers N = int(input()) A = list(map(int, input().split())) # Count the primes count = 0 for x in A: if is_prime(x): count += 1 print(count) 17
is_prime.py の実行例 入力例: 6 2 4 7 9 11 15 出力例: 3 素数は 2 , 7 , 11 の3つです。 18
is_primeのポイント 本体側を見ると、 if is_prime(x): だけで意図が伝わります。 判定処理を直接書くより、読みやすくなります。 19
i * i <= n 素数判定では、 while i * i <= n: と書いています。 平方根を計算せず、掛け算で比較しています。 float の誤差を避けられる安全な書き方です。 20
9-2 よく使う組み込み関数 Pythonには、何も準備せずに使える関数がたくさんあります。 すでに使ってきたもの: len sum max min input print range map 21
abs abs() は絶対値を返します。 print(abs(-5)) print(abs(5)) print(abs(-3.2)) 出力: 5 5 3.2 2つの値の差は、 abs(a - b) と書けます。 22
divmod divmod() は、商と余りを同時に求めます。 q, r = divmod(17, 5) print(q, r) 出力: 3 2 タプルを返すので、アンパックで受け取ります。 23
divmod_abs.py # Read two integers A, B = map(int, input().split()) # Absolute difference print(abs(A - B)) # Quotient and remainder at once q, r = divmod(A, B) print(q, r) 入力例: 17 5 出力例: 12 3 2 24
roundは単純な四捨五入ではない は小数を丸める関数です。 しかし、単純な四捨五入ではありません。 round() print(round(0.5)) print(round(1.5)) print(round(2.5)) print(round(3.5)) 25
round_half_even.py print(round(0.5)) print(round(1.5)) print(round(2.5)) print(round(3.5)) 出力: 0 2 2 4 ちょうど .5 のとき、近いほうの偶数に丸められます。 26
roundの罠 AtCoderで、 小数第1位を四捨五入せよ と書かれているときに round() を使うと、WAになることがあります。 round(2.5) が 2 になるからです。 27
整数で四捨五入する 整数 a を整数 b で割った結果を四捨五入したいなら、 answer = (2 * a + b) // (2 * b) と書けます。 / を使わないので、誤差がありません。 28
sorted sorted(A) は、並べ替えた新しいリストを返します。 A = [3, 1, 4] print(sorted(A)) print(A) 出力: [1, 3, 4] [3, 1, 4] 元のリストは変わりません。 29
reversed は逆順にたどるものを返します。 ただし、リストではありません。 reversed(A) A = [3, 1, 4] print(reversed(A)) print(list(reversed(A))) リストとして使うなら list() が必要です。 30
reversed_list.py A = [3, 1, 4] print(list(reversed(A))) print(A[::-1]) 出力: [4, 1, 3] [4, 1, 3] 競技プログラミングでは A[::-1] のほうが手軽です。 31
any と all 条件をまとめて判定する関数です。 any(...) :1つでも True があれば True all(...) :すべて True なら True 第6講のジェネレータ式と組み合わせると便利です。 32
any_all_basic.py A = [3, 1, 4, 1, 5] print(any(x > 4 for x in A)) print(all(x > 0 for x in A)) print(all(x % 2 == 0 for x in A)) 出力: True True False 33
フラグパターンを1行にする 第4講のフラグパターンは、 found = False for x in A: if 条件: found = True break でした。 これは any() で1行にできます。 34
any_all.py # Read N and the list of N integers N = int(input()) A = list(map(int, input().split())) # Is there at least one even number? if any(x % 2 == 0 for x in A): print("Yes") else: print("No") # Are all the values distinct from 0? if all(x != 0 for x in A): print("Yes") else: print("No") 35
any_all.py の実行例 入力例: 5 3 1 4 1 5 出力例: Yes Yes 偶数が1つあり、0は1つもありません。 36
9-3 mathモジュールの頻出関数 は数学関連の関数を集めたモジュールです。 使うには先頭で読み込みます。 math import math 37
最大公約数 gcd 最大公約数は math.gcd() で求めます。 import math print(math.gcd(12, 18)) 出力: 6 3つ以上でも渡せます。 38
最小公倍数 lcm 最小公倍数は math.lcm() で求めます。 import math print(math.lcm(4, 6)) 出力: 12 39
lcmの公式 最小公倍数は、次の式でも求められます。 answer = a * b // math.gcd(a, b) または、 answer = a // math.gcd(a, b) * b 先に割ると、途中の数が小さくなります。 40
ceil と floor import math print(math.ceil(3.2)) print(math.floor(3.8)) 出力: 4 3 ただし、AtCoderでは使わないほうがよい場面があります。 41
math.ceil(N / K) は避ける math.ceil(N / K) は / を使うため、 N が大きいと誤差でずれる可能性があります。 切り上げ割り算は、第2講の定石を使います。 (N + K - 1) // K 42
sqrt import math print(math.sqrt(16)) 出力: 4.0 は float を返します。 整数として扱う問題では、誤差に注意が必要です。 math.sqrt() 43
整数で平方根を扱う 平方数判定などでは、 math.isqrt() を使うか、 i * i <= n のように掛け算で比較するのが安全です。 整数で済むなら整数で処理します。 44
gcd_lcm.py import math # Read two integers A, B = map(int, input().split()) # Greatest common divisor g = math.gcd(A, B) print(g) # Least common multiple print(A // g * B) 入力例: 12 18 出力例: 6 36 45
9-4 main関数スタイル ここまでのコードは、ファイルの先頭から順に書いてきました。 それでも問題なく動きます。 ただし、次のように main 関数にまとめる書き方もあります。 46
main関数テンプレート def main(): N = int(input()) print(N * 2) if __name__ == "__main__": main() main() の中に、問題を解く処理を書きます。 47
決まり文句 if __name__ == "__main__": main() これは、 このファイルが直接実行されたときだけ main() を呼ぶ という意味の決まり文句です。 形として覚えれば十分です。 48
main関数のメリット 主なメリットは3つです。 1. 変数の管理がしやすい 2. 実行が速くなることがある 3. 提出コードの形が毎回同じになる A・B問題で必須ではありません。 49
変数の管理がしやすい 関数の中で作った変数は、その関数の中だけで有効です。 うっかり同じ変数名を使い回して壊す事故を減らせます。 50
main_template.py def main(): # ========================================== # Write your code here # ========================================== N = int(input()) A = list(map(int, input().split())) print(sum(A), max(A)) if __name__ == "__main__": main() 51
main_template.py の実行例 入力例: 5 3 1 4 1 5 出力例: 14 5 本書のコード例では、基本的には先頭から順に書くスタイルも使います。 52
章末まとめ:関数 def 関数名(引数): 処理 return 戻り値 に到達したら関数は終わります。 関数に切り出すと、再利用でき、本体が読みやすくなります。 return 53
章末まとめ:組み込み関数 :絶対値 divmod(a, b) :商と余り sorted(A) :並べ替えた新しいリスト reversed(A) :逆順にたどるもの any(...) :1つでもTrue all(...) :すべてTrue abs(a - b) 54
章末まとめ:roundの注意 は単純な四捨五入ではありません。 round(2.5) は 2 です。 確実に四捨五入したいなら、整数だけで計算します。 round() (2 * a + b) // (2 * b) 55
章末まとめ:math import math よく使うもの: math.gcd math.lcm math.ceil math.floor math.sqrt ただし、整数問題では float を混ぜないように注意します。 56
章末まとめ:main関数 main 関数スタイルは、変数管理と速度に利点があります。 def main(): ... if __name__ == "__main__": main() 使うかどうかは自由です。 57
練習問題 9-1 素数の個数 個の整数 A_1, A_2, ..., A_N が与えられます。 このうち素数であるものの個数を出力してください。 N 58
練習問題 9-1:入力と出力 入力例: 7 2 4 7 9 11 15 1 出力例: 3 素数は 2 , 7 , 11 です。 1 は素数ではありません。 59
answer_9_1.py # Return True if n is a prime number def is_prime(n): if n < 2: return False i = 2 while i * i <= n: if n % i == 0: return False i += 1 return True # Read N and the list of N integers N = int(input()) A = list(map(int, input().split())) # Count the primes print(sum(1 for x in A if is_prime(x))) 60
answer_9_1.py のポイント を必ず入れます。 0 や 1 は素数ではありません。 割る数は、 if n < 2: i * i <= n の範囲までで十分です。 61
練習問題 9-2 平均の四捨五入 個の整数が与えられます。 平均値を求め、 小数第1位を四捨五入した値 を出力してください。 ちょうど .5 の場合は、大きいほうに切り上げます。 N 62
練習問題 9-2:入力と出力 入力例: 4 1 2 3 4 出力例: 3 平均は 2.5 なので、答えは 3 です。 63
roundを使ってはいけない この問題で round() を使うと危険です。 round(2.5) は 2 になります。 問題文の「四捨五入」と違う結果になります。 64
answer_9_2.py # Read N and the list of N integers N = int(input()) A = list(map(int, input().split())) # Round half up using integer arithmetic only S = sum(A) print((2 * S + N) // (2 * N)) float を使わず、整数だけで処理します。 65
練習問題 9-3 最大公約数と最小公倍数 3つの整数 A , B , C が与えられます。 3つの最大公約数と最小公倍数を、この順に空白区切りで1行に出力してください。 66
練習問題 9-3:入力と出力 入力例: 4 6 10 出力例: 2 60 最大公約数は 2 。 最小公倍数は 60 です。 67
answer_9_3.py import math # Read three integers A, B, C = map(int, input().split()) # GCD and LCM of three values print(math.gcd(A, B, C), math.lcm(A, B, C)) math.gcd() と math.lcm() は3つ以上の値にも使えます。 68
answer_9_3_lcm_func.py import math # Least common multiple of two values def lcm(a, b): return a // math.gcd(a, b) * b # Read three integers A, B, C = map(int, input().split()) print(math.gcd(A, B, C), lcm(lcm(A, B), C)) 関数定義と math.gcd() を組み合わせています。 69
第9講まとめ これで第2部は完了です。 データを扱う道具は一通り揃いました。 この講では、 処理に名前を付ける方法 と 便利な組み込み関数 を学びました。 70
次回予告 次の第10講からは第3部に入ります。 テーマは、 辞書と集合 です。 リストと二重ループでも書ける処理を、正しい道具で一瞬で書けるようにします。 71