Spigot algorithms

 投稿者:M.N  投稿日:2010年 2月12日(金)10時29分16秒
  (Spigot algorithms こつこつ方法)
こつこつ方法で定数を数値展開すると1桁ずつ生成できます。
繰り上がりを考える必要がないので、eに対してはこれが
特に簡単です。.....中略......次のアルゴリズムは、eの
数値展開を作り出します。

長さn+1の配列Aを1に初期化します。そして、次をn-1回
繰り返します。

(a) Aの各要素に10を掛けます。
(b) 右から始めて、Aの第i要素をi+1を法として導き、
割り算の商を左隣の桁へ持って行きます。作り出された
最後の商がeの次の桁の数になります。

このアルゴリズムは高速に収束する級数e=Σ(n=0 to ∞)(1/n!)
を単純に書き直した次の式に基づいています。

e=1+1/1(1+1/2(1+1/3(1+1/4(1+1/5(1+......)))))
--------------------------------------------------------
以上 数学を生み出す魔法のるつぼ(Jonathan Borwein
Keith Devlin 伊知地 宏 訳  オライリ-ジャパン)より

以上のアルゴリズムでプログラムを作るとどうなるでしょうか。
どなたかわかる方ご教示お願いします。言語はできるだけBASIC
でお願いします。
(数学愛好者/質問者)
 

Re: Spigot algorithms

 投稿者:山中和義  投稿日:2010年 2月12日(金)11時50分14秒
  > No.1025[元記事へ]

M.Nさんへのお返事です。

> 長さn+1の配列Aを1に初期化します。そして、次をn-1回
> 繰り返します。
>
> (a) Aの各要素に10を掛けます。
> (b) 右から始めて、Aの第i要素をi+1を法として導き、
> 割り算の商を左隣の桁へ持って行きます。作り出された
> 最後の商がeの次の桁の数になります。

そのままコード化すると、、、
LET N=20
DIM A(0 TO N) !長さn+1の配列Aを1に初期化します。
MAT A=CON
FOR k=1 TO N-1 !そして、次をn-1回繰り返します。
!!!MAT PRINT A; !trace
   MAT A=10*A !(a) Aの各要素に10を掛けます。
   FOR i=N TO 1 STEP -1 !(b) 右から始めて、
      LET Q=INT(A(i)/(i+1)) !Aの第i要素をi+1を法として導き、
      LET A(i)=A(i)-Q*(i+1)
      LET A(i-1)=A(i-1)+Q !割り算の商を左隣の桁へ持って行きます。
   NEXT i
   PRINT Q !作り出された最後の商がeの次の桁の数になります。
   LET A(0)=0 !※累積されるので、オーバーフローとなる。これを防ぐ!
NEXT k
END
 

Spigot algorithms

 投稿者:M.N  投稿日:2010年 2月12日(金)11時56分1秒
  すばやい対応に感謝します。ありがとうございました。  

Spigot algorithms

 投稿者:M.N  投稿日:2010年 2月12日(金)14時48分36秒
  M.Nです。たびたびお邪魔して申し訳ございません。Spigot algorithms を用いて
SIN(20)の値などをたとえば10000桁求めることなどできるのでしょうか。
できるならプログラムをどう書き換えたらよろしいでしょうか。
 

Re: Spigot algorithms

 投稿者:山中和義  投稿日:2010年 2月12日(金)18時55分35秒
  > No.1028[元記事へ]

M.Nさんへのお返事です。

> Spigot algorithms を用いて
> SIN(20)の値などをたとえば10000桁求めることなどできるのでしょうか。

順に求まる下位桁からの「桁上り」(1桁にならない場合のこと)を考慮すればできると思います。

以下は、その心配がない範囲の改修です。
!テイラー展開より
!   e^x=1+x/1!+x^2/2!+x^3/3!+x^4/4!+x^5/5!+ …
! sin(x)=  x/1!       -x^3/3!       +x^5/5!  …
!
!xのべき乗の係数A(i)を比較すると、
!   e^xは、1,1,1, 1,1,1,…
! sin(x)は、0,1,0,-1,0,1,…
!
!計算手順
! e^x=((((( … +1)*x/5+1)*x/4+1)*x/3+1)*x/2+1)*x/1+1

!x=1、すなわちsin(1)なら
LET x=1

LET N=15
DIM A(0 TO N) !長さn+1の配列Aを0,1,-1に初期化します。
FOR i=0 TO N
   IF MOD(i,2)=1 THEN LET A(i)=(-1)^INT(i/2)
NEXT i
FOR k=1 TO N-1 !そして、次をn-1回繰り返します。
!!!MAT PRINT A; !trace
   MAT A=10*A !(a) Aの各要素に10を掛けます。
   FOR i=N TO 1 STEP -1 !(b) 右から始めて、
      LET Q=INT(A(i)*x/i) !Aの第i要素をiを法として導き、
      LET A(i)=A(i)-Q*i/x
      LET A(i-1)=A(i-1)+Q !割り算の商を左隣の桁へ持って行きます。
   NEXT i
   PRINT Q !作り出された最後の商がeの次の桁の数になります。
   LET A(0)=0 !※累積されるので、オーバーフローとなる。これを防ぐ!
