疑問

 投稿日:2008年11月16日(日)07時09分52秒
  1から3を   1 3
         2
と配列すれば、上の段の2数の差が(ただし大きい方から小さい方を引く。3-1)
下の数となる。
この規則を敷衍し
1から6までの数字を一度だけ使用して
       ● ● ●
        ● ●
         ●
の位置に入れたい。
試行錯誤の後、6 2 5
        4 3
         1
なる配列が(もちろん他のパターンも存在すると思う。)求められる。
しかし、
次からが人間には限界が出てきて、
では、1~10の数字を一度だけ使用して
      ● ● ● ●
       ● ● ●
        ● ●
         ●
の配列を構成
さらに、1~15の数字で
     ● ● ● ● ●
      ● ● ● ●
       ● ● ●
        ● ●
         ●
1~21で
    ● ● ● ● ● ●
     ● ● ● ● ●
      ● ● ● ●
       ● ● ●
        ● ●
         ●
・・・・
・・・・
は構成可能なのだろうか?
この問題を解決してもらいたい。
 

Re: 疑問

 投稿者:荒田浩二  投稿日:2008年11月16日(日)12時05分29秒
  > No.84[元記事へ]

GAIさんへのお返事です。

n=10では存在しました。

  9  10   3   8
    1   7   5
      6   2
        4

次のプログラムで発見しましたが、少し工夫すれば全数調査も可能ではないかと思います。

LET k=4  ! 4行
LET n=k*(k+1)/2  ! n=10
DIM a(k,k),check(n)
FOR maxn=1 TO CEIL(k/2)
   FOR r=1 TO n^(n/2)
      MAT check=ZER
      LET a(1,maxn)=n
      LET check(n)=1
      FOR i=1 TO k
         FOR j=1 TO k+1-i
            IF NOT (i=1 AND j=maxn) THEN
               DO
                  LET num=INT((n-1)*RND)+1
               LOOP UNTIL check(num)=0
               LET a(i,j)=num
               LET check(num)=1
            END IF
         NEXT j
      NEXT i
      !MAT PRINT a
      CALL diff
      IF p=0 THEN MAT PRINT a
      LET p=0
   NEXT r
NEXT maxn
SUB diff
   FOR i=1 TO k-1
      FOR j=1 TO k-i
         IF ABS(a(i,j)-a(i,j+1))<>a(i+1,j) THEN
            LET p=1
            EXIT SUB
         END IF
      NEXT j
   NEXT i
END SUB
END
 

Re: 疑問

 投稿者:山中和義  投稿日:2008年11月17日(月)09時14分59秒
  > No.84[元記事へ]

GAIさんへのお返事です。

●サンプル その1
前回紹介したバックトラック法による総当りです。
K段の場合、K*(K+1)/2の階乗になります。(K=5、15!=1,307,674,368,000)

この手のパズルはバックトラック法で解けます(時間はかかる)ので、ぜひマスターしてみてください。
PUBLIC NUMERIC ANSWER_COUNT !解答数
LET ANSWER_COUNT=0

LET K=5 !段数 ※1~

LET N=K*(K+1)/2 !1~Kまでの数字
DIM A(N)
MAT A=ZER

LET t0=TIME
PRINT K;"段"
PRINT "1 ~";N;"までの数字"
CALL backtrack(1,K,N,A)
IF ANSWER_COUNT=0 THEN PRINT "解なし"
PRINT "計算時間=";TIME-t0

END


EXTERNAL SUB backtrack(p,K,N,A())
FOR nm=1 TO N
   LET A(p)=nm !仮に設定してみる

   CALL checkrule(p,nm,N,A, rc) !条件を満たすなら
   IF rc=1 THEN

      IF p=N THEN !すべて埋まったら
         LET ANSWER_COUNT=ANSWER_COUNT+1
         PRINT ANSWER_COUNT;"個目"

         FOR i=K TO 1 STEP -1 !上段から
            PRINT REPEAT$(" ",K-i); !右へシフト
            FOR j=1 TO i !この段の数字の数
               PRINT USING "###": A((i-1)*i/2+j);
            NEXT j
            PRINT
         NEXT i
         !!!MAT PRINT A;

      ELSE
         CALL backtrack(p+1,K,N,A) !次へ

      END IF

   END IF
   LET A(p)=0 !元に戻す
NEXT nm
END SUB


EXTERNAL SUB checkrule(p,nm,N,M() ,rc) !条件が満たすかどうか確認する
LET rc=0

!●数字は重複していないか?
FOR i=1 TO p-1
   IF nm=M(i) THEN EXIT SUB !見つかったのでNG!
NEXT i

!●上段の2数の差?
!M(p)の添え字番号と配置
!11 12 …   5段目 s=11
! 7 8 9 10  4段目 s=7
!  4 5 6   3段目 s=4
!  2 3    2段目 s=2
!   1    1段目 s=1
!
!M(p-1) M(p)  x段目
!  M(p-x)

LET a=1/2 !段数を得る ※xの2次方程式(x-1)*x/2+1-p=0の解
LET b=-1/2
LET c=1-p
LET D=b^2-4*a*c !判別式 ※この解は実数のみ
LET x=INT((-b+SQR(D))/(2*a))

