Project Euler

Project Euler 問題9(特別なピタゴラス数)を解いてみる

Euler_009

Project Euler で紹介されている問題9を解いてみました。素直なプログラム例とピタゴラス数が満たす性質を使った解き方を紹介します。

問題9(特別なピタゴラス数)

次の式を満たす3つの自然数 a<b<c をピタゴラス数と呼ぶ。

a2+b2=c2

例)32+42=9+16=25=52

a+b+c=1000 となるピタゴラス数がただ一つ存在する。このピタゴラス数の積 a×b×c を求めよ。

解答例

方針としては、a<b<c かつ a+b+c を満たす abc を生成して、それが三平方の式を満たすか調べることにします。

検算をするために解答に加えて、求めたピタゴラス数も出力しています。C 言語での実装例です。

#include <stdio.h>

#define SUM 1000

int main(void)
{
	int a, b, c;

	for (a = 1; a <= (SUM/3); ++a) {
		for (b = a + 1; b <= (SUM/2); ++b) {
			c = SUM - (a + b);
			if (b < c) {
				if (a * a + b * b == c * c) {
					printf("Problem009: %d\n", a * b * c);
					printf("Problem009: a:%d b:%d c:%d\n", a, b, c);
				}
			}
		}
	}

	return 0;
}

プログラムは、条件をみたすピタゴラス数をすべて出力します。プログラムを動作させると、問題に記載されている通り、1通りしか解が出力されないことも確認できます。

考察

この問題は、工夫すれば、手計算でも解を求めることができます。

原始ピタゴラス数

abc の最大公約数が 1 である、つまり abc が互いに素であるピタゴラス数を原始ピタゴラス数(primitive Pythagorean)と呼びます。

例えば、345 は原始ピタゴラス数です。明らかに原始ピタゴラス数を k 倍した数もピタゴラス数になります。345 を2倍した、6810 は、62+92=102 を満たします。

次の事実(定理)が古代ギリシャですでに知られていました。証明は省略します。

自然数 m,n(m>n>0)を用いて、

a=m2n2,b=2mn,c=m2+n2

とすると、a,b,c はピタゴラス数になる。

逆にすべての原始ピタゴラス数は、この形で表すことができる。

問題に適用する

つまり、すべてのピタゴラス数は、原始ピタゴラス数を k 倍(k1 の場合も含む)した数となるため、すべてのピタゴラス数は、以下の形になります。

a=k(m2n2),b=2kmn,c=k(m2+n2)

これから a+b+c を計算すると、

a+b+c=k(m2n2)+2kmn+k(m2+n2)
=2km(m+n)

一方、a+b+c=1000=2×2×2×5×5×5 となるため

500=2×2×5×5×5=km(m+n)

ここで、m+n は、明らかに m より大きいため、素因数から、考えられる mm+n をすべて抜き出して、m>n を満たすか確認します。

mm+n
小さな値から調べる
m>n を満たす n があるか?
12×(m=1,n=1 となる)
25×(m=2,n=3 となる)
45〇(m=4,n=1 となる)
425×(m=4,n=21 となる)
510×(m=5,n=5 となる)
1025×(m=10,n=15 となる)
2025〇(m=20,n=5 となる)
25候補が存在しない×

解が2つでたように表から読み取れますが、m=4=2×2m+n=5 の場合、k=25=5×5となります。もう一方のm=20=2×2×5m+n=25=5×5 となるため、k=1 となります。この解は、以下を計算して一致していることが分かります(この問題では、a<b となることを求めているため、気になるかたは、以下の ab を入れ替えて読んでください)。

a=25×(4212),b=25×2×4×1,c=25×(42+12)

a=20252,b=2×20×5,c=202+52

ピタゴラス数がもつ性質によって、解答を求めることができました。

解答案(改善版)

考察したロジックをプログラムに落とし込みます。手計算では素因数から候補を求めましたが、プログラムの場合は、2重ループで、k の値が自然数になるか確認しています。プログラムは、mnk も出力しています。

#include <stdio.h>

#define SUM 1000

int main(void)
{
	int m, n, k;
	int a, b, c;

	for (n = 1; n * n <= SUM; ++n) {
		for (m = n + 1; m * m <= SUM; ++m) {
			if ((SUM / 2)%(m *(m + n)) == 0) {
				k = (SUM / 2)/(m *(m + n));
				if ((m * m - n * n)<= 2 * m * n) {
					a = k *(m * m - n * n);
					b = k * 2 * m * n;
				} else {
					a = k * 2 * m * n;
					b = k *(m * m - n * n);
				}
				c = k *(m * m + n * n);
				printf("Problem009: %d\n", a * b * c);
				printf("Problem009: a:%d b:%d c:%d\n", a, b, c);
				printf("Problem009: m:%d n:%d k:%d\n", m, n, k);
			}
		}
	}

	return 0;
}

素直なプログラムは、1000に対して、1000/2、1000/3 まで調べています(工夫すれば、もう少し小さくできます)。一方、考察に従ったプログラムは、1000 まで調べることになるため、和の数(問題9の場合、1000)が大きくなっていくと、考察に従ったプログラムの方が速く動作するようになります。

最後に

素直なプログラムを提示したあと、ピタゴラス数を生成する式から解答を求めてみました。

今回の問題を解くためには使いませんでしたが、高校数学の数学A「整数の性質」の範囲で、余り(合同式)を使う証明として、以下の例がよくでてきます(a、b、c はピタゴラス数とします)。

  • a、b のうち、少なくともひとつは、3の倍数である。
  • a、b のうち、少なくともひとつは、4の倍数である。
  • a、b、c のうち、少なくともひとつは、5の倍数である。

ピタゴラス数は、昔から知られていますが、興味深い性質が多くあるようです。

引き続き、Project Euler の問題を紹介していきます。

COMMENT

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA