プログラムの書き直し

 投稿者:GAIメール  投稿日:2008年12月16日(火)22時55分17秒
  UBASICによる次のプログラムを十進BASICに書き直してもらいたいのですが、どなたかよろしくお願いいたします。

10 !euler function
20 input "n=";M
30 Mw=M:Phi=1
40 repeat
50   P=prmdiv(Mw)
60   Phi*=P-1:Mw\=P
70   while Mw@P=0:Phi*=P:Mw\=P:wend
80 until Mw=1
90 print Phi
100 end


オイラー関数(1 から n までの自然数のうち n と互いに素なものの個数)
を求める目的です。
 

Re: プログラムの書き直し

 投稿者:SECOND  投稿日:2008年12月17日(水)05時50分56秒
  > No.158[元記事へ]

GAIさんへのお返事です。

! 関数 prmdiv() の本来は、素数で割っていくようです。

! euler function
!------------------
INPUT PROMPT "n=":M
LET Mw=M
LET Phi=1
DO
   LET P=prmdiv(Mw)
   LET Phi=Phi*(P-1)
   LET Mw=INT(Mw/P)
   DO WHILE MOD(Mw,P)=0
      LET Phi=Phi*P
      LET Mw=INT(Mw/P)
   LOOP
LOOP UNTIL Mw=1
PRINT Phi

FUNCTION prmdiv(Mw) !1<の最小の約数
   FOR i=2 TO Mw
      IF MOD(Mw,i)=0 THEN EXIT FOR
   NEXT i
   LET prmdiv=i
END FUNCTION

END

<プログラムの照合用に> 原文を uBASIC で、実行した結果。
run
n=2~400
   1   2   2   4   2   6   4   6   4  10   4  12   6   8   8  16   6  18   8  12
  10  22   8  20  12  18  12  28   8  30  16  20  16  24  12  36  18  24  16  40
  12  42  20  24  22  46  16  42  20  32  24  52  18  40  24  36  28  58  16  60
  30  36  32  48  20  66  32  44  24  70  24  72  36  40  36  60  24  78  32  54
  40  82  24  64  42  56  40  88  24  72  44  60  46  72  32  96  42  60  40 100
  32 102  48  48  52 106  36 108  40  72  48 112  36  88  56  72  58  96  32 110
  60  80  60 100  36 126  64  84  48 130  40 108  66  72  64 136  44 138  48  92
  70 120  48 112  72  84  72 148  40 150  72  96  60 120  48 156  78 104  64 132
  54 162  80  80  82 166  48 156  64 108  84 172  56 120  80 116  88 178  48 180
  72 120  88 144  60 160  92 108  72 190  64 192  96  96  84 196  60 198  80 132
100 168  64 160 102 132  96 180  48 210 104 140 106 168  72 180 108 144  80 192
  72 222  96 120 112 226  72 228  88 120 112 232  72 184 116 156  96 238  64 240
110 162 120 168  80 216 120 164 100 250  72 220 126 128 128 256  84 216  96 168
130 262  80 208 108 176 132 268  72 270 128 144 136 200  88 276 138 180  96 280
  92 282 140 144 120 240  96 272 112 192 144 292  84 232 144 180 148 264  80 252
150 200 144 240  96 306 120 204 120 310  96 312 156 144 156 316 104 280 128 212
132 288 108 240 162 216 160 276  80 330 164 216 166 264  96 336 156 224 128 300
108 294 168 176 172 346 112 348 120 216 160 352 116 280 176 192 178 358  96 342
180 220 144 288 120 366 176 240 144 312 120 372 160 200 184 336 108 378 144 252
190 382 128 240 192 252 192 388  96 352 168 260 196 312 120 396 198 216 160
OK
 

Re: プログラムの書き直し

 投稿者:荒田浩二  投稿日:2008年12月17日(水)10時56分47秒
  > No.159[元記事へ]

SECONDさんへのお返事です。


