第12講 Functionプロシージャの再帰的使用の学習

第6話 Functionプロシージャの再帰的使用によって素数判定を行う
シート例
素数判定シート
コード例
Private Sub CommandButton1_Click()

  Dim n As Long
  n = Cells(2, 4)

  If n = 2 Then
    Cells(5, 2) = "素数である"
    Exit Sub  '判定が終了し表示も済んでいるのでのでPrivate Sub CommandButton1_Clickを強制的に終了している。
  End If
  ' 偶数ははじめから素数でない。
  If n Mod 2 = 0 Then
    Cells(5, 2) = "素数でない"
    Exit Sub  '判定が終了し表示も済んでいるのでPrivate Sub CommandButton1_Clickを強制的に終了している。
  End If
  If f(n, 3) = 1 Then
    Cells(5, 2) = "素数である"
  Else
    Cells(5, 2) = "素数でない"
  End If

End Sub

Function f(n As Long, g As Long)

  If g * g > n Then
    f = 1
    Exit Function  '判定が終了しているのでfを強制的に終了している。
  End If
  If n Mod g <> 0 Then
    f = f(n, g + 2)
  Else
    f = 0
  End If

End Function

Private Sub CommandButton2_Click()

  Range("D2,B5").Select
  Selection.ClearContents
  Cells(1, 1).Select

End Sub

解説
g * g > n の意味は、gがnの平方根を超えたら判定をやめなさいです。
整数は、平方根より大きい整数で割りきれないのは明らかです。
例 25は25未満の最大の約数は25の平方根の5です。ですから、6では割り切れません。
  同様に27の平方根は、5.19615242270663でこれを超える6では割り切れませんね。

f(n, g + 2)の g + 2 の意味は、2以外の素数は奇数ですから、奇数のみで割って余りが出るかを判定するばよいわけです。

さて、これを改良して素数列挙版に変更しましょう。
シート例
素数列挙型シート 
実行画面例
実行結果全体

実行結果詳細
さて、コードを考えてください。コード例は、30行下。


















コード例
Private Sub CommandButton1_Click()

  Dim i As Long, n As Long, cn As Long

  n = Cells(2, 4)

  Cells(4, 1) = 2
  cn = 1
  For i = 3 To n
    If i Mod 2 = 1 Then
      If f(i, 3) = 1 Then
        Cells(4 + Int(cn / 20), 1 + (cn Mod 20)) = i
        cn = cn + 1
      End If
    End If
  Next

  Cells(5 + Int(cn / 20), 1) = "素数個数"
  Cells(5 + Int(cn / 20), 2) = cn

End Sub

Function f(n As Long, g As Long)

  If g * g > n Then
    f = 1
    Exit Function
  End If
  If n Mod g <> 0 Then
    f = f(n, g + 2)
  Else
    f = 0
  End If

End Function

Private Sub CommandButton2_Click()

  Rows("4:300000").Select
  Selection.ClearContents
  Range("D2").Select
  Selection.ClearContents
  Cells(1, 1).Select

End Sub

では、Functionプロシージャの再帰的使用によるプログラムの最後の課題です。
10進数の整数を希望進数に翻訳するプログラムを考えましょう。
シート例
n進数翻訳マクロシート
実行画面例
n進数翻訳マクロ結果
D2とB4は、そのセル上で右クリック→セルの書式設定→表示形式→数値が選んください。

考え方を説明します。
115を2進数に翻訳するには、
115÷2=57・・・
57÷2 =28・・・
28÷2 =14・・・
14÷2 =7 ・・・
7÷2  =3 ・・・
3÷2  =1 ・・・
1÷2  =0 ・・・
と2でどんどん割っていき、余りを逆順に(下から)並べると1110011
と答えになります。
つまり、商が0になるまで遡及していけばよいわけです。
尚、今回のFunctionの戻り値はString型(文字型)ですので、
遡及していって最後に現れるfの正体は、f = ""です。
最後まで遡及していったら、無だったとは何とも不思議な話です。

果たして意味があるのでしょうか。

C言語あたりでは、string f(int a,int n)などと明示的に戻り値の型を示しますが、
VBやVBAの場合Function f(a As Long, n As Byte)の形を見ればお分かりになるように、
戻り値の型が指定されていません。では、どのように戻り値の型を判断しているのでしょうか。
それは、一番最初に代入されたもので型を判断します。
ですから、f = ""は意味がないように見えますが、実は戻り値の型がString型(文字型)であるという重要な情報を与えています。
つまり、遡及の末にわかったfの正体は文字型だった(今までの例はすべて整数型でしたね)のです。

重要な説明を落としていました。素数判定、
Function f(n As Long, g As Long)

  If g * g > n Then
    f = 1
    Exit Function
  End If
  If n Mod g <> 0 Then
    f = f(n, g + 2)
  Else
    f = 0
  End If

End Function
でもはじめてfに値が代入されるのは、一番内側の人形にたどり着いたときです。
これは、累乗のプログラム
Function f(n As Long)

  If n = 1 Then f = 1
  If n - 1 > 0 Then
    f = n * f(n - 1)
  End If

End Function
でも同じです。一番内側の人形にたどり着いて、はじめてIf n = 1 Then f = 1 によって代入されているのです。
f = n * f(n - 1)への代入は、内側から2番目以降において代入されます。
なぜなら、
  If n - 1 > 0 Then
    f = n * f(n - 1)
  End If
nが1にになるまでは遡及の旅路にあります。つまり、自分の一番内側への旅が続いているのです。
旅の途上では、代入操作は一切行われていません。
一番内側の人形にたどり着いてはじめて代入 f = 1 が行われるのです。
そして、一番内側の人形から2番目の人形に戻ってきたときにはじめて、
2番目の人形世界において代入f = n * f(n - 1)が実行されるのです。
そして、2番目から3番目に戻ってきたとき、
3番目の人形世界において代入f = n * f(n - 1)が遂行されます。
内側から2番目以降においては、自分より内側の人形から戻ってきたときにはじめて代入されるのです。
つまり、内側から2番目以降においては逆遡及上で代入が行われるのです。



余りは、整数型ですので強制的に文字型に変更する必要がありますので、
f = f + CStr(a Mod n)
としてください。CStr(・・)は、括弧の中のデータを文字型に変更する命令です。
では、皆さんコードを考えてみましょう。


第5話へ 第7話へ

004
  


VBA講義第1部へ
vc++講義へ
vb講義へ
VB講義基礎へ
初心者のための世界で一番わかりやすいVisual C++入門基礎講座へ
初心者のための世界で一番わかりやすいVisual Basic入門基礎講座へ

数学研究室に戻る