Problem 66
Problem 66
各 D <= 1000 (D は平方数でない整数) に対して、
(x, y は整数)
を満たす最小の x を求め、それが最大となる D を求めよ、という問題。
最初は brute force で解こうとしたけど、D = 61 で詰まった。全然整数解が見つからない。
そこで、別の方法を探した。以下、その解法。
どうやら、この手の不定方程式 (ディオファントス方程式) はペル方程式と呼ばれているらしい。
ペル方程式 - Wikipedia
解き方は
http://www004.upp.so-net.ne.jp/s_honma/pell/pell.htm
を参考にした。
リンク先のをまとめて、最終的な結論を書くと、
という漸化式があって、
または
となる i を見つければ、
のときは のときは
が、ペル方程式 の解。
しかし問題は +1 の場合のみなので、 の場合はその次の解である
が求める解となる。
#include <math.h> #include <gmp.h> #define M 1000 /* Solve Pell Equation */ int main(void) { mpz_t g, gp, h, hp, k, kp, k0, y, yp, ypp, x, xp, ansx, ansy, maxx; mpz_init(g); mpz_init(gp); mpz_init(h); mpz_init(hp); mpz_init(k); mpz_init(kp); mpz_init(k0); mpz_init(y); mpz_init(yp); mpz_init(ypp); mpz_init(x); mpz_init(xp); mpz_init(ansx); mpz_init(ansy); mpz_init_set_ui(maxx, 0); unsigned long D, maxd = 0; for (D = 2; D <= M; D++) { unsigned long d = lround(floor(sqrt(D))); if (d*d == D) { /* square number */ continue; } mpz_set_ui(gp, 0); mpz_set_ui(hp, 1); mpz_set_ui(k0, d); mpz_set(kp, k0); mpz_set_ui(yp, 0); mpz_set_ui(ypp, 1); mpz_set_ui(xp, 1); for(;;) { //g = -gp + kp*hp; mpz_mul(g, kp, hp); mpz_sub(g, g, gp); //h = (D - g*g)/hp; mpz_set_ui(h, D); mpz_submul(h, g, g); mpz_divexact(h, h, hp); //k = (k0 + g)/h; mpz_add(k, k0, g); mpz_fdiv_q(k, k, h); //y = ypp + kp*yp; mpz_mul(y, kp, yp); mpz_add(y, y, ypp); //x = g*y + h*yp; mpz_mul(x, g, y); mpz_addmul(x, h, yp); if (mpz_cmp(h, hp) == 0 || mpz_cmp(gp, g) == 0) { break; } mpz_set(gp, g); mpz_set(hp, h); mpz_set(kp, k); mpz_set(ypp, yp); mpz_set(yp, y); mpz_set(xp, x); } if (mpz_cmp(h, hp) == 0) { //ansx = (xp*x + D*yp*y)/h; mpz_mul_ui(ansx, yp, D); mpz_mul(ansx, ansx, y); mpz_addmul(ansx, xp, x); mpz_divexact(ansx, ansx, h); //ansy = (xp*y + x*yp)/h; mpz_mul(ansy, xp, y); mpz_addmul(ansy, x, yp); mpz_divexact(ansy, ansy, h); } else { //ansx = (xp*xp + D*yp*yp)/hp; mpz_mul_ui(ansx, yp, D); mpz_mul(ansx, ansx, yp); mpz_addmul(ansx, xp, xp); mpz_divexact(ansx, ansx, hp); //ansy = 2*xp*yp/hp; mpz_mul_ui(ansy, xp, 2); mpz_mul(ansy, ansy, yp); mpz_divexact(ansy, ansy, hp); } /* use x and y for temporary */ //x = ansx*ansx mpz_mul(x, ansx, ansx); //y = D*ansy*ansy mpz_mul_ui(y, ansy, D); mpz_mul(y, y, ansy); if (mpz_cmp(x, y) < 0) { /* use g as temporary */ mpz_set(g, ansx); //ansx = ansx*ansx + D*ansy*ansy; mpz_mul_ui(ansx, ansy, D); mpz_mul(ansx, ansx, ansy); mpz_addmul(ansx, g, g); //ansy = 2*ansx*ansy; mpz_mul(ansy, ansy, g); mpz_mul_ui(ansy, ansy, 2); } if (mpz_cmp(ansx, maxx) > 0) { mpz_set(maxx, ansx); maxd = D; } } gmp_printf("when D=%lu, minimal x=%Zu\n", maxd, maxx); mpz_clear(g); mpz_clear(gp); mpz_clear(h); mpz_clear(hp); mpz_clear(k); mpz_clear(kp); mpz_clear(k0); mpz_clear(y); mpz_clear(yp); mpz_clear(ypp); mpz_clear(x); mpz_clear(xp); mpz_clear(ansx); mpz_clear(ansy); mpz_clear(maxx); return 0; }