メインページ   モジュール   データ構造   ファイル一覧   データフィールド   グローバル  

root.c の解説

#include "multiprec.h"
#include <string.h>

ソースコードを見る。

関数

void ymp_iroot (mp_ref_t result, mp_cref_t self, size_t n)
 多倍長整数の冪乗根の整数部分. より詳しく...


関数の解説

void ymp_iroot mp_ref_t    result,
mp_cref_t    self,
size_t    n
 

多倍長整数の冪乗根の整数部分.

result := floor( <self> ** (1/n) )

引数:
result  結果を格納するstruct multiprecへのポインタ
self  元の数を表すstruct multiprecへのポインタ
n  冪指数
事前条件:
nが偶数ならば、selfは非負であること。 そうでないときはymp_fatalが呼ばれる。
nは0でないこと. そうでないとき、結果は不定

root.c16 行で定義されています。

参照 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 }


YMPに対してTue Mar 16 19:23:51 2004に生成されました。 doxygen1.2.14 作者 Dimitri van Heesch, © 1997-2002