素数かどうかを判定するFunction

素数かどうかを判定するFunction

久しぶりの投稿。

素数かどうかを判定するメソッドを自作してみた。

その前に前回の記事の恥ずかしい誤りを訂正せえよ、と思っていますが。

コード

ごちゃごちゃなしで、書いたコードをぶちまける。

リスト1 標準モジュール
Option Explicit

Private Const NOT_NATURAL_NUMBER As String = _
  "引数は自然数でなければいけません。"

Public Function isPrimeNumber( _
            ByVal targetNumber As Long) As Boolean
  isPrimeNumber = False
  If targetNumber < 1 Then _
    Call Err.Raise(Number:=10001, _
                   Source:="Arg is not natural number.", _
                   Description:=NOT_NATURAL_NUMBER)
  If targetNumber = 1 Then Exit Function
  If targetNumber = 2 Then GoTo Finalizer  '……(*)'
  Dim turningPoint As Long
  turningPoint = CLng(targetNumber / 2)
  Dim i As Long
  For i = 2 To turningPoint    '……(**)'
    If targetNumber Mod i = 0 Then Exit Function
  Next
Finalizer:
  isPrimeNumber = True
End Function

(*)では、引数が「2」のときに「True」を返すように、

If targetNumber = 2 Then GoTo Finalizer

と書いたが、実はこの1行がなくても、「isPrimeNumber(2)」は「True」を返す。

引数targetNumberが「2」のとき、変数turningPointの値はForループの初期値「2」よりも小さくなるのだが、ステップ実行してみると、(**)の

For i = 2 To turningPoint
  If targetNumber Mod i = 0 Then Exit Function
Next

ループに突入しないのだ。

Forループの開始値が終了値よりも大きいときにStepを抜かすとコンパイルエラーになると思い込んでいたので、これは意外だった。

ただ、だからといって(*)を書かないと、後で見たときにわけがわからなくなると思ったので、冗長ではあるが明示しておくことにした。

実験

次のコードで動作確認。

スト2 標準モジュール
Public Sub testIsPrimeNumber()
  Dim i As Long
  For i = 1 To 100
    Debug.Print i & vbTab & ":" & isPrimeNumber(i)
  Next
End Sub

こいつを実行すると、

f:id:akashi_keirin:20190530074834j:plain

こうなった。

おわりに

素数判定プログラムなんて、太古の昔から先人たちが色んなアルゴリズムを考えて洗練されまくっているだろうから、後で調べてみる。

車輪を再発明した後、既に発明済みの車輪を見て学ぶ、というのは良い方法だと思う。

ちなみに、今回なぜこんなことをしようと思ったのかというと、Twitterで結婚式の祝儀に3万11円を包む、というのを見かけたから。素数ゆえ「割れない」ということで。

この話を同僚にしようとしたものの、「アレ、30011円だっけ? 30001円だっけ?」となったので、「素数かどうかを判定するメソッドを書いてみようか」となったわけ。

f:id:akashi_keirin:20190530074838j:plain

30011円だと割れなくて、30001円だと割れる(意外じゃないっすか?)ので、気をつけましょう。