NEXT k


PRINT SIN(x) !検算

END
 

Spigot algorithms

 投稿者:M.N  投稿日:2010年 2月13日(土)10時46分16秒
  再三再四のお返事ありがとうございました。大変勉強になりました。
お忙しい中、まことに恐縮ですがもうひとつだけお頼みしたいことがあります。Π(PAI)の値を求める時はどうすればよろしいのでしょうか。自分でやってみたのですが、うまくできませんでした。
PAI=2+1/3(2+2/5(2+3/7(2+....(2+k/(2k+1)+...))))
 

Re: Spigot algorithms

 投稿者:山中和義  投稿日:2010年 2月13日(土)11時55分44秒
  > No.1030[元記事へ]

M.Nさんへのお返事です。

> PAI=2+1/3(2+2/5(2+3/7(2+....(2+k/(2k+1)+...))))

計算手順
 π=((((( … +2)*5/11+2)*4/9+2)*3/7+2)*2/5+2)*1/3+2

  e=((((( … +1)/5+1)/4+1)/3+1)/2+1)/1+1

ここでeの場合、プログラム(前出)の意味は

式の「+1」の部分
 MAT A=CON !長さn+1の配列Aを1に初期化します。

式の「/5,/4,…,/2,/1」の部分
 LET Q=INT(A(i)/i) !Aの第i要素をiを法として導き
 LET A(i)=A(i)-Q*i

ですので、πの方に比較対応させると良いでしょう。

実験数学ですから、プログラムで試行錯誤すればよいかと思います。


途中の計算は割り切れない可能性があるので、有理数(分数)で計算する必要があります。
 LET A(i)=A(i)-Q* (2*i+1)/i の部分

漸化式の収束の関係で、精度は先頭から3分の1程度です。

また、2桁で表示されるときは、その前の数値に桁上げする必要があります。(前記載の問題点より)
!漸化式
! a[0]=2
! a[n]=n/(2*n+1)*a[n-1]、n≧1
!とすると
! π=Σ{n=0,∞}a[n]

!計算手順
! π=((((( … +2)*5/11+2)*4/9+2)*3/7+2)*2/5+2)*1/3+2

LET N=20
DIM A(0 TO N) !長さn+1の配列Aを2に初期化します。
MAT A=2*CON
FOR k=1 TO N-1 !そして、次をn-1回繰り返します。
!!!MAT PRINT A; !trace
   MAT A=10*A !(a) Aの各要素に10を掛けます。
   FOR i=N TO 1 STEP -1 !(b) 右から始めて、
      LET Q=INT(A(i)* i/(2*i+1) ) !Aの第i要素を(2*i+1)/iを法として導き、
      LET A(i)=A(i)-Q* (2*i+1)/i
      LET A(i-1)=A(i-1)+Q !割り算の商を左隣の桁へ持って行きます。
   NEXT i
   PRINT Q !作り出された最後の商がπの次の桁の数になります。
   LET A(0)=0 !※累積されるので、オーバーフローとなる。これを防ぐ!
NEXT k
END
 

Spigot algorithms

 投稿者:M.N  投稿日:2010年 2月13日(土)12時15分4秒
  度重なる回答に感謝いたします。たいへんお手数をおかけしました。
おかげさまでeもPAIもできました。
ありがとうございました。
 

Re: Spigot algorithms

 投稿者:劉徹  投稿日:2012年 1月22日(日)18時37分58秒
  > No.1025[元記事へ]

こんな感じでどうでしょう?
一応動作確認済み。
OPTION BASE 0
INPUT k
DIM a(k)
LET z=10 !基数
LET d=0
LET i=k-1
LET j=0
LET temp=0
LET a(k-1)=z
DO WHILE i>0
   LET temp=INT(a(i)/i)
   LET a(i-1)=z+temp
   LET a(i)=a(i)-temp*i
   LET i=i-1
LOOP
LET temp=INT(a(0)/z)
PRINT USING "#.":temp;
LET d=a(0)-temp*z
FOR j=0 TO k-1 !収束具合で調整
   LET a(0)=0
   LET a(k-1)=a(k-1)*z
   LET i=k-1
   DO WHILE i>0
      LET temp=INT(a(i)/i)
      LET a(i-1)=a(i-1)*z+temp
      LET a(i)=a(i)-temp*i
      LET i=i-1
   LOOP
   LET temp=INT(a(0)/z)
   LET d=d+temp
   PRINT USING "%":d;
   LET d=a(0)-temp*z
NEXT j
END

ちなみにC版(元)も
#include <stdio.h>
#define k 1000000
#define z 10
int a[k];
int main(){
int d,i,j;
a[k-1]=z;
for(i=k-1;i>0;i--){
a[i-1]=z+a[i]/i;
a[i]%=i;
}
printf("%d.",a[0]/z);
d=a[0]%z;
for(j=0;j<k/4;j++){
a[0]=0;
a[k-1]*=z;
for(i=k-1;i>0;i--){
a[i-1]=a[i-1]*z+a[i]/i;
a[i]%=i;
}
d+=a[0]/z;
printf("%d",d);
d=a[0]%z;
}
}
 

戻る