新しく発言する EXIT インデックスへ
ビット演算によるパズルなどの解法

  ビット演算によるパズルなどの解法 山中和義 2007/07/23 15:58:12 
  !やっぱり再帰コールした方が・・ SECOND 2007/07/23 23:24:15  (修正3回)
  !すみません。思いっきりイジリました。 SECOND 2007/07/25 09:59:15  (修正1回)
   └新ネタを探しているとき、サイトを見つけま... 山中和義 2007/07/26 07:05:21  (修正2回)
    └ありがとうございました。面白いカウンター... SECOND 2007/07/26 12:56:56 
     └!これを書いた本人も、よく分かっていない。... SECOND 2007/07/27 06:21:18  (修正1回)
      ├もっと簡単に記述できるようです。 山中和義 2007/07/27 14:45:00  (修正3回)
      │└ありがとうございます。移動元、移動先の取... SECOND 2007/07/27 23:00:20 
      └別解 山中和義 2007/09/17 20:15:11 

  ビット演算によるパズルなどの解法 山中和義 2007/07/23 15:58:12  ツリーへ

ビット演算によるパズルなどの解法 返事を書く ノートメニュー
山中和義 <drdlxujciw> 2007/07/23 15:58:12
●ハノイの塔

!※Microsoft BASIC互換モードで実行すること

DEF XOR(x,y)=(NOT x AND y) OR (x AND NOT y) !排他的論理和
DEF GrayCode(x)=XOR(x,INT(x/2)) !グレイコード ※右シフトしてXOR
DEF Bit(x,m)=MOD(INT(x/2^m),2) !mビット目を得る


LET n=4 !円盤の数

a0=GrayCode(0)
FOR i=1 TO 2^n-1 !移動回数
a=GrayCode(i) !n-1からnへ移行したとき、変化したビットを検出する
b=XOR(a,a0)
FOR j=0 TO n-1
IF Bit(b,j)=1 THEN PRINT j+1;"番目の円盤を ";
NEXT j
a0=a !次へ

a=MOD(i AND (i-1),3) !各ビットの論理積、論理和をとる
b=MOD((i OR (i-1))+1,3)

PRINT mid$("ABC",a+1,1);" から ";mid$("ABC",b+1,1);" へ移動"
NEXT i

END


円盤を番号を算出する部分は、

FOR j=0 TO n-1 !各ビットの0→1(ポジティブトリガ)を検出する
IF Bit(i-1,j)=0 THEN
IF Bit(i,j)=1 THEN PRINT j+1;"番目の円盤を ";
END IF
NEXT j

としてもよい。



●三山崩し

!※Microsoft BASIC互換モードで実行すること

DEF XOR(x,y)=(NOT x AND y) OR (x AND NOT y) !排他的論理和
DEF ToBin$(x,m)=right$(REPEAT$("0",m-1)&BSTR$(MOD(x,2^m),2),m) !mビットの2進数に変換する


OPTION BASE 1
DIM a(3)
DATA 3,5,7
MAT READ a

LET st=0
DO
LET st=MOD(st+1,2) !次へ

FOR k=1 TO 3 !状態を表示する
PRINT k;"番目の山に";a(k);"個",ToBin$(a(k),8)
NEXT k
PRINT ,ToBin$(XOR(XOR(a(1),a(2)),a(3)),8); !パリティ
PRINT " パリティビット"


IF st=0 THEN PRINT "先手" ELSE PRINT "後手"
DO
INPUT PROMPT "どの山から取りますか(1〜3)":p
LOOP UNTIL p>0 AND p<4 AND a(p)>0 !石がある山を選択したら
DO
INPUT PROMPT "いくつ取りますか(1〜"&STR$(a(p))&")":q
LOOP UNTIL q>0 AND q<=a(p) !1個以上


LET a(p)=a(p)-q !石を取り除く

LOOP UNTIL (a(1) OR a(2) OR a(3))=0 !すべてなくなったら

PRINT
IF st=0 THEN PRINT "先手の勝ちです。" ELSE PRINT "後手の勝ちです。"


END

  !やっぱり再帰コールした方が・・ SECOND 2007/07/23 23:24:15  (修正3回) ツリーへ

