LoginSignup
4
3

More than 5 years have passed since last update.

整数の集合を一発で剰余類に分けよう

Posted at

例えば $\bmod 3$だったら

ns = range(30)
m = 3
list(zip(*zip(*[iter(ns)]*m)))
[(0, 3, 6, 9, 12, 15, 18, 21, 24, 27),
 (1, 4, 7, 10, 13, 16, 19, 22, 25, 28),
 (2, 5, 8, 11, 14, 17, 20, 23, 26, 29)]

となり,$3$ で割ったときの余りが $0$ のもの,$1$ のもの,$2$ のもので整数を分けることができます.数でないリストに突っ込んだ場合,中身を3つ飛ばしにしたものを1つの固まりにします.

ls = list('abcdefghijkl')
m = 3
list(zip(*zip(*[iter(ls)]*m)))
[('a', 'd', 'g', 'j'), ('b', 'e', 'h', 'k'), ('c', 'f', 'i', 'l')]

この記法がクールと思えるかどうかは分かりませんが,なぜこれでうまくいくかを簡単に説明しましょう.例えば

s = list(range(12))
s
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]

を3つの数ずつに4分割するのに

list(zip(*[iter(s)]*3))
[(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11)]

という方法があるのは割と有名だと思います.実は,公式チュートリアルのzip関数のところにいきなり書いてあります.

zip(*[iter(s)]*n) を使ってデータ系列を長さ n のグループにクラスタリングするイディオムが使えます。

よくよく眺めていると,この結果をzip関数に突っ込めば,3つ飛ばしのリストが出来上がることに気付くと思います.

list(zip(*zip(*[iter(s)]*3)))
[(0, 3, 6, 9), (1, 4, 7, 10), (2, 5, 8, 11)]

但し,この方法だと,10個の数のリストを3つの数ずつに分割しようとすると,半端な部分が切り捨てられてしまいます.

list(zip(*[iter(list(range(10)))]*3)) #9が切り捨てられる
[(0, 1, 2), (3, 4, 5), (6, 7, 8)]

これはzip関数の仕様です.これを回避するために,itertoolszip_longest関数が用意されています.

from itertools import zip_longest
list(zip_longest(*[iter(list(range(10)))]*3)) #半端なところにはNoneが入る
[(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, None, None)]

これを用いて,剰余類の方もアップデートしましょう.最後にNoneを取り除く行程があるのですが,tupleがイミュータブルである点は注意です.最終的には数学らしく,setの集まりにしました.

from itertools import zip_longest
ns = range(50)
m = 3
[{x for x in t if x != None} for t in zip(*zip_longest(*[iter(ns)]*m))]
[{0, 3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36, 39, 42, 45, 48},
 {1, 4, 7, 10, 13, 16, 19, 22, 25, 28, 31, 34, 37, 40, 43, 46, 49},
 {2, 5, 8, 11, 14, 17, 20, 23, 26, 29, 32, 35, 38, 41, 44, 47}]

zip関数は名前があまり好きじゃないのですが,奥が深いです.

4
3
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
3