教えて下さい。

 投稿者:GAI  投稿日:2010年 2月27日(土)13時32分58秒
  1~10の数字を2つずつ5組に分けて、各組の2数の差が1,2,3,4,5
となる組を探すことをしたくて、山中さんから作って頂いたプログラムを参考に次のものを作り調べようと思いました。
ただし0と10の数の違いに作業がうまくいきません。
この点をクリアーするための方法を教えて下さい。
また、プログラム中の数値P,Q,R,S,Tを小さい順に並べて表示させるためにはどうすればいいのでしょうか?
ご教授お願いします。
これを一般化して2n枚のカードに1~2nの数字が書かれているものを2枚ずつのn組に分けて、その2数の差が1,2,3,・・・,nとなれる組がいくつ存在できるか知りたい。


DIM A$(945) !全パターン  例. 1234567890なら、(1,2)(3,4)(5,6)(7,8)(9,10)と読む
LET C=1
FOR i=2 TO 10 !1組目(1,i)  ※comb(10,2)/5=9通り
   LET w$="1234567890" !restore it

   LET A$(C)=w$(1:1)&w$(i:i) !その2つを抜き取って、連番を再構成する
   LET w$(i:i)=""
   LET w$(1:1)=""

   FOR j=2 TO 8 !2組目(1,j)  ※comb(8,2)/4=7通り
      LET x$=w$ !restore it

      LET A$(C)=A$(C)(1:2)&x$(1:1)&x$(j:j)
      LET x$(j:j)=""
      LET x$(1:1)=""

      FOR k=2 TO 6 !3組目(1,k)  ※comb(6,2)/3=5通り
         LET y$=x$ !restore it

         LET A$(C)=A$(C)(1:4)&y$(1:1)&y$(k:k)
         LET y$(k:k)=""
         LET y$(1:1)=""

         FOR l=2 TO 4 !4組目(1,l) ※comb(4,2)/2=3通り
            LET z$=y$  !restore it

            LET A$(c)=A$(C)(1:6)&z$(1:1)&z$(l:l)
            LET z$(l:l)=""
            LET z$(1:1)=""

            LET A$(C)=A$(C)&z$ !5組目  ※comb(2,2)/1=1通り


            PRINT C;": ";A$(C) !結果を表示する

            LET C=C+1
            IF C<=945 THEN LET A$(C)=A$(C-1)(1:6)   !copy it
         NEXT l
      NEXT k
   NEXT j
NEXT i

PRINT
PRINT

FOR C=1 TO 945

   LET P=ABS(VAL(A$(C)(1:1))-VAL(A$(C)(2:2)))
   LET Q=ABS(VAL(A$(C)(3:3))-VAL(A$(C)(4:4)))
   LET R=ABS(VAL(A$(C)(5:5))-VAL(A$(C)(6:6)))
   LET S=ABS(VAL(A$(C)(7:7))-VAL(A$(C)(8:8)))
   LET T=ABS(VAL(A$(C)(9:9))-VAL(A$(C)(10:10)))
   PRINT C;"  ";P;Q;R;S;T

NEXT C

END
 

Re: 教えて下さい。

 投稿者:山中和義  投稿日:2010年 2月27日(土)15時03分3秒
  > No.1056[元記事へ]

GAIさんへのお返事です。

> 1~10の数字を2つずつ5組に分けて、各組の2数の差が1,2,3,4,5となる組を探す
> ただし0と10の数の違いに作業がうまくいきません。

16進法で対応します。


> また、プログラム中の数値P,Q,R,S,Tを小さい順に並べて表示させるためにはどうすればいいのでしょうか?

差1~5を索引番号として、どの組で発生したかを「一意性」と一緒に扱えばいいでしょう。


