2009-11-19 2 views
1

Я анализирую код с сайта, и я попробовал его на моей стороне, но, похоже, он не работает. Не могли бы вы рассказать мне, почему? большое спасибо вашей помощи.простое число в 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 
+0

Для каких чисел это не работает? В чем проблема? –

+1

Кажется, все в порядке. Возможно, вы захотите изменить это, хотя «Loop While D <= N - 1», возможно, для «Loop While D <= N/2' для начинающих –

+0

Работает и для меня. Единственное, о чем я могу думать, это то, что он/она не помещает фактическое число в нужное место на листе XLS (хе-хе). – ajdams

ответ

7

Самая большая проблема, которую я вижу в использовании Single вместо Integer или Long. Простые числа являются положительными целыми числами и не рассматриваются в контексте десятичных значений (насколько я знаю). Таким образом, используя синглы и сравнивая их с разделенными ints, вы открываете себя до неприятных ошибок на рулонном крае.

В строке If N/D = Int(N/D) Then используется плохой метод, чтобы узнать, являются ли цифры первыми. Это , предполагая, что каждый раз, когда вы делите число с плавающей запятой (в данном случае на сингл) на делитель, если оно имеет десятичный остаток, то целочисленное преобразование этого остатка не будет равным. Тем не менее, я иногда сталкивался с ошибками округления с числами с плавающей запятой при попытке сравнить ответы, и в целом я научился избегать использования конверсий с плавающей точкой в ​​int как способ сравнения чисел.

Вот какой код вы могли бы попробовать вместо этого. Некоторые примечания:

  • Я изменил типы N и D так, чтобы они были длинными, а не одиночными. Это означает, что они не являются плавающей точкой и подвержены возможным ошибкам округления.
  • Я также явно преобразовал значение ячейки в длинный. Таким образом, вы знаете в своем коде, что вы не работаете с типом с плавающей точкой.
  • Для сравнения, я использовал 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 указывает, что вы определяете процедуру в коде, которая не возвращает никакого значения. (Иногда вы можете увидеть слово Function вместо Sub.Это означает, что процедура возвращает значение, например Private Function ReturnANumber() As Long.) Процедура (Sub) является темой кода, который будет выполняться при вызове. Также стоит отметить, что макрос excel хранится в VBA как процедура Sub.

В вашей строке кода CommandButton1_Click() находится имя процедуры. Скорее всего, это было создано автоматически, добавив кнопку в электронную таблицу Excel. Если кнопка привязана к электронной таблице Excel, то CommandButton1_Click() будет выполняться каждый раз при нажатии кнопки.

В вашем коде Private указывает сфера действия процедуры. Private обычно означает, что процедура не может вызываться за пределами модуля или класса, в котором она находится. В моем коде я оставил Private, потому что вы можете позвонить GetPrime из другого модуля кода.

Вы упомянули в своих комментариях, что вам нужно было изменить название моей процедуры от 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 для дальнейших вашего понимания!

+0

@bmccormack: спасибо за разъяснение.лет, я прочитал его снова, пока я ухожу, чтобы тренироваться сейчас. Я проверю его снова, как только я вернусь домой – tintincutes

+0

Также стоит отметить, что я проверил премьер больше 100 000 000, а мой код вернул результат примерно через 15 секунд. Код, который вы предоставили, никогда не заканчивается после нескольких минут работы. –

+0

@bmccormack: я также попробовал ваш пример, и это не сработает, если я не изменил «Sub GetPrime()» в «Private Sub CommandButton1_Click()» Я только начинаю изучать VBA, я не понимаю этого Sub GetPrime начало. Какая разница? Если вы делаете свой тест, вы пишете его в редакторе VBA, как тот, который вы разместили? спасибо – tintincutes

2

Я не уверен, где вы скопировали этот код, но это ужасно неэффективно. Если я могу:

  1. Dim N, D As Long приведет к тому, что D будет длинным, а N - вариантом. Как вы знаете, варианты являются одним из самых медленных типов данных. Эта строка должна быть: Dim N As Long, D As Long
  2. Вам нужно всего лишь проверить каждый другой номер, поскольку четное число будет делиться на два. (Поэтому не может быть простым).
  3. Вам не нужно проверять весь путь до N. Вам нужно только проверить до Квадратный корень N. Это связано с тем, что после квадратного корня факторы просто переключают стороны, поэтому вы просто возвращаете значения.
  4. Для циклов только один раз оценивает значение For-Line для жизни цикла, но циклы Do и While определяют их условное значение на на каждый цикл, поэтому N-1 оценивается много, много раз. Сохраните это значение в переменной, если вы хотите использовать Do Loop.

Итак, теперь, когда мы отказались от бла-бла-бла-бла, вот код. Я структурированы так, вы можете использовать его в качестве UDF из Excel, а также (Ex: = 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 
+0

@Oorang: Я скопировал его здесь: http://www.vbtutor.net/VBA/vba_tutorial.html – tintincutes

2

Вы можете оптимизировать его дальше (и сделать его более удобным для чтения, на мой взгляд), сделав следующие изменения. Первые результаты:

  • Использование длинномерное, а не плавает. Это приведет к огромному увеличению скорости.
  • Вам не нужно проверять до n-1, только квадратный корень n.Это связано с тем, что если существует коэффициент d, более sqrt(n), его аналог n/d был бы найден подsqrt(n). Для этого используется специальная переменная, так что мы не получаем переполнение путем вычисления делителя . Это также ускоряет вычисление того, что один раз вместо вычисления квадрата каждый раз через цикл (хотя получение квадратного корня, несомненно, медленнее, чем квадрат, это происходит только один раз).
  • Выполняйте специальную проверку сначала для кратных двух, тогда вам нужно только проверить, что ваш номер кратен нечетному числу, эффективно удваивая скорость (не проверяя, являетесь ли вы коэффициентом несколько из двух).
  • Используйте оператор modulo, а не деление/умножение.

Теперь читаемости:

  • Используйте описательные имена переменных.
  • Используйте логическое значение для булевых значений (не строка, как tag).
  • Переместите логику окна сообщений вниз, основываясь на isPrime булевом, вместо того, чтобы рассеивать сообщения среди вашего кода.

Со всеми этими изменениями следующий код может обнаружить 9-значное простое число (795 028 841) в течение секунды. Фактически, мы можем обнаружить самое большое 31-битное простое (2 147 483 647) в одно и то же время.

Основываясь на контрольных точках (помещая 10 000-итерацию for вокруг 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 
+0

@paxdiablo: строка, которая гласит: «Если номер div divisor = 0 Then» отмечен как красный. Не могли бы вы проверить на своей стороне? У вас тоже есть такая же проблема? Код не будет работать. – tintincutes

+0

Извините, это должно быть «mod» как в «modulo», не «rem», как в «остатке», или, как правильно видит VBA, «rem», как в «примечании» :-) – paxdiablo

+0

без проблем я изменил это, но , он не работает. Нет отображаемого сообщения, если его простое или не простое число. Ничего не случилось :-( – tintincutes

Смежные вопросы