四則演算ゲーム(計算パズル)

 投稿者:山中和義  投稿日:2011年10月16日(日)10時31分53秒
  四則演算ゲーム
 4つの1桁の数に対して、四則(+,-,×,÷)、括弧、べき乗、平方根を使って、
 ある数Nを求める式をつくる。

 1,3,4,6から24を表す計算式は、6÷(1-3÷4)=24 ※難問


●四則演算、括弧、べき乗による場合
通常の式の表現(中置表現)では、括弧の組合せを考えるのは容易ではありません。
この場合は数が4つなので何とかなると思います。
そこで、一部の電卓やプログラム言語の数式処理で採用されている後置表記(逆ポーランド表記)を使ってみます。

 後置表記(逆ポーランド表記)による数式
 例
  (1+2)*3-4 → 12+3*4-
 利点
  括弧を使わずにすべての式を表現できる。
  数式を計算するプログラムが容易に作れる。

場合の数
 4つの数の並びは、4!通り
 式「12A3B4C」のABCの位置に入る演算子(四則、べき乗)の並びは、5^3通り
 式「12A3B4C」のABCの位置に入る演算子の個数 0≦A≦1, 0≦B≦2-A, C=3-(A+B)

これらに注意して、コーディングしてみます。


1000 !四則演算ゲーム
1010 DATA 1,3,4,6 !4つの数
1020 DIM X(4)
1030 MAT READ X
1040
1050
1060 FOR N=0 TO 100 !求める数
1070    !!PRINT "N=";N
1080
1090
1100    DIM idx(4) !参照位置
1110    LET h=0
1120    DO WHILE h<FACT(4) !数字の並びは、4!通り
1130       CALL Num2PermFactorial(h, idx,4)
1140       !!!MAT PRINT idx; !debug
1150
1160
1170       LET OP$="+-*/^" !演算子
1180       LET L=LEN(OP$)
1190
1200       FOR i=0 TO L^3-1 !L進法による「演算子の並び」のパターンを生成する
1210          !!!PRINT i !debug
1220
1230          LET P$="" !演算子の並び
1240          LET t=i
1250          FOR k=1 TO 3 !個数は3個 ※進数変換
1260             LET w=MOD(t,L)+1
1270             LET P$=P$&OP$(w:w)
1280             LET t=INT(t/L)
1290          NEXT k
1300          !!!PRINT P$ !debug
1310
1320          FOR A=0 TO 1 !式「12A3B4C」に入る演算子の個数
1330             FOR B=0 TO 2-A
1340                LET C=3-(A+B) !残り
1350                !!!PRINT A;B;C !debug
1360
1370                !パターンから必要な個数だけ切り出して、式をつくる
1380                LET s$=STR$(X(idx(1))) !1番目の数
1390                LET s$=s$&STR$(X(idx(2))) !2番目の数
1400                LET s$=s$&P$(1:A)
1410                LET s$=s$&STR$(X(idx(3))) !3番目の数
1420                LET s$=s$&P$(1+A:A+B)
1430                LET s$=s$&STR$(X(idx(4))) !4番目の数
1440                LET s$=s$&P$(1+A+B:A+B+C)
1450                !!!PRINT s$ !debug
1460
1470                CALL calc(s$,X, v) !式を計算する
1480                IF ABS(v-N)<=1E-12 THEN !該当するなら、解となる
1490                   PRINT s$;"=";v
1500
1510                   EXIT DO !※最初に見つかった1つのみ
1520                END IF
1530
1540             NEXT B
1550          NEXT A
1560
1570       NEXT i
1580
1590       LET h=h+1 !次の並びへ
1600    LOOP
1610
1620 NEXT N
1630
1640 END
1650
1660
1670 EXTERNAL SUB calc(s$,X(), v) !後置表記の式を計算する
1680 DIM STK(4) !スタック
1690 LET SP=0 !スタックポインタ
1700 LET v=-99999999 !エラーの場合の値
1710 FOR i=1 TO LEN(s$) !式を計算する
1720    SELECT CASE s$(i:i)
1730    CASE "+","+" !加算
1740       LET STK(SP-1)=STK(SP-1)+STK(SP)
1750       LET SP=SP-1
1760    CASE "-","-" !減算
1770       LET STK(SP-1)=STK(SP-1)-STK(SP)
1780       LET SP=SP-1
1790    CASE "*","×" !乗算
1800       LET STK(SP-1)=STK(SP-1)*STK(SP)
1810       LET SP=SP-1
1820    CASE "/","÷" !除算
1830       IF STK(SP)=0 THEN EXIT SUB !0による割り算
1840       LET STK(SP-1)=STK(SP-1)/STK(SP)
1850       LET SP=SP-1
1860
1870    CASE "^" !べき乗
1880       IF STK(SP-1)=0 AND STK(SP)<=0 THEN EXIT SUB !0の負数乗、0の0乗
1890       IF STK(SP-1)<0 AND STK(SP)<>INT(STK(SP)) THEN EXIT SUB !負数の非整数乗
1900       WHEN EXCEPTION IN
1910          LET STK(SP-1)=STK(SP-1)^STK(SP)
1920          LET SP=SP-1
1930       USE
1940          EXIT SUB !オーバーフロー、アンダーフロー
1950       END WHEN
1960
1970    CASE "√" !平方根
1980       IF STK(SP)<0 THEN EXIT SUB !負数
1990       LET STK(SP)=SQR(STK(SP))
2000
2010    CASE " "
2020       !nop
2030
2040    CASE IS >="0", IS <="9" !1桁の数値
2050       LET SP=SP+1
2060       LET STK(SP)=VAL(s$(i:i))
2070
2080    CASE ELSE
2090    END SELECT
2100 NEXT i
2110 IF SP<>1 THEN PRINT "error!!!"
2120 LET v=STK(1) !結果
2130 END SUB
2140
2150
2160 EXTERNAL SUB Num2PermFactorial(h, A(),N) !番号から順列パターンを生成する ※辞書式順序
2170 LET v=h !非負の10進数整数を階乗進数へ
2180 FOR j=1 TO N
2190    LET t=INT(v/j)
2200    LET A(N-j+1)=v-t*j +1 !階乗進数の各桁の値+1 A[1..N]=(N-1)! … 3! 2! 1! 0!
2210    LET v=t
2220 NEXT j
2230 FOR j=N-1 TO 1 STEP -1 !順列パターンへ
2240    FOR k=j+1 TO N
2250       IF A(k)>=A(j) THEN LET A(k)=A(k)+1
2260    NEXT k
2270 NEXT j
2280 END SUB


