第9講 プロシージャの再帰的使用
第10話 トレースによる解説
コード主要部分例
  Sub f(g As Integer)
    Dim i, j, h As Integer
    For i = 0 To n - 1
      If g = 0 Then x(g) = i + 1
      h = 1
      If g > 0 Then
        For j = 0 To g - 1
          If x(j) = i + 1 Then
            h = 0
            Exit For
          End If
        Next
        If h = 1 Then x(g) = i + 1
      End If
      If h = 1 Then
        If g + 1 < n Then
          f (g + 1)
        Else
          cn += 1
          For j = 0 To n - 1
            Console.Write("{0:d} ", x(j))
          Next
          Console.WriteLine()
        End If
      End If
    Next
  End Sub

End Module

この難解なプログラムを解説していきましょう。
テーマ名に、聞いたことのない言葉が登場しています。
トレースとは、コンピュータの動きを一つ一つ追うことです。
3次順列生成でトレースすると、
後5話ぐらい必要になりますし、
煩雑すぎますので、2次順列生成をトレースして、
3次や4次の場合は皆さん自身にやっていただくことにします。
かなり面倒ですが、トレースは大変大切ですから、
必ずやって下さい。

2次順列生成ですから、n=2が以下のトレースの前提となります。
Main()からf(0)でプロシージャf()に仕事が依頼されますから、最初gは0です。
以下
    For i = 0 To n - 1
      ・
    Next
の動きを追います。
i=0の場合、
        If g = 0 Then x(g) = i + 1
 は
        If g = 0 Then x(0) = i + 1
 ですから、if文の条件が満たされ、x(0) = 0+1が実行されてx(0)は1となります。
 次の
      h = 1
      If g > 0 Then
        For j = 0 To g - 1
          If x(j) = i + 1 Then
            h = 0
            Exit For
          End If
        Next
        If h = 1 Then x(g) = i + 1
      End If

  青はgが0ですから実行されません。
  すると、hは1のままですから、
      If h = 1 Then
        If g + 1 < n Then
          f (g + 1)
        Else
          cn += 1
          For j = 0 To n - 1
            Console.Write("{0:d} ", x(j))
          Next
          Console.WriteLine()
        End If
      End If
  すなわち、
      If h = 1 Then
        If 0 + 1 < n Then
          
f (0 + 1)
        Else
          cn += 1
          For j = 0 To n - 1
            Console.Write("{0:d} ", x(j))
          Next
          Console.WriteLine()
        End If
      End If
  の
肯定部分が実施されて、
  f(0)は、自分の分身f(1)を呼び出します。
  このとき、gは1になっています。
    For i = 0 To n - 1
    
  If g = 0 Then x(g) = i + 1
  ですから、青は無視されて、
      h = 1
      If g > 0 Then
        
For j = 0 To g - 1
          If x(j) = i + 1 Then
            h = 0
            Exit For
          End If
        Next

        If h = 1 Then x(g) = i + 1
      End If
 へと進みますが、g=1なので、
 
ピンクが実行されます。
 j=0のとき、
        
For j = 0 To g - 1
          If x(j) = i + 1 Then
            
h = 0
            Exit For

          End If
        Next
        
If h = 1 Then x(g) = i + 1
      End If

      If h = 1 Then
        If g + 1 < n Then
          f (g + 1)
        Else
          cn += 1
          For j = 0 To n - 1
            Console.Write("{0:d} ", x(j))
          Next
          Console.WriteLine()
        End If
      End If

    x(0)は1ですから
          h = 0
          Exit For

  実行されてしまいます。
  こでExit Forは強制的にFor文(繰り返し処理=ループ処理)をやめさせるものですが、
  g=1の時には、Exit Forは意味のない文となります。
  強制的にやめさせなくてもFor文は終了してしまうからです。
  Exit Forが生きるのはg>1のときです。
  さて、hが0となり、ピンクが実行されずに、jが1つ進み、
