集合の計算 山中和義 2007/07/13 13:16:04 (修正2回) └つづき(サブルーチン部分) 山中和義 2007/07/13 13:17:22 (修正3回) ├典型的な問題(高校数学) 山中和義 2007/07/13 13:19:43 (修正4回) └!川渡りパズル(狼とヤギと野菜)をつくる 山中和義 2007/07/13 13:25:26 (修正3回) └つづき 山中和義 2007/07/13 13:26:24 (修正2回) └参考、解答を得るプログラム 山中和義 2007/07/13 20:39:28
集合の計算 山中和義 2007/07/13 13:16:04 (修正2回) ツリーへ
集合の計算 |
返事を書く ノートメニュー |
山中和義 <drdlxujciw> 2007/07/13 13:16:04 ** この記事は2回修正されてます | |
!集合 A ⇒ 配列変数 A$()
!要素 ai ⇒ 添え字iでの値 A$(i)=ai !要素の数 N(A) ⇒ A$(0)=STR$(nA) !例. A={1,2,5}の場合、A$(1)="1",A$(2)="2",A$(3)="5"、A$(0)="3" OPTION BASE 0 LET TRUE=1 !真 LET FALSE=0 !偽 FUNCTION N(A$()) !要素の個数 IF A$(0)="" THEN LET N=0 ELSE LET N=VAL(A$(0)) END FUNCTION SUB empty(A$()) !空集合にする A=φ LET A$(0)=STR$(0) FOR i=1 TO UBOUND(A$) LET A$(i)="" NEXT i END SUB SUB element(x$, A$()) !xをAの要素とする x∈A LET nA=N(A$) FOR i=1 TO nA IF x$=A$(i) THEN EXIT SUB !既に存在するなら、追加しない NEXT i LET nA=nA+1 LET A$(nA)=x$ LET A$(0)=STR$(nA) END SUB SUB copy(A$(), C$()) !コピーする C=A CALL empty(C$) LET nA=N(A$) FOR i=1 TO nA LET C$(i)=A$(i) NEXT i LET C$(0)=STR$(nA) END SUB SUB union(A$(),B$(), C$()) !和 A∪B CALL copy(A$, C$) LET nB=N(B$) FOR j=1 TO nB CALL element(B$(j), C$) NEXT j END SUB SUB intersect(A$(),B$(), C$()) !積 A∩B CALL empty(C$) LET nA=N(A$) LET nB=N(B$) LET nC=N(C$) FOR i=1 TO nA FOR j=1 TO nB IF A$(i)=B$(j) THEN !一致するなら LET nC=nC+1 LET C$(nC)=B$(j) END IF NEXT j NEXT i LET C$(0)=STR$(nC) END SUB SUB except(A$(),B$() ,C$()) !差 A-B CALL empty(C$) LET nA=N(A$) LET nB=N(B$) LET nC=N(C$) FOR i=1 TO nA FOR j=1 TO nB IF A$(i)=B$(j) THEN EXIT FOR NEXT j IF j>nB THEN !一致しないなら LET nC=nC+1 LET C$(nC)=A$(i) END IF NEXT i LET C$(0)=STR$(nC) END SUB SUB cross(A$(),B$(), C$()) !直積 A×B ※2次元 CALL empty(C$) LET nA=N(A$) LET nB=N(B$) FOR i=1 TO nA FOR j=1 TO nB LET C$(i*j)=A$(i)&" "&B$(j) NEXT j NEXT i LET nC=nA*nB LET C$(0)=STR$(nC) END SUB SUB subset(A$(),m,n, C$()) !部分集合 C⊆A ※m<n CALL empty(C$) FOR i=m TO n LET C$(i-m+1)=A$(i) NEXT i LET nC=n-m+1 LET C$(0)=STR$(nC) END SUB |
└つづき(サブルーチン部分) 山中和義 2007/07/13 13:17:22 (修正3回) ツリーへ
Re: 集合の計算 |
返事を書く ノートメニュー |
山中和義 <drdlxujciw> 2007/07/13 13:17:22 ** この記事は3回修正されてます | |
つづき(サブルーチン部分)
FUNCTION isEmpty(A$()) !空集合かどうか確認する A=φ? LET nA=N(A$) IF nA=0 THEN LET isEmpty=TRUE ELSE LET isEmpty=FALSE END FUNCTION FUNCTION isElement(x$,A$()) !要素かどうか確認する x∈A? LET isElement=TRUE LET nA=N(A$) FOR i=1 TO nA IF x$=A$(i) THEN EXIT FUNCTION !要素として存在するなら NEXT i LET isElement=FALSE END FUNCTION FUNCTION isEqual(C$(),A$()) !等しいかどうか確認する C=A? LET isEqual=FALSE IF N(C$)=N(A$) AND isSubset(C$,A$)=TRUE THEN LET isEqual=TRUE END FUNCTION FUNCTION isSubset(C$(), A$()) !部分集合かどうか確認する C⊆A? LET nC=N(C$) IF nC=0 THEN !空集合なら ELSE LET isSubset=FALSE FOR j=1 TO nC !すべてのx∈Cで、 IF isElement(C$(j),A$)=FALSE THEN EXIT FUNCTION !要素として存在しないなら NEXT j END IF LET isSubset=TRUE END FUNCTION !補助ルーチン SUB PrintOut(nm$,C$()) !外延的表示(要素を並べる) LET nC=N(C$) IF nC<1 THEN PRINT nm$;"=φ" ELSE PRINT nm$;"={ "; FOR i=1 TO nC-1 IF POS(C$(i)," ")>0 THEN !2次元なら PRINT "{ ";C$(i);" } "; ELSE PRINT C$(i);", "; END IF IF MOD(i,10)=0 AND nC>10 THEN PRINT REPEAT$(" ",BLEN(nm$&"={ ")); END IF NEXT i IF POS(C$(i)," ")>0 THEN !最後の要素 PRINT "{ ";C$(i);" } }" ELSE PRINT C$(i);" }" END IF END IF END SUB SUB conv2to1(A$, C$()) !2次元から1次元へ変換する CALL empty(C$) LET nC=N(C$) IF A$="" THEN EXIT SUB LET tt$="" FOR i=1 TO LEN(A$) !文字列の終りまで LET t$=mid$(A$,i,1) IF t$="," OR t$=" " THEN !分離文字なら LET nC=nC+1 LET C$(nC)=tt$ !設定する LET tt$="" ELSE LET tt$=tt$&t$ END IF NEXT i LET nC=nC+1 LET C$(nC)=tt$ !設定する LET C$(0)=STR$(nC) END SUB SUB sort(A$()) !昇順に並べ替える LET nA=N(A$) FOR i=1 TO nA-1 FOR j=i TO nA IF A$(j)<A$(i) THEN LET t$=A$(j) !swap A$(i),A$(j) LET A$(j)=A$(i) LET A$(i)=t$ END IF NEXT j NEXT i END SUB |
├典型的な問題(高校数学) 山中和義 2007/07/13 13:19:43 (修正4回) ツリーへ
Re: つづき(サブルーチン部分) |
返事を書く ノートメニュー |
山中和義 <drdlxujciw> 2007/07/13 13:19:43 ** この記事は4回修正されてます | |
典型的な問題(高校数学)
!3桁の自然数のうち U={100,101,102, … ,999} DIM U$(1000) FOR k=100 TO 999 !要素の定義 CALL element(STR$(k),U$) !k∈U NEXT k PRINT N(U$) !N(U) CALL printout("U",U$) !表示して確認する !6で割り切れる数 A DIM A$(1000) FOR k=INT(100/6)+1 TO INT(1000/6) CALL element(STR$(k*6),A$) !k*6∈A NEXT k PRINT N(A$) CALL printout("A",A$) !8で割り切れない数 ¬B=U-B DIM B$(1000) !まず、8で割り切れる数を求めて、、、 FOR k=INT(100/8)+1 TO INT(1000/8)-1 !1000は除く CALL element(STR$(k*8),B$) !k*8∈B NEXT k PRINT N(B$) CALL printout("B",B$) DIM Bbar$(1000) !その補集合 CALL except(U$,B$, Bbar$) !U-B PRINT N(Bbar$) CALL printout("¬B",Bbar$) !6でも8でも割り切れる数 A∩B DIM AnB$(1000) CALL intersect(A$,B$, AnB$) PRINT N(AnB$) CALL printout("A∩B",AnB$) !6でも8でも割り切れない数 ¬A∩¬B=¬(A∪B)=U-(A∪B) ※ド・モルガンの法則 DIM AuB$(1000),F$(1000) CALL union(A$,B$, AuB$) !A∪B CALL sort(AuB$) PRINT N(AuB$) CALL printout("A∪B",AuB$) CALL except(U$,AuB$, F$) !U-(A∪B) PRINT N(F$) CALL printout("¬A∩¬B",F$) END |
└!川渡りパズル(狼とヤギと野菜)をつくる 山中和義 2007/07/13 13:25:26 (修正3回) ツリーへ
Re: つづき(サブルーチン部分) |
返事を書く ノートメニュー |
山中和義 <drdlxujciw> 2007/07/13 13:25:26 ** この記事は3回修正されてます | |
!川渡りパズル(狼とヤギと野菜)をつくる
DIM X$(1) !集合の定義 DATA 1, "人" !要素の個数、要素の定義 MAT READ X$ DIM Y$(3) DATA 3, "狼","ヤギ","野菜" MAT READ Y$ !初期化する DIM Z1$(2),Z2$(2) !禁じ手 Y$={狼,ヤギ,野菜} CALL subset(Y$,1,2, Z1$) !{狼,ヤギ} CALL subset(Y$,2,3, Z2$) !{ヤギ,野菜} DIM D$(10),W$(10) !作業変数 CALL cross(X$,Y$, W$) !乗船の組合せ=(人×物)∪人 CALL union(X$,W$, D$) DIM bank$(4),opposite_bank$(4),ship$(2) !岸、対岸、船 CALL union(X$,Y$, bank$) !岸=A∪B CALL empty(ship$) !船=φ CALL empty(opposite_bank$) !対岸=φ LET st=0 !ステップ数 LET GameClear=FLASE DO WHILE GameClear=FLASE !ゲームクリアまで LET st=st+1 CALL DisplayStatus !現状を表示する LET aboard=FALSE DO IF MOD(st,2)=0 THEN PRINT "対"; PRINT "岸から舟に乗るものを選択してください。(";st;"回目)" FOR k=1 TO N(D$) CALL conv2to1(D$(k), W$) !!!PRINT USING"##=<######":k,D$(k) CALL PrintOut(STR$(k),W$) NEXT k DO INPUT PROMPT "番号を入力してください。":m !選択する LOOP UNTIL m>0 AND m=<N(D$) CALL conv2to1(D$(m), ship$) !船⊆岸なら、乗船は可能だが、、、 CALL PrintOut("debug",ship$) !debug debug debug IF MOD(st,2)=1 THEN CALL copy(bank$, W$) ELSE CALL copy(opposite_bank$, W$) END IF IF isSubset(ship$,W$)=FALSE THEN PRINT "選択できません。" ELSE DIM C$(10) !作業変数 CALL except(W$,ship$, C$) !岸-船 IF check(C$)=TRUE THEN !岸に残されたものを確認しないと、、、 PRINT "禁じ手で選択できません。" ELSE IF MOD(st,2)=1 THEN CALL copy(C$, bank$) ELSE CALL copy(C$, opposite_bank$) END IF LET aboard=TRUE END IF END IF LOOP UNTIL aboard=TRUE !乗船を確認する |
└つづき 山中和義 2007/07/13 13:26:24 (修正2回) ツリーへ
Re: !川渡りパズル(狼とヤギと野菜)をつくる |
返事を書く ノートメニュー |
山中和義 <drdlxujciw> 2007/07/13 13:26:24 ** この記事は2回修正されてます | |
つづき
!川を渡る CALL DisplayStatus !現状を表示する !船を降りて、対岸を確認する IF MOD(st,2)=1 THEN CALL union(opposite_bank$,ship$, C$) !対岸∪船 CALL copy(C$, opposite_bank$) ELSE CALL union(bank$,ship$, C$) CALL copy(C$, bank$) END IF CALL empty(ship$) !船=φ LET GameClear=isEmpty(bank$) !渡りきったら LOOP CALL DisplayStatus !現状を表示する PRINT st;"回でクリア!" FUNCTION check(C$()) !禁じ手かどうか確認する LET check=FALSE IF isSubset(Z1$,C$)=TRUE THEN LET check=TRUE IF isSubset(Z2$,C$)=TRUE THEN LET check=TRUE END FUNCTION SUB DisplayStatus !現状を表示する CALL PrintOut("岸",bank$) CALL PrintOut("船",ship$) CALL PrintOut("対岸",opposite_bank$) END SUB END |
└参考、解答を得るプログラム 山中和義 2007/07/13 20:39:28 ツリーへ
Re: つづき |
返事を書く ノートメニュー |
山中和義 <drdlxujciw> 2007/07/13 20:39:28 | |
参考、解答を得るプログラム
!狼、やぎ、野菜の有無を2進数で表す。 !狼は2ビット目、やぎは1ビット目、野菜は0ビット目 !例. 岸に狼と野菜があれば、K=2^2+2^0=5 !同様に、移動するものもこの値で表現する。 !例. 狼と野菜を移動するのは、K=2^2+2^0=5だが、1つまでなのでこれは禁じ手となる。 FUNCTION f(i,p) !評価関数 ※対岸に移動した数 SELECT CASE p CASE 0 !何もない LET f=N2 CASE 1,2,4 !野菜、やぎ、狼 IF MOD(i,2)=1 THEN LET f=N2+1 ELSE LET f=N2-1 CASE ELSE PRINT "移動できません。(移動は1つまで)" STOP END SELECT END FUNCTION LET TRUE=1 LET FALSE=0 LET K=7 !岸には{狼,やぎ,野菜} LET K2=0 !対岸には何もない LET N2=0 !対岸の個数 FOR i=1 TO 20 !ステップ数 ※奇数は岸⇒対岸、偶数は対岸⇒岸 PRINT "●";i;"回目" IF MOD(i,2)=1 THEN LET x=K ELSE LET x=K2 !どちらの岸か LET m0=m !前に移動したもの LET n=-999 IF m0=0 THEN !同じものを移動 ※元に戻る ELSE CALL www(i,x,0, m,n) !何も移動しないと仮定する END IF FOR j=0 TO 2 !野菜、やぎ、狼を移動すると仮定する IF m0=2^j THEN ELSE IF MOD(INT(x/2^j),2)=1 THEN CALL www(i,x,2^j, m,n) !ビットチェック END IF NEXT j PRINT "{ "; !この手にする CALL printOut(m) PRINT "}を移動" IF MOD(i,2)=1 THEN LET K=K-m !状況を更新する LET K2=K2+m ELSE LET K=K+m LET K2=K2-m END IF LET N2=n PRINT "岸={ "; IF MOD(i,2)=0 THEN PRINT "人 "; CALL printOut(K) PRINT "}","対岸={ "; IF MOD(i,2)=1 THEN PRINT "人 "; CALL printOut(K2) PRINT "}" IF K=0 THEN EXIT FOR !クリアなら NEXT i SUB www(i,x,p, m,n) !移動できるか確認する LET v0=f(i,p) !pで検討 PRINT "{ "; CALL printOut(p) PRINT "}を移動すれば対岸は";v0;"になる" IF check(x,p)=FALSE THEN !残りは岸に残せれば、採用する IF v0>=n THEN !今までより、良い手で PRINT "採用!" LET n=v0 !対岸の個数 LET m=p !移動するもの ELSE PRINT "不採用!" END IF ELSE PRINT "禁じ手です!" END IF END SUB FUNCTION check(x,p) !禁じ手かどうか確認する LET t=x-p !岸に残すもの SELECT CASE t CASE 3,6,7 !{やぎ,野菜}、{狼,やぎ}、{狼,やぎ,野菜} LET check=TRUE CASE ELSE LET check=FALSE END SELECT END FUNCTION SUB PrintOut(p) IF MOD(INT(p/4),2)=1 THEN PRINT "狼 "; IF MOD(INT(p/2),2)=1 THEN PRINT "やぎ "; IF MOD(p,2)=1 THEN PRINT "野菜 "; END SUB END |