> FUNCTION prmdiv(Mw) !1<の最小の約数
>    FOR i=2 TO Mw
>       IF MOD(Mw,i)=0 THEN EXIT FOR
>    NEXT i
>    LET prmdiv=i
> END FUNCTION



関数定義を改良しました。
引数が大きく、最小の約数も大きいとき効果があります。

FUNCTION prmdiv(Mw) !1<の最小の約数
   IF MOD(Mw,2)=0 THEN
      LET prmdiv=2
      EXIT FUNCTION
   ELSEIF MOD(Mw,3)=0 THEN
      LET prmdiv=3
      EXIT FUNCTION
   END IF
   FOR i=5 TO SQR(Mw) STEP 6
      IF MOD(Mw,i)=0 THEN
         LET prmdiv=i
         EXIT FUNCTION
      ELSEIF MOD(Mw,i+2)=0 THEN
         LET prmdiv=i+2
         EXIT FUNCTION
      END IF
   NEXT i
   LET prmdiv=Mw
END FUNCTION
 

Re: プログラムの書き直し

 投稿者:山中和義  投稿日:2008年12月18日(木)15時43分28秒
  > No.158[元記事へ]

UBASICの整数論関連の組込み関数を移植しました。

!RSA公開鍵暗号の計算

PRINT modpow(1371,1241,2279) !1371を暗号化する。公開鍵43*53=2279と1241

PRINT eul(2279) !=2184、43と53と2184は秘密
PRINT gcd(43,53) !互いに素
PRINT gcd(2184,1241) !互いに素

PRINT modinv(1241,2184) !秘密鍵1649

PRINT modpow(2003,1649,2279) !2003を複合化する


!nの1より大きな最小の約数
PRINT prmdiv(1234567) !127
PRINT prmdiv(23456789) !23456789
!PRINT prmdiv(11111111111111111) !2071723


!素数の生成
PRINT prm(100) !541
PRINT nxtprm(999) !prm(1000)=7919
!PRINT nxtprm(9999) !prm(10000)=104729


!オイラー関数(1からnまでの自然数のうちnと互いに素なものの個数)
FOR N=2 TO 100
   LET S=0
   FOR i=1 TO N-1
      IF GCD(N,i)=1 THEN LET S=S+1
   NEXT i
   PRINT N;S, eul(N)
NEXT N


FOR i=2 TO 100
   PRINT USING "#### #### #### ####": i,fnSigma(i),eul(i),moeb(i)
NEXT i


END



!整数論関連 ※ubasicより

EXTERNAL FUNCTION prmdiv(n) !1より大きな最小の約数
IF MOD(n,2)=0 THEN !2の倍数
   LET prmdiv=2
ELSEIF MOD(n,3)=0 THEN !3の倍数
   LET prmdiv=3
ELSE
   FOR i=5 TO SQR(n) STEP 6
   !!!FOR i=5 TO INTSQR(n) STEP 6 !<----- ※有理数モード
      IF MOD(n,i)=0 THEN !5,11,17,23,29,…
         LET prmdiv=i
         EXIT FUNCTION
      ELSEIF MOD(n,i+2)=0 THEN !7,13,19,25,31,…
         LET prmdiv=i+2
         EXIT FUNCTION
      END IF
   NEXT i
   LET prmdiv=n !その数自身
END IF
END FUNCTION

EXTERNAL FUNCTION eul(n) !オイラー関数 φ(n)(1からnまでの自然数のうちnと互いに素なものの個数)
LET t=n
IF MOD(n,2)=0 THEN
   LET t=t/2
   DO
      LET n=n/2
   LOOP WHILE MOD(n,2)=0
END IF
LET d=3
DO WHILE n/d>=d
   IF MOD(n,d)=0 THEN
      LET t=t/d*(d-1)
      DO
         LET n=n/d
      LOOP WHILE MOD(n,d)=0
   END IF
   LET d=d+2
LOOP
IF n>1 THEN LET t=t/n*(n-1)
LET eul=t
END FUNCTION