LET A$="" !全パターン 例. 123456789Aなら、(1,2)(3,4)(5,6)(7,8),(9,10)と読む
LET C=0
FOR i=2 TO 10 !1組目(1,i) ※comb(10,2)/5=9通り
   LET w$="123456789A" !restore it

   LET A$=w$(1:1)&w$(i:i) !その2つを抜き取って、連番を再構成する
   LET w$(i:i)=""
   LET w$(1:1)=""

   FOR j=2 TO 8 !2組目(1,j) ※comb(8,2)/4=7通り
      LET x$=w$ !restore it

      LET A$=A$(1:2)&x$(1:1)&x$(j:j)
      LET x$(j:j)=""
      LET x$(1:1)=""

      FOR k=2 TO 6 !3組目(1,k) ※comb(6,2)/2=5通り
         LET y$=x$ !restore it

         LET A$=A$(1:4)&y$(1:1)&y$(k:k)
         LET y$(k:k)=""
         LET y$(1:1)=""

         FOR L=2 TO 4 !4組目(1,k) ※comb(4,2)/2=3通り
            LET z$=y$ !restore it

            LET A$=A$(1:6)&z$(1:1)&z$(L:L)
            LET z$(L:L)=""
            LET z$(1:1)=""

            LET A$=A$&z$ !5組目 ※comb(2,2)/1=1通り


            DIM P(5) !差1~5の出現度数
            MAT P=ZER
            FOR q=1 TO 5
               LET t=ABS( BVAL(A$(2*q-1:2*q-1),16)-BVAL(A$(2*q:2*q),16) ) !ペアの差を算出する
               IF NOT(t<=5 AND P(t)=0) THEN EXIT FOR !差の範囲「1~5」と「一意性」
               LET P(t)=q !※「表示するときの索引番号」を兼ねる
            NEXT q
            IF q>5 THEN !結果を表示する
               LET C=C+1 !解答数
               PRINT "No.";C
               FOR q=1 TO 5 !索引番号の順に
                  PRINT "(";A$(2*P(q)-1:2*P(q)-1);",";A$(2*P(q):2*P(q));") ";
               NEXT q
               PRINT
            END IF

         NEXT L
      NEXT k
   NEXT j
NEXT i

END
 

Re: 教えて下さい。

 投稿者:山中和義  投稿日:2010年 2月28日(日)09時01分39秒
  > No.1056[元記事へ]

GAIさんへのお返事です。

> これを一般化して2n枚のカード

計算量 O(COMB(2*N,N)*FACT(N)) n=12は無理かな、、、
!問題
! 1~2nの数の記入されたカードで2枚ずつのペアをn組つくるとき、
! カードの数字の差がそれぞれ1、2、3、4、…、nになるような組み合わせを求めよ。

!答え
! n mod 4が0,1のとき、可能性がある。2,3なら、0
!
!n=1,2,3,4, 5,6,7,  8,   9,10,11,12,13,14,15, …
! 1,0,0,6,10,0,0,504,2656, 0, 0,??,??, 0, 0, …
!※パソコンの性能により調査できず

!PentiumⅢ700MHz,WindowsMe,2進モード n=9の場合、計算時間= 218.93 秒

LET t0=TIME


LET N=4 !1~2*Nの数が記入されたカード Ω={1,2,…,N-1,N,N+1,…,2*N-1,2*N}

LET s=1
FOR k=1 TO N !Σ[k=1,N]{COMB(2*k,2)/k}
   LET s=s*COMB(2*k,2)/k
NEXT k
PRINT "場合の数=";s


LET ANSWER_COUNT=0

!●ステップ1
!ペアを適当に作って
! ペアの小さい方の数字P={a,b,c,d,…}の総和をA=a+b+c+d+ …
! ペアの大きい方の数字Q={x,y,z,w,…}の総和をB=x+y+z+w+ …
!とする。

LET w=0 !B+A=(a+b+c+d+ …)+(x+y+z+w+ …)=Σ[k=1,2*n]k …式[1]
FOR k=1 TO 2*N
   LET w=w+k
NEXT k
LET s=0 !B-A=(a+b+c+d+ …)-(x+y+z+w+ …)=Σ[k=1,n]k …式[2]
FOR k=1 TO N
   LET s=s+k
NEXT k

LET B=(w+s)/2 !連立方程式を解く [1]+[2]より
LET A=w-B ![1]に代入
PRINT "A=";A; "B=";B !debug
IF A<>INT(A) OR B<>INT(B) THEN
   PRINT "AまたはBは整数でないので、解なし。"
   STOP
END IF


!●ステップ2 Aに着目して、Pを求める。

DIM E(N) !初期パターン {1,2,3,4,…,N}
FOR k=1 TO N
   LET E(k)=k
