今更ながら「最大公約数」の英語が複数あることを知る。
- G.C.D (Greatest Common Divisor)
- G.C.F (Greatest Common Factor)
- H.C.F (Highest Common Factor)
英語圏の人はこれで困ったりしないんだろうか?
それはそれとして。
「立体地形図を手作りしてみようかな、っと(9)」で、x軸y軸の両方が割り切れる値、が必要だったのだけれど、公約数のシンプルな求め方、調べたことないなぁと思って。
最大公約数は一撃で求まるのね。fractions モジュールの gcd で。これだけなのよ:
1 >>> import fractions
2 >>> fractions.gcd(150, 225)
3 75
けど欲しいのは、「公約数全部」なわけね。3, 5, 15, 25, 75 のリストが欲しいのです。
どうしたもんかと思っていたが、いつものようにStackOverflowにヒントが。
まず gcd は fractions モジュールの gcd でもいいちゃぁいいんだけれど、これ、少なくとも Python 2.7 では pure Python の、ホントにこれだけなのね:
1 def gcd(a, b):
2 """Calculate the Greatest Common Divisor of a and b.
3
4 Unless b==0, the result will have the same sign as b (so that when
5 b is divided by it, the result comes out positive).
6 """
7 while b:
8 a, b = b, a%b
9 return a
これはユークリッド互除法そのもの。で、欲しいのは、この最大公約数を求めるだけで終わらせずに、残る公約数も含めリストで返すことなので、fractions モジュールの gcd を呼び出すよりは、この関数をベースに書き換えちゃっても良かろう、と。
StackOverflowにある「divisorsの求め方」として紹介されてるのが、ちょっと入り組んだワンライナー:
1 >>> [d for d in range(2, 75 // 2 + 1) if 75 % d == 0]
2 [3, 5, 15, 25]
若干伝わりにくいので、ここに至る過程を分解してみた:
1 >>> # まずはリスト内包の基礎のおさらい
2 >>> [d for d in range(10)]
3 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
4 >>> [d for d in range(10) if d % 2 == 0] # 2で割り切れるもの
5 [0, 2, 4, 6, 8]
6 >>>
7 >>> # ので
8 >>> [d for d in range(2, 75 // 2 + 1)]
9 [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37]
10 >>> [d for d in range(2, 75 // 2 + 1) if 75 % d == 0] # 75 を割り切れるもの
11 [3, 5, 15, 25]
よろしいね。ので、こんなかねぇ:
1 >>> def common_divisors(a, b):
2 ... while b:
3 ... a, b = b, a % b
4 ... return [d for d in range(2, a // 2 + 1) if a % d == 0] + [a]
5 ...
6 >>> common_divisors(225, 150)
7 [3, 5, 15, 25, 75]
これを使えば「立体地形図を手作りしてみようかな、っと(9)」の scipy.ndimage.zoom の「困らない縮小率」を機械的に決められるぞ、てなわけで。
にしても「電池付き」な Python でも提供されてないんだなぁ。numpy か scipy にないかと探してはみたけれど、ないみたい。