#include "multiprec.h"#include <string.h>関数 | |
| void | ymp_iroot (mp_ref_t result, mp_cref_t self, size_t n) |
| 多倍長整数の冪乗根の整数部分. より詳しく... | |
|
||||||||||||||||
|
多倍長整数の冪乗根の整数部分. result := floor( <self> ** (1/n) )
参照 DIGIT_BIT, multiprec::digits, mp_cref_t, positive_sign, multiprec::sign, multiprec::used, ymp_add, ymp_add_digit, ymp_alloca_initialize, ymp_assign, ymp_assign_abs, ymp_assign_abs_digit, ymp_compare_abs, ymp_div, ymp_fatal, ymp_ilog2, ymp_is_zero, ymp_mul, ymp_mul_digit, ymp_pow, と ymp_sub_digit.
00017 {
00018 /* Newton法による */
00019
00020 size_t i;
00021 size_t ilog2, bit_len;
00022 struct multiprec x, x_prev; /* 近似途中の値を保持 */
00023 struct multiprec tmp; /* 計算上の一時領域 */
00024 struct multiprec self_copy;
00025
00026 if (!self->sign)
00027 { /* 非負ならば良い */
00028 result->sign = positive_sign;
00029 }
00030 else
00031 { /* そうでないとき */
00032 /* 偶数乗根はエラー */
00033 if ( (n & 0x01) == 0) ymp_fatal("負数の偶数乗根");
00034
00035 /* 結果の符号が確定 */
00036 result->sign = self->sign;
00037
00038 /* 以下、-<self> >= 0 の絶対値を算出すれば良い */
00039 memcpy(&self_copy, self, sizeof(struct multiprec));
00040 self_copy.sign = positive_sign;
00041 self = &self_copy;
00042 }
00043
00044 /* bit長により、初期値を大まかに決定 */
00045 /* (初期値) >= (収束値) を保証して収束が遅くなるのを防止する */
00046 /* ilog2 := { 2**k > |<self>|なる最小のk } */
00047 ilog2 = ymp_ilog2(self); ++ilog2;
00048
00049 bit_len = ilog2 / n;
00050 if (bit_len == 0)
00051 {
00052 if (ymp_is_zero(self)) ymp_assign_abs_digit(result, 0);
00053 else ymp_assign_abs_digit(result, 1);
00054 return;
00055 }
00056 if (ilog2 % n) ++bit_len;
00057
00058 ymp_alloca_initialize(&x, self->used * n + 1);
00059 for (i=0; i<bit_len/DIGIT_BIT; ++i) x.digits[i] = 0;
00060 x.digits[i] = 1u << (bit_len%DIGIT_BIT);
00061 x.used = ++i;
00062
00063 /* 今、x は (2**k)**n > |<self>| なる最小の2**k */
00064
00065
00066 /* 作業領域確保 */
00067 ymp_alloca_initialize(&tmp, self->used * n + 1);
00068 ymp_alloca_initialize(&x_prev, self->used * n + 1);
00069
00070
00071 /* f(x) = x**n - |<self>| に対するNetwon法 */
00072 for (;;)
00073 {
00074 l_newton_iteration:
00075 ymp_assign(&x_prev, &x);
00076
00077 /* x_{i+1} = ((n-1) * x_i**n + a) / (n * x_i**(n-1)) */
00078 ymp_pow(&tmp, &x_prev, n-1); /* tmp = x_i ** (n-1) */
00079
00080 ymp_assign(&x, &tmp); /* x = x_i ** n */
00081 ymp_mul(&x, &x, &x_prev);
00082 ymp_mul_digit(&x, &x, n-1); /* x = (n-1) * x_i**n */
00083 ymp_add(&x, &x, self); /* (n-1) * x_i**n + a */
00084
00085 ymp_mul_digit(&tmp, &tmp, n); /* tmp = n*x_i**(n-1) */
00086
00087 ymp_div(&x, &x, &tmp); /* x = x_{i+1} */
00088
00089
00090 if (x.used != x_prev.used) continue; /* まだ誤差が大きい */
00091
00092 for (i=1; i<x.used; ++i)
00093 {
00094 if (x.digits[i] != x_prev.digits[i]) /* まだ誤差が大きい */
00095 goto l_newton_iteration; /* Newton法続行 */
00096 }
00097
00098 /* かなり収束した。現在の誤差の絶対値が1以下なら、あとは微調整 */
00099 switch ((int)(x.digits[0] - x_prev.digits[0]))
00100 {
00101 case 0:
00102 ymp_assign_abs(result, &x);
00103 return;
00104
00105 case 1:
00106 for (;;)
00107 {
00108 ymp_pow(&tmp, &x, n);
00109 if (ymp_compare_abs(&tmp, self) > 0) break;
00110 ymp_add_digit(&x, &x, 1);
00111 }
00112 ymp_sub_digit(&x, &x, 1);
00113 ymp_assign_abs(result, &x);
00114 return;
00115
00116 case -1:
00117 for (;;)
00118 {
00119 ymp_pow(&tmp, &x, n);
00120 if (ymp_compare_abs(&tmp, self) <= 0) break;
00121 ymp_sub_digit(&x, &x, 1);
00122 }
00123 ymp_assign_abs(result, &x);
00124 return;
00125 }
00126 }
00127 }
|
1.2.14 作者 Dimitri van Heesch,
© 1997-2002