●べき乗を含まない場合

1170       LET OP$="+-*/" !演算子

と変更(べき乗演算子を削除)してください。


●平方根を含む場合
式を計算する部分は既に組み込んでいますので、後は式の生成のみです。
同様に、組合せを考えればよいのですが、組合せの階層が増えてプログラムが複雑になります。
そこで、手動で切り抜ける方法を紹介します。

(√1)+2+3+4型

1380                LET s$=STR$(X(idx(1)))&"√" !1番目の数
1390                LET s$=s$&STR$(X(idx(2))) !2番目の数
1400                LET s$=s$&P$(1:A)
1410                LET s$=s$&STR$(X(idx(3))) !3番目の数
1420                LET s$=s$&P$(1+A:A+B)
1430                LET s$=s$&STR$(X(idx(4))) !4番目の数
1440                LET s$=s$&P$(1+A+B:A+B+C)


√(1+2+3)+4型

1380                LET s$=STR$(X(idx(1))) !1番目の数
1390                LET s$=s$&STR$(X(idx(2))) !2番目の数
1400                LET s$=s$&P$(1:A)
1410                LET s$=s$&STR$(X(idx(3))) !3番目の数
1420                LET s$=s$&P$(1+A:A+B)&"√"
1430                LET s$=s$&STR$(X(idx(4))) !4番目の数
1440                LET s$=s$&P$(1+A+B:A+B+C)

のように平方根を各数の後や演算子の後に組み合わせて追加すればよいと思います。
 

Re: 四則演算ゲーム(計算パズル)

 投稿者:山中和義  投稿日:2011年10月17日(月)11時35分56秒
  > No.1678[元記事へ]

後置表記では、読み難いので通常の式に変換するルーチンを追加しておきます。

6134/-/= 24 → 6/(1-3/4)= 24


変更部分(使用例)

1490                   PRINT s$;"=";v



1490                   CALL rpn2std(s$,t$)
1495                   PRINT t$;"=";v

