パズル カークマン(Kirkman)女学生散歩問題

 投稿者:山中和義  投稿日:2012年 2月 3日(金)11時26分25秒
 
!カークマン(Kirkman)女学生散歩問題

!n人をk人ずつm個のグループに分けるとする。

LET N=6 !総人数
LET K=2 !グループ内人数

LET M=N/K !グループ数
PRINT N; M
IF M<>INT(M) THEN !整数 ※必要条件
   PRINT "解なし"
   STOP
END IF

LET D=(N-1)/(K-1) !1を基準にして、k=3なら(1,2,3),(1,4,5),…,(1,n-1,n)のようなグループ分けできるか
PRINT D
IF D<>INT(D) THEN !整数 ※必要条件
   PRINT "解なし"
   STOP
END IF

LET S=COMB(N-1,K-1) !(1,2),(1,3),…,(1,N)のような(1,…)形の個数
LET T=COMB(N,K) !グループ分けの総数
PRINT S; T


!日程表
! n=6,k=2の場合、m=3
! A():  ┌ m ┐
!   ┌  1 10 15  ← t
!   │  2  *  *
!  d    3  *  *
!   │  4  *  *
!   └  5  *  *
!       ↑  └ s<x<t, 10を除く。すなわち、6,7,8,9, 11,12,13,14
!       s
!
! ただし、d=(n-1)/(k-1)、s=C(n-1,k-1)、t=C(n,k)、数字1~tは、組合せ番号

DIM F(T) !日程表に埋める「組合せ番号」の使用状況 0:未使用、1:使用中
MAT F=ZER
DIM A(D*M) !日程表 d行m列

DIM Z(K) !「組合せ番号」に対応する「組合せパターン」
LET W=1
FOR i=1 TO T
   CALL Num2Comb(i-1, Z,N,K) !一覧を表示する
   PRINT STR$(i);": (";
   FOR j=1 TO K-1
      PRINT STR$(Z(j)); ",";
   NEXT j
   PRINT STR$(Z(j)); ")"


   IF Z(1)=1 THEN !日程表の1列目を埋める
      IF K=2 THEN !(1,2),(1,3),(1,4),…,(1,n-1),(1,n)のd個
         LET A((W-1)*M+1)=i
         LET F(i)=1
         LET W=W+1
      ELSE
         IF MOD(Z(2)-1,K-1)=1 THEN !(1,2,3),(1,3,4),(1,5,6),…,(1,n-1,n)のd個
            FOR j=2 TO K-1
               IF Z(j)+1<>Z(j+1) THEN EXIT FOR
            NEXT j
            IF j>K-1 THEN
               LET A((W-1)*M+1)=i
               LET F(i)=1
               LET W=W+1
            END IF
         END IF
      END IF
   END IF

   IF MOD(Z(1)-1,K)=0 THEN !日程表の1行目を埋める
      FOR j=1 TO K-1 !(1,2,3),(3,4,5),(6,7,8),…,(n-2,n-1,n)のm個
         IF Z(j)+1<>Z(j+1) THEN EXIT FOR
      NEXT j
      IF j>K-1 THEN
         LET A((Z(1)-1)/K+1)=i
         LET F(i)=1
      END IF
   END IF

NEXT i
MAT PRINT F; !debug
MAT PRINT A; !debug

PUBLIC NUMERIC C !解の数
LET C=0
CALL try(M+2,S,T,D,M,K,A,F) !2行目2列目から順に埋めていく

END


EXTERNAL SUB try(p,S,T,D,M,K,A(),F()) !バックトラック法で検索する
IF p>D*M THEN !すべて埋まったなら
   LET C=C+1 !結果を表示する
   PRINT "No.";C
   !!!MAT PRINT A; !debug
   FOR i=1 TO D*M !d行m列
      PRINT A(i);
      IF MOD(i,M)=0 THEN PRINT
   NEXT i

ELSE
   IF A(p)=0 THEN !未定義なら
      FOR i=S+1 TO T-1 !日程表に埋める「番号」の範囲
         IF i>A(p-1) THEN !並びは組合せとする

            IF F(i)=0 THEN !未使用なら
               LET F(i)=1
               LET A(p)=i

               LET N=K*M !同じ行で「グループ」を構成する「人」に重複がないか確認する
               DIM U(N),Z(K)
               MAT U=ZER !全体集合U={1,2,3,…,n}
               !!!PRINT INT((p-1)/M)*M+1; p !debug
               FOR j=INT((p-1)/M)*M+1 TO p
                  CALL Num2Comb(A(j)-1, Z,N,K) !※0~COMB(N,K)-1
                  !!!MAT PRINT Z; !debug
                  FOR L=1 TO K !部分集合{e1,e2,…,ek}
                     LET W=Z(L) !要素el
                     IF U(W)=1 THEN EXIT FOR !既に使用されているなら
                     LET U(W)=1
                  NEXT L
                  IF L<=K THEN EXIT FOR
               NEXT j
               IF j>p THEN !重複しないなら

                  CALL try(p+1,S,T,D,M,K,A,F) !次へ

               END IF

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

         END IF
      NEXT i

   ELSE !skip it
      CALL try(p+1,S,T,D,M,K,A,F)

   END IF