Re: ビット演算によるパズルなどの解法 返事を書く ノートメニュー
SECOND <cszcthjjdj> 2007/07/23 23:24:15 ** この記事は3回修正されてます
!やっぱり再帰コールした方が・・
!1,3,5,7,,枚は、A→Cで、一致しますが、
! 2,4,6,,,枚は、A→Bになっていませんか。

!------------
! ハノイの塔:
! 上段にいく程、より小さい円盤が、積まれている山を、A塔→C塔へ移す問題。
! 空のB塔を使い、小さい円盤の上に大きい円盤は、乗らない規則で1枚づつ運ぶ。
!------------

! 上からK番目の円盤を、Sから、Dへ動かす方法。

! 円盤は、上から、1,2,3,,,の番号で識別。K に代入して使用。
! A,B,C の塔は、左から、1,2,3 で識別。S,D に代入して使用。
! 中継する塔の番号は、(1+2+3)-S-D= 6-S-D で計算される。

! 1つ上の、
!(K−1)番目までの円盤の全部を、S、D、でない塔(6-S-D)へ移動し、
! K番目の円盤を、Sから、Dへ移動する。
! 先の(K−1)番目までの円盤の全部を、Dの上に積んで終了。
!
!(K−1)番目の上にも、円盤が有るため、
! 再帰的に同じ処理をコールし、K=1になる毎に
! 再帰が止まって、リターンしてくる。
!1番下の最大の円盤から、最初のコールをする。

!1枚少ない実行回数を N(k-1) とすると、N(k)= N(k-1)+1+N(k-1)= 2*N(k-1)+1
! N(1)=1 であるので、この数列 N(K) は、1,3,7,15,31,,,(2^k)-1
!----------

LET K=5 !一番下の円盤番号から開始。塔の段数、として設定。
LET S=1 ! 出発塔の番号123→ABC
LET D=3 ! 到着塔の番号123→ABC
CALL MovK00( K, S, D)

SUB MovK00( K, S, D)
IF 1<K THEN CALL MovK00( K-1, S, 6-S-D) ! 移動要求 K-1,,1層 S→ 中継の塔
PRINT K;"番目の円盤を ";mid$("ABC",S,1);" から ";mid$("ABC",D,1);" へ移動" ! 表示 K層 S →D
IF 1<K THEN CALL MovK00( K-1, 6-S-D, D) ! 移動要求 K-1,,1層 中継の塔 →D
END SUB

END

  !すみません。思いっきりイジリました。 SECOND 2007/07/25 09:59:15  (修正1回) ツリーへ

Re: ビット演算によるパズルなどの解法 返事を書く ノートメニュー
SECOND <cszcthjjdj> 2007/07/25 09:59:15 ** この記事は1回修正されてます
!すみません。思いっきりイジリました。
!ハノイの塔が、ある種のカウンターだったとは、驚きです。
!どうやって、見つけたんですか。
!何とか全部一致するように、なりましたが、
!何故なのか、未だに、よく分かりません。


!※Microsoft BASIC互換モードで実行すること。
! (オプション→ 文法→ Microsoft BASIC 互換)

LET n=4 !円盤の数

FOR i=1 TO 2^n-1 ! 移動回数
PRINT right$("0000000"+BSTR$(i,2),8);" ";

pEdge=i AND (NOT i-1) ! 各ビットの0→1(ポジティブ・エッジ)を検出する

K=1+LOG(pEdge)/LOG(2) ! log( 底2 )pEdge= 右から数えたビット番号。0,1,2,3,,,
S=1+MOD(i AND (i-1), 3)
D=1+MOD( (i OR (i-1))+1, 3)

IF MOD(n,2)=0 THEN ! 枚数が、2,4,6,,,の時、2(B),3(C) を交換する。
IF S=2 OR S=3 THEN S=5-S
IF D=2 OR D=3 THEN D=5-D
END IF

PRINT K;"番目の円盤を ";mid$("ABC",S,1);" から ";mid$("ABC",D,1);" へ移動"
NEXT i
END

   └新ネタを探しているとき、サイトを見つけま... 山中和義 2007/07/26 07:05:21  (修正2回) ツリーへ

Re: !すみません。思いっきりイジリました。 返事を書く ノートメニュー
山中和義 <drdlxujciw> 2007/07/26 07:05:21 ** この記事は2回修正されてます
新ネタを探しているとき、サイトを見つけました。

