江戸時代の問題

 投稿者:GAI  投稿日:2010年 2月 6日(土)13時03分33秒
  大きな樽に油が32升入っている。
10升枡3つと7升枡1つで、32升を8升ずつ4つに分けよ。

これは藤岡茂之著「算之記」(1657年)にある問題だそうで、途中まで経過が記してあるが途中の経過が省略されて紹介されていました。
いろいろ試していましたが、根気負けの状態です。
ぜひ最短手数を調べて下さい。


32升枡   10升枡   10升枡   10升枡    7升枡
 32      0      0      0      0
  5     10     10      0      7
  5     10     10      7      0
  5      3     10      7      7
  0      8     10      7      7
・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・
・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・
・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・
  8      8      8      8      0
 

Re: 江戸時代の問題

 投稿者:山中和義  投稿日:2010年 2月 6日(土)20時38分55秒
  > No.1011[元記事へ]

GAIさんへのお返事です。

> ぜひ最短手数を調べて下さい。

分量は32,10,10,10,7の順
解= 26 手
 26:  8  8  8  8  0
 25:  1  8  8  8  7
 24:  1  8  8 10  5
 23: 11  8  8  0  5
 22: 11  8  8  5  0
 21:  4  8  8  5  7
 20:  4  8  8 10  2
 19: 14  8  8  0  2
 18: 14  8  8  2  0
 17:  7  8  8  2  7
 16:  7  8  8  9  0
 15:  0  8  8  9  7
 14:  0  8  8 10  6
 13:  8  8  0 10  6
 12:  8  8  6 10  0
 11:  8  1  6 10  7
 10:  8  7  6  4  7
  9: 15  7  6  4  0
  8: 15  0  6  4  7
  7: 15  0 10  4  3
  6: 15  3 10  4  0
  5: 15  3 10  0  4
  4: 15  3  7  0  7
  3: 15 10  7  0  0
  2: 15 10  0  0  7
  1: 22 10  0  0  0
  0: 32  0  0  0  0
 

Re: 江戸時代の問題

 投稿者:GAI  投稿日:2010年 2月 7日(日)06時27分57秒
  > No.1012[元記事へ]

山中和義さんへのお返事です。


お見事です。!!!
これはどうやって探せるんですか?
もしプログラムでやられたら是非拝見させて下さい。
 

Re: 江戸時代の問題

 投稿者:山中和義  投稿日:2010年 2月 7日(日)09時43分40秒
  > No.1013[元記事へ]

GAIさんへのお返事です。

> これはどうやって探せるんですか?
> もしプログラムでやられたら是非拝見させて下さい。


手作業と同様に手を重ねて、樹形図を作成します。
1つ前の手から、今回の手20通り(Perm(5,2))をすべて展開していきます。
ただ、「元に戻る」、「以前の手」などの展開はしません。

樹形図の例
(32,0,0,0,0)
  →(22,10,0,0,0)
    →(12,10,10,0,0)
    →(12,10,0,10,0)
    →(15,10,0,0,7)
    →(22,3,0,0,7)
  →(22,0,10,0,0)
    →(12,0,10,10,0)
    →(15,0,10,0,7)
    →(22,0,3,0,7)
  →(22,0,0,10,0)
      :
  →(25,0,0,0,7)
      :

この樹形図は、「(単方向)リスト」と呼ばれるデータ構造で、BASICにはありません。
そこで、配列変数Q(,)を使って実装しています。

終了パターンが見つかると、この矢印を遡りその過程を表示します。

サンプル・プログラム ※2進モードで実行のこと
!油分け算
! 32リットルの容器に水が一杯に満たされている。
! この容器とは別に、10リットルの空の容器が3つ、7リットルの空の容器が1つある。
! これらの容器を用いて、8リットルずつに分けたい。どうしたらよいか?

LET C=5 !容器の数 ※←←←←←
!LET C=4
!LET C=3

DIM B(C) !容器の容量 ※←←←←←
DATA 32,10,10,10,7
!DATA 21,11,8,5
!DATA 10,7,3
MAT READ B

! 1. 容器いっぱいに水を満たす
! 2. 容器を空にする
! 3. 他の容器に水を移す
DIM W(C)
SUB move(x,y) !xからyへ移動する
   LET t=MIN(W(x),B(y)-W(y)) !分量を算出する
   LET W(x)=W(x)-t
   LET W(y)=W(y)+t
END SUB

DIM Q(0 TO 8000,0 TO C) !配分のパターン(局面) ※樹形図
MAT Q=ZER
DATA 32,0,0,0,0 !容器の初期状態 ※←←←←←
!DATA 21,0,0,0
!DATA 10,0,0
FOR i=1 TO C
   READ Q(0,i)
NEXT i
LET Q(0,0)=-1 !単方向リスト ※番兵

DIM G(C) !最終パターン ※←←←←←
DATA 8,8,8,8,0
!DATA 7,7,7,0
!DATA 5,5,0
MAT READ G

LET top=0 !n手目の展開
LET btm=0
LET n=0
DO WHILE n<=30 !手の上限 ※幅優先探索
   PRINT n; top;btm !debug

   LET new_btm=btm

   FOR i=top TO btm !(n+1)手目へと展開する
      DIM M(C)
      FOR k=1 TO C !元パターンを復元する
         LET M(k)=Q(i,k)
      NEXT k

      FOR h=0 TO Perm(C,2)-1 !移動は高々20通り
         DIM P(2)
         CALL Num2Perm(h,P,C,2)
         MAT W=M !restore it
         CALL move(P(1),P(2)) !移動してみる

         FOR x=new_btm TO 0 STEP -1 !最新のものから順に、既出のパターンかどうか確認する
            FOR k=1 TO C
               IF W(k)<>Q(x,k) THEN EXIT FOR
            NEXT k
            IF k>C THEN EXIT FOR !一致する
         NEXT x
         IF x<0 THEN !新しい局面なら、登録する

            LET new_btm=new_btm+1 !登録位置を算出する
            FOR k=1 TO C !パターン
               LET Q(new_btm,k)=W(k)
            NEXT k
            LET Q(new_btm,0)=i !リンクする


            FOR k=1 TO C !終了パターンかどうか確認する
               IF W(k)<>G(k) THEN EXIT FOR
            NEXT k
            IF k>C THEN !解を表示する

               LET d=n+1
               PRINT "解=";d;"手"
               LET x=new_btm !記録を元に「手」を遡る
               DO UNTIL x<0
                  PRINT USING "###:": d;
                  FOR k=1 TO C
                     PRINT USING "###": Q(x,k);
                  NEXT k
                  PRINT

                  LET x=Q(x,0) !後方へ
                  LET d=d-1
               LOOP

               LET new_btm=new_btm-1 !他を探すため登録しない
               !!!STOP

            END IF

         END IF
      NEXT h

   NEXT i

   LET n=n+1 !次へ
   LET top=btm+1
   LET btm=new_btm
LOOP

END


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


!アルゴリズム、パズル、幅優先探索、単方向リスト
 

戻る