NEXT k

DIM P(N) !P={a,b,c,d,…}
MAT P=E
DO !ペアの全パターンから
   LET s=0 !a+b+c+d+ …
   FOR k=1 TO N
      LET s=s+P(k)
   NEXT k
   IF P(1)<>1 THEN EXIT DO !※1∈P
   IF P(N)<>2*N AND s=A THEN !条件を満たすもの ※2*N∈Q
      MAT PRINT USING("P={"&REPEAT$("### ",N)&"}"): P !debug


      !そのPからQは一意に求まる

      DIM Q(2*N) !Q={x,y,z,w,…}=Ω-P
      MAT Q=ZER !未使用とする
      FOR k=1 TO N
         LET Q(P(k))=-1 !使用とする
      NEXT k
      !!!FOR k=1 TO 2*N !Q
      !!!   IF Q(k)=0 THEN PRINT k;
      !!!NEXT k
      !!!PRINT


      !●ステップ3 PとQが差の条件を満たすかどうか確認する

      DIM R(N) !差分列 R={1,2,3,4,…}
      MAT R=E
      DO !Pと差分Rから機械的に、ペア (Pk,Pk+Rk)を生成する
         DIM QQ(2*N)
         MAT QQ=Q !restore it

         FOR k=1 TO N !生成した数がQの要素(Pk+Rk∈Q)となるかどうか確認する
            LET s=P(k)+R(k)
            IF s>2*N THEN EXIT FOR !要素の範囲?
            IF QQ(s)<>0 THEN EXIT FOR !一意性?
            LET QQ(s)=k !使用とする
         NEXT k
         IF k>N THEN !Qと一致するなら、結果を表示する
            LET ANSWER_COUNT=ANSWER_COUNT+1 !解答数
            PRINT "No.";ANSWER_COUNT
            FOR k=1 TO N !ペア (Pk,Pk+Rk)
               PRINT "(";P(k);",";P(k)+R(k);") ";
            NEXT k
            MAT PRINT R; !差分列
         ELSE
            LET h=PermFactorial2Num(R,N) + FACT(N-k)-1 !…,Rk,~をスキップする
            CALL Num2PermFactorial(h, R,N)
         END IF

         CALL NextPermFactorial(R,N, rc) !次へ
      LOOP UNTIL rc=0 !FACT(N)通り

   END IF


   CALL NextComb(P,2*N,N, rc) !次へ
LOOP UNTIL rc=0 !COMB(2*N,N)通り

IF ANSWER_COUNT=0 THEN PRINT "解なし"


PRINT "計算時間=";TIME-t0

END


!異なるn個からr個を選ぶ組合せ COMB(N,R)通り

EXTERNAL SUB NextComb(A(),N,R, rc) !辞書式順序で次の組合せを返す
LET rc=0 !完了
FOR i=R TO 1 STEP -1
   IF A(i)<N-R+i THEN !i~N-R+iで更新する
      LET A(i)=A(i)+1
      FOR j=i+1 TO R !A(i)<A(i+1)< … <A(R) 最初の並び
         LET A(j)=A(j-1)+1
      NEXT j
      LET rc=1 !未了
      EXIT SUB
   END IF
NEXT i
END SUB


!異なるn個のものをすべて並べる FACT(n)通り

EXTERNAL FUNCTION PermFactorial2Num(A(),N) !順列パターンに番号を付ける ※辞書式順序
FOR j=1 TO N-1 !階乗進数の各桁の値+1 A[1..N]=(N-1)! … 3! 2! 1! 0!
   FOR k=j+1 TO N
      IF A(k)>=A(j) THEN LET A(k)=A(k)-1
   NEXT k
NEXT j
LET v=0
FOR j=N TO 1 STEP -1 !非負の10進数整数へ
   LET v=v*j+A(N-j+1)-1
NEXT j
LET PermFactorial2Num=v
END FUNCTION

EXTERNAL SUB Num2PermFactorial(h, A(),N) !番号から順列パターンを生成する ※辞書式順序
LET v=h !非負の10進数整数を階乗進数へ
FOR j=1 TO N
   LET t=INT(v/j)
   LET A(N-j+1)=v-t*j +1 !階乗進数の各桁の値+1 A[1..N]=(N-1)! … 3! 2! 1! 0!
   LET v=t