IF x>1 THEN !2段目以降なら
   IF p>(x-1)*x/2+1 THEN !この段の2列目以降なら
      IF ABS(M(p-1)-M(p))<>M(p-x) THEN EXIT SUB !不成立なのでNG!
   END IF
END IF

!●左右対称
IF p=N AND M(p)<M(p-x+1) THEN EXIT SUB !上段の左端と右端

LET rc=1 !OK!
END SUB
(実行結果)
 5 段
1 ~ 15 までの数字
 1 個目
  6 14 15  3 13
   8  1 12 10
    7 11  2
     4  9
      5
計算時間= 36.36
※WindowsME、CPU PentiumⅢ700MHzにて
 

Re: 疑問

 投稿者:山中和義  投稿日:2008年11月17日(月)10時46分44秒
  > No.88[元記事へ]

GAIさんへのお返事です。

●サンプル その2
K段の場合、使用する数字は1~N=K*(K+1)/2となる。
上段の数字のみが自由に設定できる。ただし、重複はしない。
この上段の数字列は、順列P(N,K)で決めることができる。
それによって、下段は階差として順に決まる。
このとき、数字の重複がないか確認する。

K=3なら、N=3*(3+1)/2=6。
上段 2 4 5 とすると

 2 4 5
  2 1 ←上段2つの差
  1 ←上段2つの差

たとえば、2段目1番目の2が重複しているため、NGとなる。

2段 3P2=          6
3段 6P3=        120
4段 10P4=     5,040
5段 15P5=   360,360
6段 21P6=39,070,080 ←かなりキツイ
 :
の数を確認する。
PUBLIC NUMERIC ANSWER_COUNT !解答数
LET ANSWER_COUNT=0

LET K=5 !段数 ※1~

LET N=K*(K+1)/2 !1~Nまでの数字
DIM F(N),FF(N),B(K),BB(K)
MAT F=ZER
MAT B=ZER

LET t0=TIME

PRINT K;"段"
PRINT "1 ~";N;"までの数字"
CALL perm(1,N,K, F,B,FF,BB)
IF ANSWER_COUNT=0 THEN PRINT "解なし"

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

END


EXTERNAL SUB perm(p,N,R, F(),B(),FF(),BB()) !順列nPrを生成する
FOR nm=1 TO N !上段のK個の数を決める

   IF F(nm)=0 THEN !数字は重複なしに埋める

      LET F(nm)=1 !使用中とする
      LET B(p)=nm !仮に設定してみる

      IF p=R THEN !すべて埋まったら
         CALL checkrule(R,F,B,FF,BB, rc) !条件を満たすなら
         IF rc=1 THEN

            LET ANSWER_COUNT=ANSWER_COUNT+1
            PRINT ANSWER_COUNT;"個目"

            MAT BB=B
            FOR j=1 TO R !上段
               PRINT USING "###": BB(j);
            NEXT j
            PRINT
            FOR i=R-1 TO 1 STEP -1 !下段へ
               PRINT REPEAT$(" ",R-i); !右へシフト
               FOR j=1 TO i !この段の数字の数
                  LET BB(j)=ABS(BB(j)-BB(j+1))
                  PRINT USING "###": BB(j);
               NEXT j
               PRINT
            NEXT i

         END IF
      ELSE
         CALL perm(p+1,N,R, F,B,FF,BB) !次へ
      END IF

      LET B(p)=0 !元に戻す
      LET F(nm)=0

   END IF

NEXT nm
END SUB


EXTERNAL SUB checkrule(K,F(),B(),FF(),BB(), rc) !条件が満たすかどうか確認する
LET rc=0

!●左右対称
IF B(1)>B(K) THEN EXIT SUB !上段の左端と右端

!●上段の2数の差?
MAT BB=B !作業配列へ
MAT FF=F
FOR x=K-1 TO 1 STEP -1 !x段目
   FOR j=1 TO x !1つ下の段
      LET p=ABS(BB(j)-BB(j+1))
      IF FF(p)=1 THEN EXIT SUB !数字が重複していないか
      LET BB(j)=p
      LET FF(p)=1 !使用中とする
   NEXT j
NEXT x

LET rc=1 !OK!
END SUB

(実行結果)
●  5 段
1 ~ 15 までの数字
 1 個目
  6 14 15  3 13
   8  1 12 10
    7 11  2
     4  9
      5
計算時間= 23.13  ←多少早くなった


●  6 段
1 ~ 21 までの数字
解なし
計算時間= 2978.17
※WindowsME、CPU PentiumⅢ700MHzにて



●改良案
数字Nは必ず上段に存在する。この配置はK通り。
残りK-1箇所に残りの数字を埋める順列はP(N-1,K-1)となる。
これによって、

2段 2×2P1=             4
3段 3×5P2=            30
4段 4×9P3=         2,016
5段 5×14P4=      120,120
6段 6×20P5=   11,162,880
7段 7×27P6=1,491,890,400 ←かなりキツイ

となり、7段の求解が現実となる?!
 

戻る