squareRoot 1 から 99 の間の平方根の値を多桁数で算出する。
int squareRoot(int *data, int keta, int n);
data (出力)平方根の値が10進4桁ずつ入った整数の配列 keta (入力)小数点以下の桁数の指定 n (入力)計算しようとする平方根の2乗の値
正常に計算できたときに、計算結果となる data の長さ。エラーのとき 0。
int squareRoot(int *data, int keta, int n)
{
int *pbuf, *qbuf, *tbuf;
int *pp, *qq, *tt;
int pketa, qketa;
int i;
int ret;
static long p0[101] = { /* p0*p0 = 4+n*q0*q0 */
0,-1, 6726, 2702, -2, 5778, 9602, 4048, 6726, -3, 1442,
7940, 2702, 1298, 898, 3842, -4, 4354, 1154, 340, 5778,
2525, 394, 2302, 9602, -5, 102, 2702, 4048, 727, 482,
3040, 6726, 2114, 4898, 1692, -6, 146, 5474, 2498, 1442,
4098, 674, 6964, 7940, 2207,48670L,9214, 2702, -7, 198,
9998, 1298, 2599, 970, 178, 898, 302,39206L,1060,3842,
1523, 126, 4048, -8, 258, 130,97684L,4354, 623, 502,
6960, 1154,4562498L,7398,2702, 340, 6239, 106, 160, 5778,
-9, 326, 164, 110, 6887, 20810, 3134, 394,1000002L,1442,
3148, 2302, 839,4286590L,6082,9602,125619266L,198,7940,-10 };
static long q0[101] = {
0,-1, 4756, 1560, -2, 2584, 3920, 1530, 2378, -3, 456,
2394, 780, 360, 240, 992, -4, 1056, 272, 78, 1292,
551, 84, 480, 1960, -5, 20, 520, 765, 135, 88,
546, 1189, 368, 840, 286, -6, 24, 888, 400, 228,
640, 104, 1062, 1197, 329, 7176, 1344, 390, -7, 28,
1400, 180, 357, 132, 24, 120, 40, 5148, 138, 496,
195, 16, 510, -8, 32, 16,11934, 528, 75, 60,
826, 136,534000L,860, 312, 39, 711, 12, 18, 646,
-9, 36, 18, 12, 747, 2244, 336, 42,106000L,152,
330, 240, 87,442128L,624, 980,12754704L,20, 798, -10 };
if (n < 1 || n > 100 || keta < 1) return 0;
if (p0[n] < 0) {
*data = -p0[n];
return 1;
}
if (p0[n]*p0[n] != 4 + q0[n]*q0[n]*n)
return 0; /* Oh, my God ! */
ret = 0;
pbuf = qbuf = tbuf = NULL;
pbuf = (int *)calloc(2*keta+3, sizeof(int));
qbuf = (int *)calloc(keta+3, sizeof(int));
tbuf = (int *)calloc(keta+3, sizeof(int));
if (pbuf != NULL && qbuf != NULL && tbuf != NULL) {
if (p0[n] > 10000)
*pbuf = p0[n]%10000, *(pbuf+1) = p0[n]/10000, pketa = 2;
else *pbuf = p0[n], pketa = 1;
if (q0[n] > 10000)
*qbuf = q0[n]%10000, *(qbuf+1) = q0[n]/10000, qketa = 2;
else *qbuf = q0[n], qketa = 1;
while (qketa*4 < keta) {
/* q = p * q */
qketa = mulf(tbuf, pbuf, pketa, qbuf, qketa);
for (tt = tbuf, qq = qbuf, i = qketa; i > 0; i--) *qq++ = *tt++;
/* p = p * p - 2 */
pketa = mulf(tbuf, pbuf, pketa, pbuf, pketa);
for (tt = tbuf, pp = pbuf, i = pketa; i > 0; i--) *pp++ = *tt++;
*pbuf -= 2;
}
ret = divf(data, pbuf, pketa, qbuf, qketa, tbuf);
}
if (tbuf != NULL) free(tbuf);
if (pbuf != NULL) free(qbuf);
if (qbuf != NULL) free(pbuf);
return ret;
}
/* mm <-- aa * bb */
static int mulf(int *mm, int *aa, int la, int *bb, int lb)
{
int i, j;
int *a, *b, *m;
int lm, ca;
long x;
for (m = mm, i = la + lb; i > 0; i--) *m++ = 0;
for (b = bb, j = 0; j < lb; j++, b++) {
for (ca = 0, a = aa, i = 0; i < la; i++) {
x = *a++;
x = x * *b + *(mm + i + j) + ca;
*(mm + i + j) = x % 10000;
ca = x / 10000;
}
*(mm + i + j) = ca;
}
return la + lb - (ca == 0);
}
/* qq <-- aa / bb */
static int divf(int *qq, int *aa, int la, int *bb, int lb, int *tt)
{
int i;
int *a, *b, *q, *t, *s;
int ca, k, Q, lt, lq, m;
long x;
for (a = aa + la + lb, s = aa + la, i = la; i > 0; i--) *--a = *--s;
for (i = lb; i > 0; i--) *--a = 0;
la += lb;
if ((k = 4999 / *(bb + lb - 1) + 1) > 1) {
for (ca = 0, a = aa, t = tt, i = 0; i < la; i++) {
x = (long)k * *a++ + ca;
*t++ = x % 10000, ca = x / 10000;
}
*t = ca;
la = i + (ca > 0);
for (t = tt, a = aa, i = la; i > 0; i--) *a++ = *t++;
for (ca = 0, b = bb, t = tt, i = 0; i < lb; i++) {
x = (long)k * *b++ + ca;
*t++ = x % 10000, ca = x / 10000;
}
*t = ca;
lb = i + (ca > 0);
for (t = tt, b = bb, i = lb; i > 0; i--) *b++ = *t++;
}
lq = la - lb + 1;
for (q = qq, i = lq; i > 0; i--) *q++ = 0;
while ((m = la) >= lb) {
if (*(aa + la - 1) >= *(bb + lb - 1)) {
for (a = aa + la - 1, b = bb + lb - 1; b != bb; a--, b--)
if (*a != *b) break;
if (b == bb) {
*(qq + la - lb) = 1;
la -= lb;
continue;
} else if (*a > *b) {
for (s = b, ca = 0, a = aa + la - lb, b = bb; b <= s; a++) {
*a -= *b++ + ca;
ca = 0;
if (*a < 0) *a += 10000, ca = 1;
}
while (*--a == 0) ;
*(qq + la - lb) = 1;
la = a - aa + 1;
continue;
}
Q = 9999;
} else Q = (10000L * *(aa+la-1) + *(aa+la-2)) / *(bb+lb-1);
if (la == lb) break;
for ( ; ; ) {
if (Q == 1) {
*(bb + lb) = 0;
for (ca=0, a=aa+(la-lb-1), b=bb, i=lb; i >= 0; i--, a++) {
*a -= *b++ + ca;
ca = 0;
if (*a < 0) *a += 10000, ca = 1;
}
while (*--a == 0) ;
la = a - aa + 1;
break;
}
for (ca = 0, t = tt, b = bb, i = 0; i < lb; i++) {
x = (long)Q * *b++ + ca;
*t++ = x % 10000, ca = x / 10000;
}
*t = ca;
lt = i + 1;
for (a = aa + la - 1, t = tt + lt - 1; t != tt; a--, t--)
if (*a != *t) break;
if (t == tt) {
la -= lt;
break;
} else if (*a > *t) {
for (s=t, ca=0, a = aa+(la-lt), t = tt; t <= s; a++) {
*a -= *t++ + ca;
ca = 0;
if (*a < 0) *a += 10000, ca = 1;
}
while (*--a == 0) ;
la = a - aa + 1;
break;
} else Q--;
}
*(qq + m - lb - 1) = Q;
}
if (*(qq + lq - 1) == 0) lq--;
return lq;
}
pi+1 = pi2 - 2
qi+1 = pi qi
を計算し、最後に、pi+1 / qi+1 で平方根を求める。
なお、初期値 p0、q0 は ペル方程式 p02 - M q02 = 4 を満たす 整数解である。ただし、M は計算しようとする平方根の2乗の値。
数学的帰納法で証明できるが、上の漸化式で得られた pi+1、 qi+1 により、
pi+1 / qi+1 = (M + 4 / (pi qi)2)1 / 2
となる。 したがって平方根の計算において、本アルゴリズムの方法ではニュートン法等ほかの方法よりも 効率がよく、数回の計算で必要とする精度のものが得られる。