EXTERNAL FUNCTION moeb(n) !メビウス関数 μ(n)
LET W=1
DO WHILE n>1
   LET P=prmdiv(n)
   IF MOD(n,P^2)=0 THEN
      LET W=0
      EXIT DO
   END IF
   LET W=-W
   LET n=INT(n/P)
LOOP
LET moeb=W
END FUNCTION

EXTERNAL FUNCTION modpow(a,b,n) !a^b≡x mod n のxを返す
IF b=0 THEN
   LET modpow=1
ELSE
   LET S=1
   DO WHILE b>0
      IF MOD(b,2)=1 THEN LET S=MOD(S*a,n) !ビットが1なら計算する
      LET b=INT(b/2) !べき乗bを2進展開する
      LET a=MOD(a*a,n)
   LOOP
END IF
LET modpow=S
END FUNCTION

EXTERNAL FUNCTION modinv(a,n) !nを法としたaの逆元 a*x (mod n)=1
LET M=n
LET Sa=1
LET Ta=0
DO WHILE M<>0
   LET Q=INT(a/M)
   LET U=a-Q*M
   LET Ua=Sa-Q*Ta
   LET a=M
   LET Sa=Ta
   LET M=U
   LET Ta=Ua
LOOP
IF a<>1 THEN
   LET modinv=0
ELSE
   IF Sa<0 THEN LET Sa=Sa+n
   LET modinv=Sa
END IF
END FUNCTION

EXTERNAL FUNCTION prm(n) !n番目の素数 ※nは1以上
DIM prime(n) !素数列
LET prime(1)=2 !1番目は2
LET k=2 !k番目
LET x=1 !検証する自然数
DO WHILE k<=n !N番目まで
   LET x=x+2 !奇数が対象
   LET j=1 !見つかった素数の倍数かどうか確認する
   DO WHILE j<k AND MOD(x,prime(j))<>0 !倍数なら途中で終了
      LET j=j+1
   LOOP
   IF j=k THEN !新しく見つかった素数を記録する
      LET prime(k)=x
      LET k=k+1
   END IF
LOOP
LET prm=prime(n)
END FUNCTION

EXTERNAL FUNCTION nxtprm(n) !n+1番目の素数
LET nxtprm=prm(n+1)
END FUNCTION

EXTERNAL FUNCTION gcd(a,b) !最大公約数
DO UNTIL b=0
   LET t=b
   LET b=MOD(a,b)
   LET a=t
LOOP
LET gcd=a
END FUNCTION

EXTERNAL FUNCTION lcm(a,b) !最小公倍数
LET lcm=a*b/gcd(a,b)
END FUNCTION


!ユーザー定義

EXTERNAL FUNCTION fnSigma(n) !約数の和 σ(n)
LET S=1
DO WHILE n>1
   LET W=1
   LET P=prmdiv(n)
   DO
      LET W=W*P+1
      LET n=INT(n/P)
   LOOP WHILE MOD(n,P)=0
   LET S=S*W
LOOP
LET fnSigma=S
END FUNCTION
 

Re: プログラムの書き直し

 投稿者:GAIメール  投稿日:2008年12月18日(木)19時29分52秒
  > No.171[元記事へ]

山中和義さんへのお返事です。

> UBASICの整数論関連の組込み関数を移植しました。

本(UBASICによるコンピュータ整数論:木田祐司・牧野潔夫著<日本評論社>)
で勉強していてる最中に、この中で使われている関数が十進basicでも使えたらなと思っている所に、山中さんから願ってもない移植のプログラムを提供して頂いたところでした。

整数論はやればやるだけ奥深さが感じられ、ガウスやオイラーなど名だたる天才が最も惹きつけられた魅力が潜んでいることがおぼろげながら窺い知れます。
当時、コンピュータという道具無しに直感(霊感?)を働かせて背後に潜む神秘さに心を奪われていった先人たちの感動を現代の魔法のマシーンを利用して凡人でも再経験して行きたいと思っています。
なにせ独学でやっていますので誤解や回り道をしているかもしれません。

