CodeIQ の「今週のアルゴリズム:ピタゴラス数」について
この記事は、結城浩(@hyuki)氏著の「数学ガール フェルマーの最終定理」↓を読んでいれば既知の内容ばかりなので、興味の無い方は読み飛ばしていただいて構いません。
数学ガール フェルマーの最終定理 (数学ガールシリーズ 2)
- 作者: 結城浩
- 出版社/メーカー: ソフトバンククリエイティブ
- 発売日: 2008/07/30
- メディア: ペーパーバック
- 購入: 35人 クリック: 441回
- この商品を含むブログ (259件) を見る
CodeIQ のアルゴリズム問題「今週のアルゴリズム:ピタゴラス数(先日締切)」を解いたのですが、その解説記事(「第1回「今週のアルゴリズム:ピタゴラス数」解説 #ruby #javascript|CodeIQ MAGAZINE」)の一部に、なんか「極めてないな感」を覚えてしまったのでカッとなって記事を書きます。
ポイントは、「互いに素の判定」。
最後に、私の提出した解答コードも掲載ます。
原始ピタゴラス数
そもそも元の問題文に
とか書いてる時点で違和感を感じていたんです。
数学的には普通、ピタゴラス数と言えば
「a2+b2=c2 を満たす自然数」
という定義です。
定義中に「互いに素」は含まれません(ついでに「a<b<c」も定義には含まれていません)。つまり「6, 8, 10」や「25, 60, 65」などのように、3つの数に公約数(>1)が存在する(=互いに素ではない)組合せも数学的にはピタゴラス数です。
「ピタゴラス数は互いに素なものである」と言うのは、例えるなら「法律で定められた休日は国民の祝日だけである」と言っているようなモノです(実際には振替休日や国民の休日(祝日と祝日に挟まれた日など)も法律で定められています)。
それと区別するために、特に互いに素なものを対象としたい場合には、「互いに素なピタゴラス数」と言っていただければまだ許せます。
でも(これは知識がなければ知らなくても仕方ありませんが)互いに素なピタゴラス数には特別な呼び方があります。
その一つが「原始ピタゴラス数」です。
(その意味は ピタゴラス数|Wikipedia にも載っているのでここでは割愛します)
だから個人的には元の問題文は、
ピタゴラス数はa2+b2=c2 を満たす自然数(ただし本問では、a<b<c とし、互いに素である組のみを対象とします)とし…
とか
対象となるピタゴラス数は 原始ピタゴラス数(すなわち a2+b2=c2 を満たす自然数で互いに素である組)で、a<b<c とし…
とかっていう風に書いて欲しかったです。
数学では定義が大事ですから。既にある定義を勝手に拡張したり制限したりしちゃダメです。
互いに素の判定
解説記事に、こんな風に書かれています。
また、今回は3つの数が互いに素かどうかを判定する必要がありますが、これは2つの数の最大公約数と、残りの1つの数との最大公約数を求めればOKです。
はい、普通のその辺に転がっている3つの数の組なら、これで間違いではありません。でも実はこれ、ピタゴラス数が相手だと少し無駄があります。なぜなら
「ピタゴラス数の3つの数のうち、2つが互いに素ならば、3つの数は互いに素(=原始ピタゴラス数)である」
だからです。
記憶に間違いが無ければこの内容も、先述の「数学ガール フェルマーの最終定理」に載っていたと思います。
私の記憶違いで載っていなかったとしても、簡単に証明できます。
概要(というか厳密でない証明)を示すと、こんな感じ。
- ピタゴラス数 a, b, c(a<b<c)のうち、aとbが互いに素でない(2以上の公約数 d が存在する)と仮定する。
- a=a0×d、b=b0×d とする。
- → c2=a2+b2
=(a0×d)2+(b0×d)2
=a02×d2+b02×d2
=(a02+b02)×d2 - c2がd2の倍数ということなので、cはdの倍数
- よって a, b, c が公約数 d(>1)を持つと言うことなので、原始ピタゴラス数ではない。
- (⇒a, b, c が互いに素となり得るのは、そのうちの2つの最大公約数が1(=互いに素)の時に限る)
この証明から分かる通り、実は
「原始ピタゴラス数以外のピタゴラス数は、ある原始ピタゴラス数を整数倍(>2)したもの」
だったりします(だから「原始」ピタゴラス数、という名前がついているのです)。
その辺のことも分かっていれば、解説記事のサンプルコードにあるような、
if ((gcd(gcd(a, b), c) == 1) && 〜《略》){
なんていうコードは無駄で、
if ((gcd(a, b) == 1) && 〜《略》){
だけで充分、ってことが分かると思います。
アルゴリズムの手前の数学。最適化の手前の数学。大事です。
ピタゴラジュースメーカー
もう1点。
ピタゴラス数は、2つの自然数 m, n(m>n>0)を使って、[m2-n2, 2×m×n, m2+n2] の組合せで生成できる、ということは解説記事にも載っています。
出題者の解答サンプルコードは、これを利用してまず a, b, c の3つの数を算出してから、互いに素かどうかを見て処理をしています*1。
これも間違いではないのですが、実は m, n に条件を加えると、初めから互いに素な組合せ(つまり原始ピタゴラス数)しか生成しないように出来ます。
Wikipedia にもちゃんと載っているし「数学ガール フェルマーの最終定理」にも『ピタゴラジュースメーカー』という愛称で紹介されています。
整数 m, n を、m>n>0、m と n は互いに素、かつ m-n は奇数(⇔m と n はいずれか一方が奇数で他方は偶数)とすると、
[m2-n2, 2×m×n, m2+n2] は原始ピタゴラス数となる。
(また a, b, c の3つの数の組が原始ピタゴラス数ならば、それに対応する m, n(で上記の条件を満たすもの)がただ1組存在する)
つまり、整数 m, n を初めから↑の条件で生成すれば、すべての原始ピタゴラス数を「漏れなくダブりなく」生成することが出来る、ということ。それだけで検索すべき数の組合せは約半分になります(m>n の2数の組 で m-n が奇数のものは約半分なので)。
(より厳密性を増すために、随所に『整数』という言葉を追記しました)
以上を踏まえて
というか他にもツッコミどころはあった*2のですがそれはうっちゃっておいて、実際に提出した私の解答コードを示します。
うん、スッキリしてますよね♪
このコードで、「正解者一番乗り☆」をいただきました♪(「第1回「今週のアルゴリズム:ピタゴラス数」正解者発表|CodeIQ MAGAZINE」参照)