NEXT j
FOR j=N-1 TO 1 STEP -1 !順列パターンへ
   FOR k=j+1 TO N
      IF A(k)>=A(j) THEN LET A(k)=A(k)+1
   NEXT k
NEXT j
END SUB

EXTERNAL SUB NextPermFactorial(A(),N, rc) !辞書式順序で次の順列を返す
LET rc=0 !完了

LET i=N-1 !順列を右から左にみて、増加列から減少列に変わる位置iを探す
DO WHILE i>0 AND A(i)>=A(i+1) !0は番人
!!!DO WHILE i>0 AND A(i)<=A(i+1) !0は番人 ※前の順列 ←←←←←
   LET i=i-1
LOOP
IF i=0 THEN EXIT SUB !A(1)>A(2)>A(3)> … >A(N)なら 例. N=4、4,3,2,1

LET j=N !その位置iより右で、A(i)以上で最小の数A(j)を探す
DO WHILE A(i)>=A(j)
!!!DO WHILE A(i)<=A(j) ! ※前の順列 ←←←←←
   LET j=j-1
LOOP
LET t=A(i) !A(i)とA(j)を交換する
LET A(i)=A(j)
LET A(j)=t

LET i=i+1 !(i+1)からNまでの範囲を逆順にする
LET j=N
DO WHILE i<j
   LET t=A(i) !swap it
   LET A(i)=A(j)
   LET A(j)=t
   LET i=i+1
   LET j=j-1
LOOP
LET rc=1 !未了
END SUB
 

Re: 教えて下さい。

 投稿者:山中和義  投稿日:2010年 2月28日(日)17時00分8秒
  > No.1056[元記事へ]

GAIさんへのお返事です。

> これを一般化して2n枚のカード

別解として、「2nから2枚を選ぶ」を「2枚を2nに置く」として考えてみました。
計算量 O(FACT(N))です。n=13が求まるかも、、、
!1~nの数が記入されたカードが2枚ずつある。
!これを1~2nの位置に置くとき、その配置の「場合の数」を求めよ。
!ただし、番号nのカードどうしはnだけ離れて置く必要がある。
!例 4の場合
! 12345678←位置
! 4***4***
! *4***4**
! **4***4*
! ***4***4

!答え
! n mod 4が0,1のとき、可能性がある。2,3なら、0
!
!n=1,2,3,4, 5,6,7,  8,   9,10,11,    12,13,14,15, …
! 1,0,0,6,10,0,0,504,2656, 0, 0,455936,??, 0, 0, …
!※パソコンの性能により調査できず

!PentiumⅢ700MHz,WindowsMe,2進モード n=9の場合、計算時間= 179.22 秒

LET t0=TIME

LET N=8 !1~nのカード

PUBLIC NUMERIC ANSWER_COUNT
LET ANSWER_COUNT=0

DIM A(2*N) !配置位置
MAT A=ZER
CALL backtrack(N,N,A) !N,…,2,1 計算量O(FACT(N))

IF ANSWER_COUNT=0 THEN PRINT "解なし"

PRINT "計算時間=";TIME-t0

END

EXTERNAL SUB backtrack(p,N,A()) !バックトラック法で検証する
FOR i=1 TO 2*N-p !カードpの1枚目を位置iに置く
   IF A(i)=0 AND A(i+p)=0 THEN !2枚とも置けるなら

      IF p=1 THEN !すべて置けたら、結果を表示する
      !!!MAT PRINT A; !debug
         LET ANSWER_COUNT=ANSWER_COUNT+1 !解答数
         PRINT "No.";ANSWER_COUNT
         FOR s=0 TO N-1 !差が1~nの順に
            FOR x=1 TO 2*N
               IF A(x)=s THEN
                  PRINT "(";x;",";x+s+1;") "; !ペア
                  EXIT FOR
               END IF
            NEXT x
         NEXT s
         PRINT
      ELSE
         LET A(i)=p-1 !仮に置いてみる
         LET A(i+p)=p-1

         CALL backtrack(p-1,N,A) !次へ

         LET A(i)=0 !元に戻す
         LET A(i+p)=0
      END IF

   END IF
NEXT i
END SUB
 

戻る