END IF
END SUB


EXTERNAL SUB Num2Comb(h, A(),N,R) !番号から組合せパターンを生成する ※辞書式順序
LET v=COMB(N,R)-h
FOR j=R TO 1 STEP -1 !組合せをビット位置とする
   FOR i=N-1 TO 0 STEP -1 !組合せをビット位置とする
      LET t=COMB(i,j)
      IF v>t THEN EXIT FOR
   NEXT i
   LET v=v-t
   LET A(R-j+1)=N-i !ビット位置(N-i-1)を1とする
NEXT j
END SUB


実行結果

6  3
5
5  15
1: (1,2)
2: (1,3)
3: (1,4)
4: (1,5)
5: (1,6)
6: (2,3)
7: (2,4)
8: (2,5)
9: (2,6)
10: (3,4)
11: (3,5)
12: (3,6)
13: (4,5)
14: (4,6)
15: (5,6)
1  1  1  1  1  0  0  0  0  1  0  0  0  0  1

1  10  15  2  0  0  3  0  0  4  0  0  5  0  0

No. 1
1  10  15
2  8  14
3  9  11
4  7  12
5  6  13
No. 2
1  10  15
2  9  13
3  8  12
4  6  14
5  7  11

 

Re: パズル カークマン(Kirkman)女学生散歩問題

 投稿者:山中和義  投稿日:2012年 2月 4日(土)13時43分28秒
  > No.1751[元記事へ]

日にちにおける「グループ分け」の候補


!カークマン(Kirkman)女学生散歩問題

!日にちにおける「グループ分け」の候補

!n人をk人ずつm個のグループに分けるとする。

LET K=2
LET M=3
LET N=K*M !N≦63

LET T=COMB(N,K) !候補の個数
PRINT T

DIM P(T) !グループ分けの候補
FOR i=1 TO T !全パターン
   CALL Num2CombBit(i-1,N,K,A) !番号は0~
   PRINT i; ":"; A; RIGHT$(REPEAT$("0",N)&BSTR$(A,2),N) !ビットパターン
   LET P(i)=A
NEXT i
MAT PRINT P;


DIM Z(M) !候補は、部分集合A1,A2,…,Atである。
CALL try(1,T,Z,M,K,N,P)

END

EXTERNAL SUB try(s,T,Z(),M,K,N,P()) !バックトラック法で検索する
FOR i=s TO T-(M-s) !組合せで考える C(T,m)通り
   IF s=1 OR (s>1 AND i>Z(s-1)) THEN
      LET Z(s)=i

      LET U=P(Z(1)) !A1∪A2∪ … ∪Am=U(全体集合)、A1∩A2∩ … ∩Am=φ
      FOR j=2 TO s
         IF BITAND(U,P(Z(j)))<>0 THEN EXIT FOR !重複する要素が存在する
         LET U=BITOR(U,P(Z(j)))
      NEXT j
      IF j>s THEN !条件を満たすなら

         IF s=M THEN !全部揃ったら
            FOR x=1 TO M !結果を表示する
               PRINT "("; !1組(e1,e2,…,ek)
               LET W=P(Z(x))
               LET F=0
               FOR e=1 TO N !2進法n桁へ展開する
                  IF MOD(W,2)=1 THEN !ビットが1なら
                     IF F>0 THEN PRINT ",";
                     PRINT STR$(e);
                     LET F=1
                  END IF
                  LET W=INT(W/2) !次の桁へ
               NEXT e
               PRINT ")";
            NEXT x
            PRINT

         ELSE
            CALL try(s+1,T,Z,M,K,N,P) !次へ

         END IF

      END IF

   END IF
NEXT i
END SUB


EXTERNAL SUB Num2CombBit(h, N,R, A) !番号から組合せパターンを生成する ※辞書式順序
LET v=h+1
LET j=R
LET A=0
FOR i=N-1 TO 0 STEP -1 !組合せをビット位置とする
   LET t=COMB(i,j)
   IF v>t THEN
      LET A=A+2^i !ビット位置iを1とする
      LET j=j-1
      LET v=v-t
   END IF
NEXT i
END SUB



実行結果

15
1 : 3 000011
2 : 5 000101
3 : 6 000110
4 : 9 001001
5 : 10 001010
6 : 12 001100
7 : 17 010001
8 : 18 010010
9 : 20 010100
10 : 24 011000
11 : 33 100001
12 : 34 100010
13 : 36 100100
14 : 40 101000
15 : 48 110000
3  5  6  9  10  12  17  18  20  24  33  34  36  40  48

(1,2)(3,4)(5,6)
(1,2)(3,5)(4,6)
(1,2)(4,5)(3,6)
(1,3)(2,4)(5,6)
(1,3)(2,5)(4,6)
(1,3)(4,5)(2,6)
(2,3)(1,4)(5,6)
(2,3)(1,5)(4,6)
(2,3)(4,5)(1,6)
(1,4)(2,5)(3,6)
(1,4)(3,5)(2,6)
(2,4)(1,5)(3,6)
(2,4)(3,5)(1,6)
(3,4)(1,5)(2,6)
(3,4)(2,5)(1,6)

 

戻る