1

ウェブサイトのコードを分析していて、自分の側でも試してみましたが、機能しないようです。理由を教えてください。よろしくお願いします。

ありがとう

Private Sub CommandButton1_Click()
    Dim N, D As Single
    Dim tag As String

    N = Cells(2, 2)
    Select Case N
        Case Is < 2
            MsgBox "It is not a prime number"
        Case Is = 2
            MsgBox "It is a prime number"
        Case Is > 2
            D = 2
            Do
                If N / D = Int(N / D) Then
                    MsgBox "It is not a prime number"
                    tag = "Not Prime"
                    Exit Do
                End If
                D = D + 1
            Loop While D <= N - 1
            If tag <> "Not Prime" Then
                MsgBox "It is a prime number"
            End If
    End Select
End Sub
4

3 に答える 3

6

私が目にする最大の問題は、またはSingleの代わりに使用することです。素数は正の整数であり、10進値のコンテキストでは考えられません(私が知る限り)。したがって、シングルを使用し、それらを分割されたintと比較することにより、厄介なエッジケースのルーティングエラーが発生する可能性があります。IntegerLong

この行If N / D = Int(N / D) Thenは、数値が素数であるかどうかを確認するために不適切な方法を使用しています。浮動小数点数(この場合はシングル)を除数で割るたびに、10進数の余りがある場合、その余りの整数変換は等しくないと想定していますただし、回答を比較しようとすると、浮動小数点数で丸め誤差が発生することがあります。一般に、数値を比較する方法として浮動小数点から整数への変換を使用しないようにすることを学びました。

代わりに試すことができるコードを次に示します。注意すべき点:

  • NとDのタイプを変更して、シングルではなくロングに変更しました。これは、浮動小数点ではなく、丸め誤差が発生する可能性があることを意味します。
  • また、セルの値を明示的にlongに変換しました。このようにして、浮動小数点型を使用していないことがコードでわかります。
  • 比較のために、の余りをで割っModた値を返すを使用しました。余りが0の場合、trueを返し、素数がないことがわかります。(注:剰余は、除算の結果の整数値のみを返す、とともに使用されることがよくあります。 通常、整数型の正確な除算で使用されます。この場合は非常に適切です。ND\Mod\
  • 最後に、比較されている実際の数を表示するようにメッセージボックスを変更しました。セル内の数値は変換されるため、ユーザーが浮動小数点値を入力すると、それが何に変換されたかを確認するのに役立ちます。

また、数億の数に達すると、このコードはコードよりもはるかに高速に実行されることに気付くでしょう。'

Sub GetPrime()
Dim N As Long
Dim D As Long
Dim tag As String

N = CLng(Cells(2, 2))

Select Case N
    Case Is < 2
        MsgBox N & " is not a prime number"
    Case Is = 2
        MsgBox N & " is a prime number"
    Case Is > 2
        D = 2
        Do
            If N Mod D = 0 Then
                MsgBox N & " is not a prime number"
                tag = "Not Prime"
                Exit Do
            End If
            D = D + 1
        Loop While D <= N - 1
        If tag <> "Not Prime" Then
            MsgBox N & " is a prime number"
        End If
End Select
End Sub

注:プロシージャの名前をに変更しましたGetPrime。あなたのコードでは、あなたは持っていました:

Private Sub CommandButton1_Click()

上記の行では、プロシージャメソッドとも呼ばれる、または単にsubと呼ばれることもあります)を定義しています。この単語Subは、値を返さないコードでプロシージャを定義していることを示します。(場合によっては、のFunction代わりに単語が表示されることがありますSub。これは、プロシージャがなどの値を返すことを意味しますPrivate Function ReturnANumber() As Long。)プロシージャ(Sub)は、呼び出されたときに実行されるコードの本体です。Subまた、ExcelマクロはプロシージャとしてVBAに保存されます。

コード行では、はプロシージャCommandButton1_Click()名前です。ほとんどの場合、これはExcelスプレッドシートにボタンを追加することによって自動的に作成されました。ボタンがExcelスプレッドシートに関連付けられている場合、CommandButton1_Click()ボタンが押されるたびに実行されます。

コード内で、プロシージャ の範囲Privateを示します。一般に、プロシージャは、それが存在するモジュールまたはクラスの外部で呼び出すことができないことを意味します。私のコードでは、別のコードモジュールから呼び出したい場合があるため、省略しました。PrivatePrivateGetPrime

コメントで、私のプロシージャの名前をからに変更する必要があると述べましGetPrime()CommandButton1_Click()。それは確かに機能します。ただし、以下のように、内部GetPrimeから単純に呼び出すこともできます。 CommandButton1_Click()

Private Sub CommandButton1_Click()
    'The following line of code will execute GetPrime()  '
    'Since GetPrime does not have parameters and does not return a value, '
    'all you need to do is put the name of the procedure without the ()   '
    GetPrime
End Sub

'Below is the entire code for the Sub GetPrime()    '
Sub GetPrime()
    'The body of the code goes below: '
    ' ... '

End Sub

これがVBAについて少し説明し、理解を深めるのに役立つことを願っています。