整数論を誰にでも理解していけるようにコンピュータプログラムを組んでいく事はとても大切な分野ではないかと思う次第です。
この分野の書籍があまり無いように感じます。(私の不勉強かもしれませんが・・・)
整数論で使われるいろいろな関数をEXTERNAL FUNCTION として道具箱にいれておけば、いろいろな現象を再現できる可能性が出てきます。
こんな道具があったら便利だろうなと感じましたらお頼みしますのでよろしくお願いします。
また、この分野で驚くべき式や結論をご存知でしたらぜひともお教えください。
 

Re: プログラムの書き直し

 投稿者:荒田浩二  投稿日:2008年12月19日(金)12時24分41秒
  > No.171[元記事へ]

山中和義さんへのお返事です。


> !素数の生成
> PRINT prm(100) !541
> PRINT nxtprm(999) !prm(1000)=7919
> !PRINT nxtprm(9999) !prm(10000)=104729
>
> EXTERNAL FUNCTION prm(n) !n番目の素数 ※nは1以上
> DIM prime(n) !素数列
> LET prime(1)=2 !1番目は2
> LET k=2 !k番目
> LET x=1 !検証する自然数
> DO WHILE k<=n !N番目まで
>    LET x=x+2 !奇数が対象
>    LET j=1 !見つかった素数の倍数かどうか確認する
>    DO WHILE j<k AND MOD(x,prime(j))<>0 !倍数なら途中で終了
>       LET j=j+1
>    LOOP
>    IF j=k THEN !新しく見つかった素数を記録する
>       LET prime(k)=x
>       LET k=k+1
>    END IF
> LOOP
> LET prm=prime(n)
> END FUNCTION
>
> EXTERNAL FUNCTION nxtprm(n) !n+1番目の素数
> LET nxtprm=prm(n+1)
> END FUNCTION


n番目の素数を返す関数を改良しました。
prm(10000)ならば5秒、prm(100000)ならば35秒ほどで求まります。
prm(5761455)=99999989(10^8以下最大素数) を求めるには2進モードで約37分です。

EXTERNAL FUNCTION prm(n) ! n番目の素数
DIM prime(n)
FOR i=1 TO MIN(n,10)
   READ prime(i)
NEXT i
DATA 2,3,5,7,11,13,17,19,23,29
FOR i=11 TO n
   LET m30=MOD(prime(i-1),30)
   IF m30=1 OR m30=23 THEN LET a=prime(i-1)+6 ELSE LET a=prime(i-1)-2*MOD(m30,3)+6
   DO
      LET sqra=SQR(a)
      FOR j=4 TO i-1
         IF MOD(a,prime(j))=0 THEN EXIT FOR
         IF prime(j)>=sqra THEN
            LET prime(i)=a
            EXIT DO
         END IF
      NEXT j
      LET m30=MOD(a,30)
      IF m30=1 OR m30=23 THEN LET a=a+6 ELSE LET a=a-2*MOD(m30,3)+6
   LOOP
NEXT i
LET prm=prime(n)
END FUNCTION
 

Re: プログラムの書き直し

 投稿者:山中和義  投稿日:2008年12月19日(金)16時34分17秒
  > No.173[元記事へ]

荒田浩二さんへのお返事です。

>  n番目の素数を返す関数を改良しました。

助かります。素数を素早く扱うかがこの手のプログラムには必要ですね。