自分なりに考えてみると、、、
前提として
・円盤の移動回数が2^n-1手になる。
 各手を0〜2^n-1の数で表現可能。2^n進カウンタか!?

・円盤とビットの対応
 1番小さい円盤と最下位ビット、…、一番大きい円盤と最上位ビットとを対応させると
 円盤の位置パターンとビットパターンとを対応できるか!?


どのように対応させるか。。。

n番の(1番大きい)円盤に着目すれば、前半と後半に分けられる。

たとえばn=3の場合、円盤の位置パターンは、
0手目と7手目
1|| ||1
2|| ||2
3|| ||3

1手目と6手目
||| |||
2|| ||2
3|1 1|3

2手目と5手目
||| |||
||| |||
321 123

3手目と4手目
||| |||
|1| |1|
32| |23

これは間に鏡を置いた「鏡面」や、各手を「前・うしろから見る」になっている。

前半は、n枚目の円盤を移動させる「準備」になっている。
再帰呼出しのプログラムの構造からもわかると思います。



次に、手を2進表記すると
0手目 000 前半
1手目 001
2手目 010
3手目 011
---------------
4手目 100 後半
5手目 101
6手目 110
7手目 111


ビットパターンは、
0手目000 と 7手目111
1手目001 と 6手目110
2手目010 と 5手目101
3手目011 と 4手目100

ビット反転すれば同じになる。

円盤の位置パターンには対称性があり、ビットパターンと一致し、対応付けが可能。


円盤の位置は、カウンタ(フリップフロップ)の「状態」を表していることになります。


プログラムはビットパターンから
・円盤の番号を算出する
・円盤の移動先を算出する
となっています。
番号の算出は、ビットパターンを眺めているとなんとなく思いつきますが、
移動先は、こちらは難しいです。




また、円盤の数(偶数、奇数)で移動先(B、C)がかわるのは、この対応付けにより
 n番の(1番大きい)円盤が、2^(n-1)手目でどこに移動するか
で決まります。


n=1の場合
0手目
1||

1手目 ※Cの位置
||1


n=2の場合
0手目
1||
2||

1手目
1||
2|1

2手目 ※Bの位置
|||
|21


n=3の場合
0手目
1||
2||
3||

1手目
|||
2||
3|1

2手目
|||
|||
321

3手目
|||
|1|
32|

4手目 ※Cの位置
|||
|1|
|23

    └ありがとうございました。面白いカウンター... SECOND 2007/07/26 12:56:56  ツリーへ

Re: 新ネタを探しているとき、サイトを見つけま... 返事を書く ノートメニュー
SECOND <cszcthjjdj> 2007/07/26 12:56:56
ありがとうございました。面白いカウンターです。
偶数枚で移動先CがBになる問題が解決できると、
もっとコンパクトになれるのに、残念ですね。

     └!これを書いた本人も、よく分かっていない。... SECOND 2007/07/27 06:21:18  (修正1回) ツリーへ

