#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 } |