22/7
今日は12月24日です。
約分して1/2にできる日でもありますが…
22/7 #01 さよなら、私のささやかな世界 において、
境目として「2016年12月24日」という日付が提示されています。(1:51)
今日は、この日付からちょうど5年です!
作中の日付が西暦とは限らないので、この日付から本当にちょうど5年だとは限らないことに注意
これを記念して、IchigoJamで22/7を計算してみました。
小数点以下の計算は、筆算のように前の桁の余りを10倍して割る数で割る操作を繰り返します。
10 ' ワリザン
20 INPUT"ブンシ =",N
30 INPUT"ブンボ=",D
40 ?N;"/";D;"=";N/D;
50 N=ABS(N):D=ABS(D):R=N%D
60 IF R=0 ?CHR$(10);:END
70 ?".";
80 WAIT5
90 N=R*10:?N/D;:R=N%D
100 IF R>0 GOTO 80
110 ?CHR$(10);
円周率に近い分数を探す
計算してみると、22/7=3.142857142857… となり、円周率に近い値になります。
そこで、IchigoJamで扱いやすい(分母と分子が変数1個で表せる)中で、より円周率に近い分数を探してみます。
分母を総当りし、値が円周率に近くなるように分子を選びます。
今回は、分母に円周率の65536倍を整数に切り捨てた値 205887 (0x3243f) を掛け、
65536で割った (16ビット右シフトした) 値から分子の探索を開始します。
分子を1ずつ大きくしていき、円周率以上になったところでその分母に関する探索を終了します。
事前の計算により、この条件において、IchigoJamで扱いやすい分母は最大10430であることがわかりました。
実装
10 ' エンシュウリツ ニ チカイ ブンスウ
20 POKE#700,183,47,8,224,147,5,144,12,170,5,147,133,245,3,51,5,181,2,65,129,130,128,201,33,137,2,63,49,72,67,0,12,112,71
30 LET[8],0,0,0,0,9999,9999,9999,9999,1415,9265,3589,7932:M=0:C=0:GOTO110
40 A=0:FOR P=0 TO 3
50 IF A=0 AND [X+P]>[Y+P] A=1
60 IF A=0 AND [X+P]<[Y+P] A=-1
70 NEXT:RETURN
80 A=0:FOR P=3 TO 0 STEP -1
90 [Z+P]=[X+P]-[Y+P]-A:IF [Z+P]<0 [Z+P]=[Z+P]+10000:A=1 ELSE A=0
100 NEXT:RETURN
110 FOR D=2 TO 10430
120 N=USR(#700,D)
130 R=N%D:LET[0],0,0,0,0:FOR I=0 TO 15
140 V=0:S=R*2:R=0:FORJ=1TO5:R=R+S:V=V+R/D:R=R%D:NEXT
150 [I/4]=[I/4]*10+V
160 NEXT
170 X=0:Y=16:GOSUB40:B=A:IF A<0 X=16:Y=0 ELSE X=0:Y=16
180 Z=4:GOSUB80:X=4:Y=12:GOSUB40
190 IF A<0 M=N:C=D:LET[8],[0],[1],[2],[3],[4],[5],[6],[7]
200 IF B<0 N=N+1:GOTO130
210 IF D%100=0 ?".";
220 IF D%1000=0 ?D
230 NEXT
240 IF D%1000<>0 ?D
250 ?M;"/";C;"=";M/C;".";
260 FOR I=8 TO 11
270 IF [I]<1000 ?"0";
280 IF [I]<100 ?"0";
290 IF [I]<10 ?"0";
300 ?[I];
310 NEXT
320 ?CHR$(10);
解説
10行目はタイトルです。
20行目は、分子の初期値を求めるためのマシン語です。
アセンブリコード
IF M0 GOTO @CODE4M0
MODE RV32C
R11 = R0 + #C9
R11 <<= 10
R11 = R11 + #3F
R10 = R10 * R11
R10 >>= 16
RET
MODE M0
@CODE4M0
R1 = #C9
R1= R1 << 10
R1 += #3F
R0 *= R1
R0 = R0 >> 16
RET
30行目で変数を初期化し、探索プログラムに飛んでいます。
今回は、配列を以下のように割り当てました。
- 0~3:現在チェックしている数の小数点以下
- 4~7:現在チェックしている数と円周率の差の絶対値の小数点以下
- 8~11:見つけた一番円周率に近い数の小数点以下
- 12~15:見つけた一番円周率に近い数と円周率の差の絶対値の小数点以下
- 16~19:円周率の値
また、見つけた一番円周率に近い数をM/C
、現在チェックしている数をN/D
とします。
40行目~70行目は、配列のX
から始まる数とY
から始まる数を比較し、その結果をA
に格納します。
80行目~100行目は、配列のX
から始まる数からY
から始まる数を引き、その結果をZ
から格納します。
今回扱う数は配列の4要素で、1要素に10進数の4桁を格納します。
110行目~230行目で探索を行います。
120行目で、マシン語を用いて分子の初期値を求めます。
130行目~160行目で、現在チェックしている数の小数点以下を求めます。
10430付近の数にいきなり10を掛けてしまうとオーバーフローすることがあるが、
2を掛けてもオーバーフローしないので、2個ずつ足して余りを求めています。
170行目で、現在チェックしている数と円周率を比較し、
大きい方(小さくない方)から小さい方(大きくない方)を引くようにします。
180行目で引き算を行い、結果を現在見つかっている一番円周率に近い数の円周率との差の絶対値と比較します。
190行目で、もし現在チェックしている数の方が円周率に近ければ、記録を更新します。
200行目で、もし現在チェックしている数が円周率未満なら、次の分子の探索に行きます。
210行目、220行目、240行目は、探索の進行状況を出力します。
250行目~320行目は、求めた円周率に一番近い数を出力します。
実行結果
IchigoKamuy (IchigoJam BASIC 1.4.1) で実行しても時間がかかりすぎそうだったので、
IchigoJam R (IchigoJam BASIC 1.5b) で実行しました。
その結果、約25分で実行が完了しました。
この求まった「355/113」という値は、「密率」というらしいです。
あっ
この円周率に近い分数を求めるプログラムを作っている時に気付いたのですが、
最初に載せた割り算のプログラムは、分母が大きいと10を掛けた時にオーバーフローしてしまう可能性があります。
そこで、オーバーフロー対策のため、10を掛けるのではなく10個足すようにしてみました。
10 ' ワリザン 2
20 INPUT"ブンシ =",N
30 INPUT"ブンボ=",D
40 ?N;"/";D;"=";N/D;
50 N=ABS(N):D=ABS(D):R=N%D
60 IF R=0 ?CHR$(10);:END
70 ?".";
80 WAIT5
90 V=0:S=R:FORI=1TO9:R=R+S
100 IF R<0 OR R>=D R=R-D:V=V+1
110 NEXT:?V;
120 IF R>0 GOTO 80
130 ?CHR$(10);
分母が大きくても、ちゃんと結果が求まっているようです。
おわりに
IchigoJamのLOAD
やSAVE
で扱える数の最大値は227
…22/7感ありますね!
- IchigoJamはjig.jpの登録商標です。
- IchigoKamuyは株式会社syushuの登録商標です。