于 2009-11-19T16:23:24.577 に答える
2

このコードをどこからコピーしたかはわかりませんが、非常に非効率的です。私がするかもしれない場合:

  1. Dim N, D As LongDがLongになり、Nがバリアントになります。ご存知かもしれませんが、バリアントは利用可能な最も遅いデータ型の1つです。この行は次のようになります。Dim N As Long, D As Long
  2. 偶数は常に2で割り切れるので、 1つおき数をテストするだけで済みます。(したがって、素数になることはできません)。
  3. Nまでテストする必要はありません。Nの平方根までテストするだけで済みます。これは、平方根の後で因子がサイドを切り替えるだけなので、値を再テストしているだけだからです。
  4. Forループはループの存続期間中にFor-Lineを1回だけ評価しますが、DoループとWhileループはすべてのループで条件付きを評価するため、N-1は何度も評価されます。Do Loopを使用する場合は、この値を変数に格納します。

さて、これで何とか、何とか、何とかを省いたので、ここにコードがあります。ExcelからUDFとしても使用できるように構成しました(例:= ISPRIME(A2)):

Option Explicit

Sub GetPrime()
    Dim varValue As Variant
    varValue = Excel.ActiveSheet.Cells(2&, 2&).Value
    If IsNumeric(varValue) Then
        If CLng(varValue) = varValue Then
            If IsPrime(varValue) Then
                MsgBox varValue & " is prime", vbInformation, "Prime Test"
            Else
                MsgBox varValue & " is not prime", vbExclamation, "Prime Test"
            End If
            Exit Sub
        End If
    End If
    MsgBox "This operation may only be performed on an integer value.", vbCritical, "Tip"
End Sub

Public Function IsPrime(ByVal num As Long) As Boolean
    Dim lngNumDiv As Long
    Dim lngNumSqr As Long
    Dim blnRtnVal As Boolean
    ''//If structure is to optimize logical evaluation as AND/OR operators do not
    ''//use short-circuit evaluation in VB.'
    If num = 2& Then
        blnRtnVal = True
    ElseIf num < 2& Then 'Do nothing, false by default.
    ElseIf num Mod 2& = 0& Then 'Do nothing, false by default.
    Else
        lngNumSqr = Sqr(num)
        For lngNumDiv = 3& To lngNumSqr Step 2&
            If num Mod lngNumDiv = 0& Then Exit For
        Next
        blnRtnVal = lngNumDiv > lngNumSqr
    End If
    IsPrime = blnRtnVal
End Function
于 2009-11-20T16:55:24.087 に答える
2

次の変更を加えることで、さらに最適化できます(そして、私の意見では、読みやすくすることができます)。初演:

  • フロートではなく、ロングを使用してください。これにより、速度が大幅に向上します。
  • までチェックする必要はありません。n-1の平方根のみをチェックしnます。これは、dより大きな係数がsqrt(n)存在する場合、その対応する要素n/dがですでに検出されているためです sqrt(n)除数2を計算してオーバーフローしないように、このために特別な変数を使用します。また、ループを通過するたびに平方根を計算するのではなく、1回計算することで速度を上げます(平方根の取得は間違いなく2乗よりも遅くなりますが、1回だけ発生します)。
  • 最初に2の倍数に対して特別なチェックを行い、次に自分の数が奇数の倍数であることを確認するだけで、速度が実質的に2倍になります(2の倍数であるかどうかはチェックしません)。
  • 除算/乗算ではなく、モジュロ演算子を使用します。

今読みやすさ:

  • わかりやすい変数名を使用します。
  • ブール値にはブール値を使用します(のような文字列ではありませんtag)。
  • isPrimeコード間でメッセージを分散させるのではなく、ブール値に基づいてメッセージボックスロジックを一番下に移動します。

これらすべての変更により、次のコードは1秒未満で9桁の素数(795,028,841)を検出できます。実際、最大の31ビットプライム(2,147,483,647)を同時に検出できます。

ベンチマーク(のfor周りに10,000回の反復ループを配置select)に基づくと、私のボックスで31ビットの素数を検出するのに35秒かかります。これは1秒あたり約285回です-うまくいけば、それはあなたにとって十分に速いでしょう:-)

Option Explicit

Public Sub Go()
    Dim number As Long
    Dim divisor As Long
    Dim maxdivisor As Long
    Dim isPrime As Boolean

    number = CLng(Cells(2, 2))
    Select Case number
        Case Is < 2
            isPrime = False
        Case Is = 2
            isPrime = True
        Case Is > 2
            isPrime = True
            If number mod 2 = 0 Then
                isPrime = False
            Else
                maxdivisor = CLng(Sqr(number)) + 1
                divisor = 3
                Do
                    If number mod divisor = 0 Then
                        isPrime = False
                        Exit Do
                    End If
                    divisor = divisor + 2
                Loop While divisor <= maxdivisor
            End If
    End Select
    If isPrime Then
        MsgBox "Number (" & number & ") is prime"
    Else
        MsgBox "Number (" & number & ") is not prime"
    End If
End Sub
于 2009-11-25T02:33:07.247 に答える