カードマジックで出会った現象

 投稿日:2008年11月 4日(火)22時52分56秒
  お二人の強力なプログラマーの出現により、C言語とBASIC言語との比較をしながらとってもいい勉強ができています。
Cのスピードは魅力的ですが、どうも約束事が多くて馴染み難いのです。
その点BASICの記述では何をしたいのかがCに較べると読み取り易い気がします。
C言語でプログラムを書いていただいた方には後ほど質問をしておきます。
話は変わりますが・・・
この場を借りて日頃疑問に感じていることを解析してほしいんですが実は自分はカードマジックが大好きでそれに関連した本を読んでいて出会った記述でして、次のような事が起きます。
ぜひ、トランプで確認を!
ハートとスペードを順にA、2、3・・・Qと重ねる。
ハートパケットはテーブルに裏向き(Aが上)で置く。
観客に1~12までの好きな数字を決め手もらう。
スペードパケットを手に裏向き(Aが上)に持ち、上から表向きにしながらテーブルへ左、右、左、・・・と2つの山を作りながらカードを重ねていく。
客が決めた数字の枚数目の時、このカードはテーブルの別の場所に捨てられた札として、表向きのまま除く。その代わりとして、ハートパケットの一番上のカードをこのカードの置くべきだった山へ表向きにのせる。後続けていき手持ちのカードが無くなるまで進む。
左の山を持ち上げ、右の山へ重ね、一つになったパケットを手にとり、裏向きで持つ。
同じことをくり返し、最終的に手にはハート、捨て場にはスペードが集まる。
この二つの山をテーブルに並べて置く。
観客にハートまたはスペードから好きなカード(A~Qまでの中から)の名前を言ってもらう。(例ハートの8を観客が選んだとして説明します。)
客がハートを選択したのなら、まずスペードの山から、上より8枚目のカードを引き出す。
(もし客がスペードの選択をしたのならハートの山からカードを引き出すことになる。)
引き出したカードの数字に従い、今度はハートパケットの上からその数字の枚数目のカードを表向きにする。
ここから客が指定しておいたハートの8が出現する。
<わかり難いでしょうか?>
この客に任意で選択させている事(1~12を選ばせたり、好きなカードを指定させたり)をやっておきながら、的確に客のカードを当ててしまう仕組みはとっても数学的に巧く計算されていると思われます。
12という数字が何かキーになる性質を有しているからだろうと予感されます。
このことをプログラムで解明して欲しいんですが・・・
 

Re: カードマジックで出会った現象

 投稿者:山中和義  投稿日:2008年11月 5日(水)16時23分20秒
  > No.48[元記事へ]

GAIさんへのお返事です。

たぶんこれで大丈夫でしょう。
剰余(mod)が関係しているのでしょうか?(一種のシャッフルですから)


LET mk$="SCHD" !マーク

SUB dec(C(),p, w) !パケット内上からp位置のカードを削除する
   LET w=C(p)
   FOR i=p TO C(0)-1 !前に詰める
      LET C(i)=C(i+1)
   NEXT i
   LET C(0)=C(0)-1
END SUB
SUB inc(C(),p,w) !パケット内上からp位置にカードを追加する
   IF p<=C(0) THEN
      FOR i=C(0) TO p STEP -1 !後ろにずらす
         LET C(i+1)=C(i)
      NEXT i
   ELSE
      LET p=C(0)+1 !最後
   END IF
   LET C(p)=w
   LET C(0)=C(0)+1
END SUB
DIM TT(0 TO 13*4+1)
SUB add(C1(),C2(), C()) !C1を上、C2を下にパケットを重ねる
   FOR i=1 TO C1(0)
      LET TT(i)=C1(i)
   NEXT i
   FOR i=1 TO C2(0) !続けて
      LET TT(C1(0)+i)=C2(i)
   NEXT i
   LET C(0)=C1(0)+C2(0)
   FOR i=1 TO C(0)
      LET C(i)=TT(i)
   NEXT i