i=1の場合となります。
      If g = 0 Then x(g) = i + 1
  は
      If g = 0 Then x(0) = i + 1
  ですから実行されずに、
      h = 1
      If g > 0 Then
        For j = 0 To g - 1
          If x(j) = i + 1 Then
            h = 0
            Exit For
          End If
        Next
        If h = 1 Then x(g) = i + 1
      End If
  すなわち
      h = 1
      If 1 > 0 Then
        For j = 0 To 1 - 1
          If x(j) = 1 + 1 Then
            h = 0
            Exit For
          End If
        Next
        If h = 1 Then x(1) = 1 + 1
      End If
  へと進みます。
  j=0のとき、
      h = 1
      If 1 > 0 Then
        For j = 0 To 1 - 1
          If x(0) = 1 + 1 Then
            h = 0
            Exit For
          End If
        Next
        If h = 1 Then x(1) = 1 + 1
      End If
  のx(0)=1+1はx(0)が1なので成り立たずに、
  hは1のままで、
        If h = 1 Then x(1) = 1 + 1
  が実行されx(1)=2となり、
      If h = 1 Then
        If g + 1 < n Then
          f (g + 1)
        Else
          
cn += 1
          For j = 0 To n - 1
            Console.Write("{0:d} ", x(j))
          Next
          Console.WriteLine()

        End If
      End If
  の
が実行されて、はじめてコンソール画面に
12
  が打ち出されます。

ごめんなさい。2次に限定しても煩雑すぎます。
以下はご自分でトレースを行って、実行画面が
12
21

となることを確認して下さい。

さて、n×n次順列を2次元に並び直して、
すべての横合計・縦合計・対角線合計が同じになるものを見いだして、
n次魔方陣を生成するというソフトの開発に取り組みます。
このソフトによって、
対称変換をすると重なるものや鏡像を別のものとして数えると
3次魔方陣は8個、4次魔方陣は7040個の答えを持っていることを、
確認されます。
理論的には何次魔方陣で作れるソフトですが、
現時点では4次が限界ですし、7040個作り出すにも20分ぐらい必要としますが、
本講義の進展と共に作成スピードは数万倍→数億倍→数兆倍→数京倍となり、
26次魔方陣クラスでも1秒で数百の勢いで生成するようになっていきます。

第10講第1話の課題を出します。
なるべくシンプルになるように、
  Dim cn, x(25), n As Integer
とグローバル配列を1つとグローバル変数を2つ用意してしまいましたが、
いずれも引数にすれば、
グローバル変数はすべて追放してローカル変数にすることが出来ます。
f()のMain()とf()からの呼び出しはf(0,n,x,cn)
(順番は任意ですが、Main()とf()からの呼び出しは同じ順に
さらに、Sub f(g As Integer, n As Integer, x() As Integer, cn() As Integer)と()内も同じ順に、
そろえなければなりません。)

という形になります。
ただし、xに加えてcnも配列にしておくと良いでしょう。
    Dim cn(0),x(25) As Integer
cnを配列とすることによって、値ではなく箱(変数)そのものを実質送ったのと同じになりましたね。
もっとも、f()をFunctionプロシージャにして、cnの値を返させる方法もありますが、
こちらの方がやや複雑になります。
でも、好きな方を採用して下さい。


グローバル変数(プログラム全体で有効な変数)は使わずに、
ローカル変数(プロシージャ内のみで有効な変数)のみでプログラムを組むが、
プログラミングの原則です。

第9話へ   第10講第1話へ

002

初心者のための excel 2016 マクロ VBA 入門講義 基礎から応用まで
vc** c言語 c** 入門 初心者 基礎から応用まで
eclipse c** 入門
魔方陣 数独で学ぶ VBA 入門

数独のシンプルな解き方・簡単な解法の研究
VB講義へ
VB講義基礎へ
初心者のための世界で一番わかりやすいVisual C**入門基礎講座
初心者のための世界で一番わかりやすいVisual Basic入門基礎講座
初心者のための世界で一番わかりやすいVBA入門講義(基礎から応用まで)
初心者のための VC**による C言語 C++ 入門 基礎から応用まで第1部
eclipse java 入門
java 入門 サイト 基礎から応用まで
本サイトトップへ