とする。


追加部分
 前出のプログラムの最後に追加して下さい。


2290
2300
2310 EXTERNAL SUB rpn2std(s$, t$) !後置表記を中置表記で表す
2320 DIM STK$(4),STK2(4) !スタック
2330 LET SP=0 !スタックポインタ
2340 FOR i=1 TO LEN(s$) !式を計算する
2350    LET OP$=s$(i:i)
2360    SELECT CASE OP$
2370    CASE "+","+" !加算
2380       LET STK$(SP-1)=STK$(SP-1) & OP$ & STK$(SP) !「1+2」とする ※(1+2)+3や1+(2+3)は1+2+3とする
2390       LET STK2(SP-1)=1 !優先順位( +,- < *,/ < ^ < √ < 数値 ) ※値が大きい程、先に計算する
2400       LET SP=SP-1
2410    CASE "-","-" !減算
2420       LET L$=STK$(SP-1) !左項
2430       LET R$=STK$(SP) !右項
2440       CALL paren(R$, FLG)
2450       IF STK2(SP)<2 AND FLG=-1 THEN !右項が+,-で、括弧が付いていなければ
2460          LET R$="(" & R$ & ")" !※1-(2±3)の場合
2470       END IF
2480       LET STK$(SP-1)=L$ & OP$ & R$
2490       LET STK2(SP-1)=1
2500       LET SP=SP-1
2510    CASE "*","×", "/","÷" !乗算、除算
2520       LET L$=STK$(SP-1) !左項
2530       LET R$=STK$(SP) !右項
2540       CALL paren(L$, FLG)
2550       IF STK2(SP-1)<2 AND FLG=-1 THEN !左項が+,-で、括弧が付いていなければ
2560          LET L$="(" & L$ & ")"
2570       END IF
2580       CALL paren(R$, FLG)
2590       IF STK2(SP)<=2 AND FLG=-1 THEN !右項
2600          LET R$="(" & R$ & ")" !※1*(2*3)や1*(2/3)や1/(2*3)や1/(2/3)の場合も含む
2610       END IF
2620       LET STK$(SP-1)=L$ & OP$ & R$
2630       LET STK2(SP-1)=2
2640       LET SP=SP-1
2650
2660    CASE "^" !べき乗
2670       LET L$=STK$(SP-1) !左項
2680       LET R$=STK$(SP) !右項
2690       CALL paren(L$, FLG)
2700       IF STK2(SP-1)<=4 AND FLG=-1 THEN !左項が+,-,*,/,^で、括弧が付いていなければ
2710          LET L$="(" & L$ & ")" !※(1^2)^3の場合も含む
2720       END IF
2730       CALL paren(R$, FLG)
2740       IF STK2(SP)<=4 AND FLG=-1 THEN !右項
2750          LET R$="(" & R$ & ")" !※1^(2^3)の場合も含む
2760       END IF
2770       LET STK$(SP-1)=L$ & OP$ & R$
2780       LET STK2(SP-1)=4
2790       LET SP=SP-1
2800
2810    CASE "√" !平方根
2820       LET L$=STK$(SP)
2830       CALL paren(L$, FLG) !括弧が付いていなければ
2840       IF FLG=-1 THEN LET L$="(" & L$ & ")" !引数 ※(1+2)*3や(1+2)*(3+4)の場合も含む
2850       LET STK$(SP)=OP$ & L$
2860       LET STK2(SP)=5
2870
2880    CASE " "
2890       !nop
2900
2910    CASE IS >="0", IS <="9" !1桁の数値
2920       LET SP=SP+1 !push it
2930       LET STK$(SP)=OP$
2940       LET STK2(SP)=9
2950
2960    CASE ELSE
2970       PRINT "未定義の文字です。"; OP$
2980    END SELECT
2990 NEXT i
3000 IF SP<>1 THEN PRINT "error!!!"
3010 LET t$=STK$(1) !結果
3020
3030 SUB paren(s$, FLG) !括弧が付いているかどうか確認する ※(1+2)*3や(1+2)*(3+4)は「なし」
3040    LET FLG=0
3050    IF s$(1:1)<>"(" OR (s$(1:1)="(" AND POS(s$,")")<LEN(s$)) THEN LET FLG=-1 !括弧なし
3060 END SUB
3070 END SUB


 

戻る