【Python/スニペット】コードリーディングで知ったことをまとめる
個人的なまとめ。
他人のコードを読んでいて勉強になったと感じる部分を記述していきます。
math.ceilを使わない天井関数のような処理
math.ceil(A/B) == (A+B-1)//B == int((A+B-1)/B)
理由
いま、非負整数に対してとする。
(1). BがAを割り切るとき、なので、
これより なので、
よって、
(2). また、BがAを 割り切らない とき、非負整数はなのでとなる。
これより、なので、
配列を裏返す
# いつも使うやつ (破壊的配列操作) N = len(arr) for i in range(N>>1): arr[i], arr[-(i+1)] = arr[-(i+1)], arr[i] # New! arr2 = arr[::-1]
これは、配列に指定するStepをにすることで、「配列をひっくり返す」ということをしてる。
ループ逆回転
Pythonでループを回すときは、いつも昇順にしか回してなかった。
だから降順で回したいときは、インデックスをごにょごにょして頑張ってた。
もっとシンプル(インデックス操作を間違いにくい)方法があるんだと、他人のソースを読んで気が付いた。
# いつもの N = len(arr) arr2 = [arr[N-i-1] for i in range(N)] # New! (1) N = len(arr) arr2 = [arr[i] for i in range(N-1,-1,-1)] # New! (2) N = len(arr) arr2 = [arr[i] for i in range(N)[::-1]]
これも、先述の「配列をひっくり返す」と似ている。
パリティチェック
偶奇チェックのこと。
これを書いた人は、きっと物理レイヤー(Cとかアセンブリの世界)に近いところでお仕事してるんじゃないかって思います。
# いつもの is_odd = True if num % 2 == 1 else Flase # ちなみに、Pythonでは True == 1 (False == 0)なので以下でも行ける is_odd = num % 2 # New! is_odd = (num & 1) == 1 # または is_odd = num & 1
理由
十進数を二進数表記して考えます。
二進数表記の時、最下位ビット()はかを表します。
最下位ビット以外は、すべてと偶数になってしまうため、偶奇を決定しているのは実質的に最下位ビットです。
最下位ビットとandをとることが、すなわちパリティチェックになります。
言語によっては、コンパイラが最適化の段階で、モジュロ演算をビット演算に変換することもあるようです(wiki)
パフォーマンス
モジュロ演算とビット演算、どちらが速いのか気になったので計測してみた。
ソース
import sys import os import re from math import ceil, floor from datetime import datetime if __name__ == '__main__': even_cnt = 0 s = datetime.now() for i in range(100000): for j in range(1000): if j % 2: continue even_cnt += 1 e = datetime.now() print("Modulo : ", e-s) even_cnt = 0 s = datetime.now() for i in range(100000): for j in range(1000): if j&1: continue even_cnt += 1 e = datetime.now() print("BitAnd : ", e-s)
結果
Modulo : 0:00:06.186608 BitAnd : 0:00:07.053160
モジュロ演算のほうが速かった。マジか。