nxtprm(x)関数は、間違っていましたので差し替えておきます。
EXTERNAL FUNCTION nxtprm(x) !xより大きい素数の最小のもの
   DIM prime(x)

   FOR i=1 TO 10 !最初の10個
      READ prime(i)
      IF x<prime(i) THEN
         LET nxtprm=prime(i)
         EXIT FUNCTION
      END IF
   NEXT i
   DATA 2,3,5,7,11,13,17,19,23,29

   FOR i=11 TO INT(x) !11個目以降
      LET m30=MOD(prime(i-1),30)
      IF m30=1 OR m30=23 THEN LET a=prime(i-1)+6 ELSE LET a=prime(i-1)-2*MOD(m30,3)+6
      DO
         LET sqra=SQR(a)
         !!!LET sqra=INTSQR(a) !<----- ※有理数モード
         FOR j=4 TO i-1
            IF MOD(a,prime(j))=0 THEN EXIT FOR
            IF prime(j)>=sqra THEN
               LET prime(i)=a
               EXIT DO
            END IF
         NEXT j
         LET m30=MOD(a,30)
         IF m30=1 OR m30=23 THEN LET a=a+6 ELSE LET a=a-2*MOD(m30,3)+6
      LOOP

      IF x<prime(i) THEN
         LET nxtprm=prime(i)
         EXIT FUNCTION
      END IF
   NEXT i

   PRINT "見つかりません。"
   STOP
END FUNCTION



●prm(n)関数を使ったものを追加しておきます。(定義済みのサブルーチンは省略)
!素数の個数
!PRINT fnPrimePi(77777)


!原始根 genshi3
FOR LP=1 TO 100
   LET P=prm(LP) !最初の100個の素数に対して

   PRINT USING "##### ### |": P,fnGenshi(P);
   IF MOD(LP,5)=0 THEN PRINT
NEXT LP
PRINT


!原始根 genshi2
FOR LP=1 TO 100
   LET P=prm(LP) !最初の100個の素数に対して

   LET G=1
   IF P<>2 THEN
50       LET G=G+1
         LET W=1
         FOR i=1 TO P-2
            LET W=MOD(W*G,P)
            IF W=1 THEN GOTO 50
         NEXT i
      END IF

      PRINT USING "##### ### |": P,G;
      IF MOD(LP,5)=0 THEN PRINT
   NEXT LP
   PRINT


END


EXTERNAL FUNCTION fnPrimePi(X) !実数xに対しx以下の素数の個数
   LET S=1
   LET E=10000 !100,000以下の素数は10,000個未満だから ※
   DO WHILE E>S+1
      LET K=INT((S+E)/2) !2分探索
      IF prm(K)<=X THEN LET S=K ELSE LET E=K
   LOOP
   LET fnPrimePi=S
END FUNCTION

EXTERNAL FUNCTION fnGenshi(P) !原始根
   LET G=1
   LET N=P-1
   IF P<>2 THEN
180    LET G=G+1
       LET Nw=N
       DO
          LET Div=prmdiv(Nw)
          DO WHILE MOD(Nw,Div)=0
             LET Nw=INT(Nw/Div)
          LOOP
          IF modpow(G,INT(N/Div),P)=1 THEN GOTO 180
       LOOP UNTIL Nw=1
    END IF
    LET fnGenshi=G
 END FUNCTION
 

Re: プログラムの書き直し

 投稿者:SECOND  投稿日:2008年12月25日(木)20時28分59秒
  > No.171[元記事へ]

山中和義さんへのお返事です。

b=0 のときの値が変です。EXTERNAL FUNCTION modpow(a,b,n) !a^b≡x mod n のxを返す
 

Re: プログラムの書き直し

 投稿者:山中和義  投稿日:2008年12月25日(木)21時19分17秒
  > No.200[元記事へ]

SECONDさんへのお返事です。

> b=0 のときの値が変です。EXTERNAL FUNCTION modpow(a,b,n) !a^b≡x mod n のxを返す

b=0での場合分けは必要なしですね。(原形ではmodpow=1ではなくて、S=1でした。)


EXTERNAL FUNCTION modpow(a,b,n) !a^b≡x mod n のxを返す
   LET S=1
   DO WHILE b>0
      IF MOD(b,2)=1 THEN LET S=MOD(S*a,n) !ビットが1なら計算する
      LET b=INT(b/2) !べき乗bを2進展開する
      LET a=MOD(a*a,n)
   LOOP
   LET modpow=S
END FUNCTION
 

戻る