END SUB
SUB clr(C()) !パケットをクリアする
   LET C(0)=0
END SUB
SUB rev(C()) !パケットを裏返す
   FOR i=1 TO INT(C(0)/2)
      swap C(i),C(C(0)-i+1)
   NEXT i
END SUB
SUB disp(C(),m$) !パケットを上から順に表示する
   PRINT m$;"(";C(0);"枚)";
   FOR i=1 TO C(0)
      PRINT C(i);
   NEXT i
   PRINT
END SUB
!-------------------- ここまでがサブルーチン


LET N=12 !枚数

DIM S(0 TO N),H(0 TO N) !スペード、ハートパケットの初期化
FOR i=1 TO N !整列
   LET S(i)=i !スペード 1~13
   LET H(i)=i+13*2 !ハート 27~39
NEXT i
LET H(0)=N !枚数
LET S(0)=N

!テーブルの初期化
DIM Y1(0 TO N),Y2(0 TO N),Y3(0 TO N) !山1、山2、捨て場
CALL clr(Y1) !山のクリア
CALL clr(Y2)
CALL clr(Y3)

CALL dump !内容を確認する

SUB dump
   CALL disp(S,"スペード") !トレース
   CALL disp(H,"ハート")
   CALL disp(Y1,"山1")
   CALL disp(Y2,"山2")
   CALL disp(Y3,"捨て場")
   PRINT
END SUB


!1回目
INPUT PROMPT "好きな数字(2~N)?": K !好きな数字 1~N

CALL routine

SUB routine !作業の定義
   FOR x=1 TO N !手持ちのカードがなくなるまで
      PRINT x;"枚目をテーブルへ"
      CALL dec(S,1,w) !削除する
      IF x=K THEN !一致する枚数目なら捨て場へ
      !IF MOD(x,K)=0 THEN !一致する枚数目なら捨て場へ
         CALL inc(Y3,1,w)
         CALL dec(H,1,w) !代替として場から
      END IF

      IF MOD(x,2)=0 THEN !左右交互で山に置く
         CALL inc(Y2,1,w)
      ELSE
         CALL inc(Y1,1,w)
      END IF

      CALL dump !内容を確認する
   NEXT x
   PRINT
END SUB




!2回目以降
DO
   CALL add(Y1,Y2, S) !山を重ねて手に持つ
   CALL rev(S)
   CALL disp(S,"スペード")
   PRINT

   IF H(0)=0 THEN EXIT DO !ハートパケットがなくなるまで

   CALL clr(Y1) !山のクリア
   CALL clr(Y2)

   CALL routine
LOOP


MAT H=Y3
CALL rev(H)
CALL disp(H,"ハート")


INPUT PROMPT "カードのマーク?": c$
INPUT PROMPT "数字(1~N)?": K

IF UCASE$(c$)="H" THEN
   LET w=H(K) !スペードの列になっている
   LET w=S(w)
ELSE
   LET w=MOD(S(K),13) !ハートの列になっている
   LET w=H(w)
END IF
PRINT mid$(mk$,INT(w/13)+1,1); MOD(w,13)


END
 

Re: カードマジックで出会った現象

 投稿者:SECOND  投稿日:2008年11月 5日(水)17時59分39秒
  > No.48[元記事へ]

!トランプ は、どの数を選んでも、
!互いに インデックスに なってしまうようです。難解。

DIM s(12),h(12),w(12),t(12)

