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代替Integeror Long。素数是正整数,不考虑十进制值(据我所知)。因此,通过使用单数并将它们与划分的整数进行比较,您将面临令人讨厌的边缘情况路由错误。

该行If N / D = Int(N / D) Then正在使用一种糟糕的方法来查看数字是否为素数。假设每次将浮点数(在本例中为单数)除以除数时,如果它有小数余数,则该余数的整数转换将不相等。但是,在尝试比较答案时,我有时会遇到浮点数的舍入错误,一般来说,我学会了避免使用浮点到 int 的转换作为比较数字的一种方式。

这是您可以尝试的一些代码。需要注意的一些事项:

  • 我改变了 N 和 D 的类型,使它们是 Longs 而不是 Singles。这意味着它们不是浮点数并且可能存在舍入误差。
  • 我还明确地将单元格值转换为 long。这样您就可以在代码中知道您没有使用浮点类型。
  • 为了比较,我使用Mod了 ,它返回N除以的余数D。如果余数为 0,则返回 true,并且我们知道我们没有素数。(注:余数常与 配合使用,只返回除法结果的\整数值。 常用于整数类型的精确除法,在这种情况下非常合适。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) 是在调用时将执行的代码体。另外值得注意的是,excel 宏作为Sub过程存储在 VBA 中。

在您的代码行中,CommandButton1_Click()是过程的名称。这很可能是通过在 Excel 电子表格中添加一个按钮自动创建的。如果按钮绑定到 Excel 电子表格,CommandButton1_Click()则每次按下按钮时都会执行。

在您的代码中,Private指示过程的范围Private通常意味着不能在它所在的模块或类之外调用该过程。在我的代码中,我省略了,Private因为您可能想GetPrime从不同的代码模块调用。

您在评论中提到您必须将我的程序名称从 更改GetPrime()CommandButton1_Click()。这当然有效。但是,您也可以简单地GetPrimeinside 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 Long将导致 D 成为 Long,而 N 成为变体。您可能知道,变体是可用的最慢的数据类型之一。这一行应该是:Dim N As Long, D As Long
  2. 您只需要测试每隔一个数字,因为偶数总是可以被 2 整除。(因此不可能是素数)。
  3. 您不需要一直测试到 N。您只需要测试到N 的平方根。这是因为在平方根之后,因子只是换边,所以你只是在重新测试值。
  4. For 循环仅在循环的生命周期内评估 For-Line 一次,但 Do 和 While 循环在每个循环上评估它们的条件,因此 N-1 被评估很多次。如果要使用 Do 循环,请将此值存储在变量中。

好的,所以现在我们已经免除了废话,废话,废话,这里是代码。我对其进行了结构化,因此您也可以将其用作 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来溢出。它还通过计算一次而不是每次通过循环计算平方来加速它(即使获得平方根无疑比平方慢,它只发生一次)。
  • 首先对二的倍数进行特殊检查,然后您只需要检查您的数字是否是奇数的倍数,从而有效地加倍速度(不检查您是否是二的倍数)。
  • 使用模运算符而不是除法/乘法。

现在可读性:

  • 使用描述性变量名称。
  • 对布尔值使用布尔值(而不是类似 的字符串tag)。
  • 根据isPrime布尔值将消息框逻辑向下移动到底部,而不是将消息分散在代码中。

通过所有这些更改,以下代码可以在不到一秒的时间内检测到 9 位素数 (795,028,841)。事实上,我们可以同时检测到最大的 31 位素数 (2,147,483,647)。

基于基准(在 .for周围放置一个 10,000 次迭代循环select),我的盒子需要 35 秒才能检测到 31 位素数。这大约是每秒 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 回答