VBA Excel 2003 中的素数
我正在分析网站上的代码,我也在我这边尝试过,但似乎不起作用。你能告诉我为什么吗?将非常感谢您的帮助。
谢谢
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
I'm analyzing a code from the website and I tried it on my side as well but seems it doesn't work. Could you please tell me why? would greatly appreciate your help.
Thanks
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
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
我发现的最大问题是使用
Single
而不是Integer
或Long
。素数是正整数,不考虑十进制值(据我所知)。因此,通过使用单精度并将它们与除法整数进行比较,您将面临令人讨厌的边缘情况路由错误。If N / D = Int(N / D) Then
行使用了一种糟糕的方法来查看数字是否为素数。 假设每次将浮点数(在本例中为单数)除以除数时,如果它有小数余数,则该余数的整数转换将不相等。然而,在尝试比较答案时,有时我会遇到浮点数的舍入错误,一般来说,我学会了避免使用浮点到 int 转换作为比较数字的方式。您可以尝试以下一些代码。需要注意的一些事项:
Mod
,它返回N
除以D
的余数。如果余数为 0,则返回 true,我们知道没有质数。 (注:余数常与\
一起使用,仅返回除法结果的整数值。常用的是Mod
和\
整数类型的精确划分,在这种情况下非常合适,数字达到数亿时,此代码的运行速度比您的代码快很多。
'
注意:我将过程的名称更改为
GetPrime
。在您的代码中,您有:在上面的行中,您正在定义一个过程(也称为方法或有时简称为子 >)。
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()
。这当然有效。但是,您也可以简单地从CommandButton1_Click()
内调用GetPrime
,如下所示:希望这有助于解释一些有关 VBA 的内容加深你的理解!
The single biggest problem I see is using
Single
instead ofInteger
orLong
. Primes are positive integers and are not thought of in the context of decimal values (as far as I know). Thus by using a singles and comparing them to divided ints, you're opening yourself up to nasty edge-case rouding errors.The line
If N / D = Int(N / D) Then
is using a poor method to see whether or not the numbers are prime. It's assuming that every time you divide a floating point number (in this case, the single) by the divisor, if it has a decimal remainder, then the integer conversion of that remainder will not be equal. However, I've run into rounding errors sometimes with floating point numbers when trying to compare answers, and in general, I've learned to avoid using floating point to int conversions as a way of comparing numbers.Here's some code you might try instead. Some things to note:
Mod
, which returns the remainder of theN
divided byD
. If the remainder is 0, it returns true and we know we don't have a prime. (Note: Remainder is often used with\
, which only returns the integer value of the result of the division.Mod
and\
are commonly used in precise division of integer types, which in this case is very appropriate.You'll probably also note that this code runs a lot faster than your code when you get to high numbers in the hundreds of millions.
'
NOTE: I changed the name of the procedure to be
GetPrime
. In your code, you had:In the line above, you are defining a procedure (also called a method or sometimes just referred to as a sub). The word
Sub
indicates you are defining a procedure in code that returns no value. (Sometimes you might see the wordFunction
instead ofSub
. This means the procedure returns a value, such asPrivate Function ReturnANumber() As Long
.) A procedure (Sub
) is a body of code that will execute when called. Also worth noting, an excel macro is stored in VBA as aSub
procedure.In your line of code,
CommandButton1_Click()
is the name of the procedure. Most likely, this was created automatically by adding a button to an Excel spreadsheet. If the button is tied to the Excel spreadsheet,CommandButton1_Click()
will execute each time the button is pressed.In your code,
Private
indicates the scope of the procedure.Private
generally means that the procedure cannot be called outside of the module or class in which it resides. In my code, I left outPrivate
because you may want to callGetPrime
from a different module of code.You mentioned in your comments that you had to change the name of my procedure from
GetPrime()
toCommandButton1_Click()
. That certainly works. However, you could also have simply calledGetPrime
from withinCommandButton1_Click()
, like below:Hopefully this helped to explain a little bit about VBA to further your understanding!
我不确定你从哪里复制这段代码,但它的效率非常低。如果我可以:
Dim N, D As Long
将导致 D 为 Long,N 为变体。如您所知,变体是可用的最慢的数据类型之一。这一行应该是:Dim N As Long, D As Long
好的,现在我们已经省去了废话,废话,废话,这是代码。我对其进行了结构化,以便您也可以将其用作 Excel 中的 UDF(例如:=ISPRIME(A2)):
I'm not sure where you copied this code from, but it's terribly inefficient. If I may:
Dim N, D As Long
will cause D to be a Long, and N to be a variant. As you may know, variants are one of the slowest data types available. This line should be:Dim N As Long, D As Long
Ok, so now that we have dispensed with the blah, blah, blah, here is the code. I structured it so you can use it as a UDF from Excel as well (Ex: =ISPRIME(A2)):
您可以通过进行以下更改来进一步优化它(在我看来,使其更具可读性)。第一个表现:
n-1
,只需检查n
的平方根。这是因为,如果存在大于sqrt(n)
的因子d
,则其对应的n/d
已经在下找到< /em>sqrt(n)
.我们为此使用一个特殊变量,这样我们就不会因计算除数2而溢出。它还通过计算一次而不是每次通过循环计算平方来加速它(尽管获得平方根无疑比平方慢,但它只发生一次)。现在可读性:
tag
这样的字符串)。isPrime
布尔值,将消息框逻辑移至底部,而不是将消息分散在代码中。经过所有这些更改,以下代码可以在不到一秒的时间内检测到 9 位素数 (795,028,841)。事实上,我们可以同时检测到最大的 31 位素数(2,147,483,647)。
根据基准测试(在
select
周围放置 10,000 次迭代for
循环),我的机器需要 35 秒才能检测到该 31 位素数。大约每秒 285 次 - 希望这对您来说足够快:-)You can optimise it further (and make it more readable, in my opinion) by making the following changes. First performance:
n-1
, only the square root ofn
. That's because if a factord
greater thansqrt(n)
exists, its counterpartn/d
would have already been found undersqrt(n)
. We use a special variable for this so that we don't get overflow by calculating divisor2. It also speeds it up by calculating that once rather than calculating the square every time through the loop (even though getting the square root is undoubtedly slower than squaring, it only happens once).Now readability:
tag
).isPrime
boolean, rather than scattering the messages amongst your code.With all those changes, the following code can detect a 9-digit prime number (795,028,841) in well under a second. In fact, we can detect the largest 31-bit prime (2,147,483,647) in the same time.
Based on benchmarks (putting a 10,000-iteration
for
loop around theselect
), it takes 35 seconds on my box to detect that 31-bit prime. That's about 285 times per second - hopefully that'll be fast enough for you :-)