Re: ありがとうございました。面白いカウンター... 返事を書く ノートメニュー
SECOND <cszcthjjdj> 2007/07/27 06:21:18 ** この記事は1回修正されてます
!これを書いた本人も、よく分かっていない。(^^;

!以下の複雑な論理式は、
!先の、「すみません。思いっきりイジリました。」の項で
!書いたプログラムと、内容が、全く同一なものです。
!単に、IF〜THEN〜 の式を除き、行数を減らす工夫を
!したもので、新しさは、残念ながら、何も有りません。


!コンパクトなハノイの塔・カウンター

!※Microsoft BASIC互換モードで実行すること。
! (オプション→ 文法→ Microsoft BASIC 互換)

LET n=4 ! 円盤の数 1,2,,,

FOR i=1 TO 2^n-1 ! 移動回数

K0=LOG(i AND NOT i-1)/LOG(2) !         立ち上り0→1のビット番号 0,1,2,,
S0=MOD(3-MOD(i AND i-1, 3)*(-1)^(n AND 1), 3) !  移動元の番号 0(A) 1(B) 2(C)
D0=MOD(3-MOD((i OR i-1)+1, 3)*(-1)^(n AND 1), 3) ! 移動先の番号 0(A) 1(B) 2(C)

PRINT 1+K0;"番目の円盤を ";CHR$(ORD("A")+S0);" から ";CHR$(ORD("A")+D0);" へ移動"
NEXT i
END

      ├もっと簡単に記述できるようです。 山中和義 2007/07/27 14:45:00  (修正3回) ツリーへ

Re: !これを書いた本人も、よく分かっていない。... 返事を書く ノートメニュー
山中和義 <drdlxujciw> 2007/07/27 14:45:00 ** この記事は3回修正されてます
もっと簡単に記述できるようです。

ビット演算は必要ありませんので、FULL BASICではこちらがよいと思います。


!ハノイの塔

LET n=4 !円盤の数

DIM D(n) !各円盤の位置 ※D(1)は最も小さく、D(n)は最も大きい
MAT D=ZER !すべてAに位置付ける ※A=0,B=1,C=2

FOR i=1 TO 2^n-1 !移動回数

FOR m=1 TO n-1 !最下位ビットから最初に1となる位置を検出する
IF MOD(INT(i/2^(m-1)),2)=1 THEN EXIT FOR
NEXT m !※1〜n-1がすべて0なら、nは1のはず
PRINT m;"番目の円盤を ";

PRINT mid$("ABC",D(m)+1,1);" から ";
LET D(m)=MOD((-1)^(m+n-1)+D(m),3) !m+n-1が偶数なら右へ、奇数なら左へ。 ※Aの左はC、Cの右はA
PRINT mid$("ABC",D(m)+1,1);" へ移動"

NEXT i

END





既にご存知だと思いますが、

!ハノイの塔の数列による表現(A→B)
!参考 http://bal4u.dip.jp/mt/program/archives/2005/08/nmc_1.html
!

LET n=4 !円盤の数

DIM x(n+1),b(n+1)

MAT x=ZER !円盤の位置はすべてAの位置

LET x(n+1)=0
LET b(n+1)=0

FOR m=0 TO 2^n-1 !移動回数

LET m2$=right$(REPEAT$("0",n-1)&BSTR$(m,2),n) !mをn桁の2進数へ
FOR i=n TO 1 STEP -1
LET b(n-i+1)=VAL(mid$(m2$,i,1)) !2進数表示 b(n)b(n-1) ... b(1)
NEXT i
PRINT
PRINT m;"手目"

FOR i=n TO 1 STEP -1
LET x(i)=MOD(x(i+1)+(-1)^(n-i)*(b(i)-b(i+1)),3)
NEXT i
FOR i=1 TO n
!PRINT i;"番の円盤の位置";mid$("ABC",x(i)+1,1) !円盤iの位置
PRINT TAB(x(i)*5);i
NEXT i
NEXT m

END

      │└ありがとうございます。移動元、移動先の取... SECOND 2007/07/27 23:00:20  ツリーへ

Re: もっと簡単に記述できるようです。 返事を書く ノートメニュー
SECOND <cszcthjjdj> 2007/07/27 23:00:20
ありがとうございます。移動元、移動先の取り出しに
新しいものが、ありますね。勉強になります。

      └別解 山中和義 2007/09/17 20:15:11  ツリーへ

Re: !これを書いた本人も、よく分かっていない。... 返事を書く ノートメニュー
山中和義 <drdlxujciw> 2007/09/17 20:15:11
別解


!ハノイの塔

!※Microsoft BASIC互換モードで実行すること。
! (オプション→ 文法→ Microsoft BASIC 互換)

LET n=4 !円盤の数

DIM D(n) !各円盤の位置 ※D(1)は最も小さく、D(n)は最も大きい
MAT D=ZER !すべてAに位置付ける ※A=0,B=1,C=2

FOR i=1 TO 2^n-1 !移動回数

x=i AND (-i) !最下位ビットから最初に1となる位置を検出する
m=LOG(x)/LOG(2)+1 !x=2^(m-1)
PRINT m;"番目の円盤を ";

PRINT mid$("ABC",D(m)+1,1);" から ";
LET D(m)=MOD((-1)^(m+n-1)+D(m),3) !m+n-1が偶数なら右へ、奇数なら左へ。 ※Aの左はC、Cの右はA
PRINT mid$("ABC",D(m)+1,1);" へ移動"

NEXT i

END


インデックスへ EXIT
新規発言を反映させるにはブラウザの更新ボタンを押してください。