PRINT "----- 最初の状態 -----"
CALL ready
CALL printa(s) ! スペード
CALL printa(h) ! ハート
PRINT
!
FOR R=1 TO 12
   PRINT "----- Request";R;"の場合-----"
   CALL ready
   LET k=1
   DO WHILE k< 13
      FOR i=1 TO 12
         LET j=MOD(i,2)*7+INT(i/2) ! 分けた2つを重ねた時の位置。
         IF i=R THEN
            LET t(j)=h(k) ! ハートをテーブルへ
            LET w(k)=s(i) ! 手元(最初スペード)を「捨て」へ
            LET k=k+1
         ELSE
            LET t(j)=s(i) ! 手元(最初スペード)をテーブルへ
         END IF
      NEXT i
      MAT s=t ! テーブルを手元(最初スペード)へ
   LOOP
   CALL printa(s) ! 比較・・・手元(最初スペード)
   CALL printa(w) ! 比較・・・「捨て」の重なり
   PRINT
NEXT R

SUB printa(a())
   FOR n=1 TO 12
      PRINT USING "## ":a(n);
   NEXT n
   PRINT " …互いに Index."
END SUB

SUB ready
   FOR i=1 TO 12
      LET s(i)=i
      LET h(i)=i
   NEXT i
END SUB

END
 

Re: カードマジックで出会った現象

 投稿者:山中和義  投稿日:2008年11月 5日(水)21時53分27秒
  > No.49[元記事へ]

リフルシャッフルの性質を利用していると思います。

プログラムを実行して表示されるKは、
「手持ちのカードを左右の山に分けて、左右と重ね1つの山にする」の操作に該当します。
一番上の索引番号が最初に聞いた好きな番号です。どの列でも構いませんが、
その列を上から順に見ていくと、捨て場に積まれる(スペードの)カードの順になります。

ところで置き換えたハートのカードは、この番号位置に置き換わりますが、
最終的に、12回の操作でスペードのカードに対応した位置に整列されることになります。

これで参照位置とその配置位置をうまく絡ませることができます。

ちょうどN回目でもとに戻る場合、たとえばN=2,4,10,12がこの問題を満たすと思います。



!置換(Permutation)の計算

!補助ルーチン
SUB PermPrintOut(A()) !表示する ※標準形(2行n列の行列表記する)
!PRINT "┌";
!FOR i=1 TO UBOUND(A)
!   PRINT USING "###": i;
!NEXT i
!PRINT " ┐"
!PRINT "└";
   FOR i=1 TO UBOUND(A)
      PRINT USING "###": A(i);
   NEXT i
   !PRINT " ┘";
   PRINT
END SUB

!置換
SUB PermIdentity(A()) !恒等置換
   FOR i=1 TO UBOUND(A)
      LET  A(i)=i
   NEXT i
END SUB
SUB PermMultiply(A(),B(), AB()) !積AB ※AB≠BA、A(BC)=(AB)C
   LET  ua=UBOUND(A)
   LET  ub=UBOUND(B)
   IF ua=ub THEN
      FOR i=1 TO ua
         LET  AB(i)=A(B(i)) !※合成写像(AB)(i)=A(B(i))
      NEXT i
   ELSE
      PRINT "次元が違います。A=";ua;" B=";ub
      STOP
   END IF
END SUB
!-------------------- ここまでがサブルーチン


!main

LET N=12 !※2,4,10,12

!A=┌ 1 2 3 4 ┐=(1 2 4 3) ※1行目の順番は固定とする
! └ 2 4 1 3 ┘
DATA 2,4,6,8,10,12,1,3,5,7,9,11 !配列変数の「添え字と値」に対応させる
!DATA 2,4,6,8,10,1,3,5,7,9 !N=10
!DATA 2,4,1,3 !N=4
!DATA 2,1 !N=2
DIM A(N)
MAT READ A

FOR i=1 TO N
   PRINT USING "###": i;
NEXT i
PRINT

DIM B(N)
CALL PermIdentity(B) !初期値

DIM c(N)
FOR k=1 TO N !回数
   CALL PermMultiply(B,A,c) !シャッフル
   PRINT "K=";k
   CALL PermPrintOut(c) !何回か実行すると元に戻る
   MAT B=c
NEXT k


END
 

戻る