Подтвердить что ты не робот

Как я могу проверить на простоту?

Я пишу небольшую библиотеку с некоторыми методами, связанными с числом чисел. Поскольку я сделал основы (также методы работы), и теперь я ищу некоторую оптимизацию. Конечно, интернет - отличное место для этого. Я, однако, наткнулся на проблему округления, и мне было интересно, как это решить.

В цикле я использую для проверки числа для его примитивности, более эффективно искать до sqrt (n) вместо n/2 или даже n - 1. Но из-за проблем округления некоторое число пропускается и, следовательно, некоторые простые числа пропущено! Например, 10000-е число должно быть: 104729, но "оптимизированная" версия заканчивается: 103811.

Некоторый код (он открыт для большей оптимизации, я знаю, но я могу обрабатывать только одну вещь за раз):

/// <summary>
/// Method for testing the primality of a number e.g.: return IsPrime(29);
/// History:
/// 1. Initial version, most basic form of testing: m smaller then n -1
/// 2. Implemented m smaller then sqrt(n), optimization due to prime factoring
/// </summary>
/// <param name="test">Number to be tested on primality</param>
/// <returns>True if the number is prime, false otherwise</returns>
public static bool IsPrime(int test)
{
    // 0 and 1 are not prime numbers
    if (test == 0 || test == 1) return false;

    // 2 and 3 are prime numbers
    if (test == 2) return true;

    // all even numbers, save 2, are not prime
    if (test % 2 == 0) return false;

    double squared = Math.Sqrt(test);
    int flooredAndSquared = Convert.ToInt32(Math.Floor(squared));

    // start with 5, make increments of 2, even numbers do not need to be tested
    for (int idx = 3; idx < flooredAndSquared; idx++)
    {
        if (test % idx == 0)
        {
            return false;
        }
    }
    return true;
}

Я знаю, что квадратная часть терпит неудачу (или я терпит неудачу), также попытался Math.Ceiling, примерно с теми же результатами.

4b9b3361

Ответ 1

К сожалению, я раньше не пробовал алгоритмические подходы. Но если вы хотите эффективно реализовать свой подход, я бы предложил сделать кеширование. Создайте массив для хранения всех простых чисел меньше определенного порога, заполните этот массив и выполните поиск внутри/используя его.

В следующем примере определение того, является ли число простым, является O (1) в лучшем случае (а именно, когда число меньше или равно maxPrime, что составляет 821 461 для буфера 64 КБ) и является несколько оптимизирован для других случаев (путем проверки мод только на 64 тыс. номеров из первых 820 000 - около 8%).

(Примечание. Не принимайте этот ответ как "оптимальный" подход. Это больше пример того, как оптимизировать вашу реализацию.)

public static class PrimeChecker
{
    private const int BufferSize = 64 * 1024; // 64K * sizeof(int) == 256 KB

    private static int[] primes;
    public static int MaxPrime { get; private set; }

    public static bool IsPrime(int value)
    {
        if (value <= MaxPrime)
        {
            return Array.BinarySearch(primes, value) >= 0;
        }
        else
        {
            return IsPrime(value, primes.Length) && IsLargerPrime(value);
        }
    }

    static PrimeChecker()
    {
        primes = new int[BufferSize];
        primes[0] = 2;
        for (int i = 1, x = 3; i < primes.Length; x += 2)
        {
            if (IsPrime(x, i))
                primes[i++] = x;
        }
        MaxPrime = primes[primes.Length - 1];
    }

    private static bool IsPrime(int value, int primesLength)
    {
        for (int i = 0; i < primesLength; ++i)
        {
            if (value % primes[i] == 0)
                return false;
        }
        return true;
    }

    private static bool IsLargerPrime(int value)
    {
        int max = (int)Math.Sqrt(value);
        for (int i = MaxPrime + 2; i <= max; i += 2)
        {
            if (value % i == 0)
                return false;
        }
        return true;
    }
}

Ответ 2

Я думаю, это ваша проблема:

for (int idx = 3; idx < flooredAndSquared; idx++)

Это должно быть

for (int idx = 3; idx <= flooredAndSquared; idx++)

чтобы вы не получили квадратные числа в виде простых чисел. Кроме того, вы можете использовать "idx + = 2" вместо "idx ++", потому что вам нужно только проверять нечетные числа (как вы писали в комментарии непосредственно выше...).

Ответ 3

Я не знаю, действительно ли это то, что вы ищете, но если вы действительно обеспокоены скоростью, тогда вы должны изучить вероятностные методы тестирования первичности, а не использовать сито. Rabin-Miller является вероятностным критерием примитивности, используемым Mathematica.

Ответ 5

Как сказал Марк, тест Миллера-Рабина на самом деле очень хороший способ. Дополнительной ссылкой (с псевдокодом) является статья Википедии об этом.

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

Я также рекомендовал бы прочитать эту статью об модульной возведения в степень, поскольку в противном случае вы будете иметь дело с очень большими номерами при попытке для выполнения теста Миллера-Рабина...

Ответ 6

Возможно, вы захотите заглянуть в небольшая теорема Ферма.

Вот псевдокод из книги Алгоритмы С. Дасгупта, C.H. Papadimitriou, и U.V. Vazirani, где n - это номер, который вы тестируете для простоты.

Pick a positive integer a < n at random
if a^n-1 is equivalent to 1 (mod n)
   return yes
else
   return no

Реализация теоремы Ферма должна быть быстрее решения сита. Тем не менее, есть числа Кармайчеля, которые проходят тест Ферма и НЕ являются главными. Для этого есть обходные пути. Я рекомендую вам обсудить раздел 1.3 в вышеупомянутой книге. Это все о тестировании примитивов и может быть полезно для вас.

Ответ 7

Вот половинная приличная функция, которую я написал, чтобы решить один из Эйлера:

private static long IsPrime(long input)
{
    if ((input % 2) == 0)
    {
        return 2;
    }
    else if ((input == 1))
    {
        return 1;
    }
    else
    {
        long threshold = (Convert.ToInt64(Math.Sqrt(input)));
        long tryDivide = 3;
        while (tryDivide < threshold)
        {
            if ((input % tryDivide) == 0)
            {
                Console.WriteLine("Found a factor: " + tryDivide);
                return tryDivide;
            }
            tryDivide += 2;
        }
        Console.WriteLine("Found a factor: " + input);
        return -1;
    }
}

Ответ 8

Попробуйте это...

if (testVal == 2) return true;
if (testVal % 2 == 0) return false;

for (int i = 3; i <= Math.Ceiling(Math.Sqrt(testVal)); i += 2)
{
   if (testVal % i == 0)
       return false;
}

return true;

Я использовал это довольно много раз. Не так быстро, как сито, но оно работает.

Ответ 9

У меня есть быстрый алгоритм для проверки простых чисел. Вы можете улучшить свой алгоритм, если знаете, что простые числа имеют форму 6k ± 1, за исключением 2 и 3. Итак, сначала вы можете проверить, делится ли вход на 2 или 3. Затем проверьте все номера формы 6k ± 1 ≤ √ вход.

bool IsPrime(int input)
        {
            //2 and 3 are primes
            if (input == 2 || input == 3)
                return true; 
            else if (input % 2 == 0 || input % 3 == 0)
                return false;     //Is divisible by 2 or 3?
            else
            {
                for (int i = 5; i * i <= input; i += 6)
                {
                    if (input % i == 0 || input % (i + 2) == 0)
                        return false;
                }
                return true;
            }
        }

Ответ 10

Попробуйте сито eratosthenes - это должно вывести проблемы с корнем и с плавающей точкой.

Что касается floor, вам будет лучше обслуживать, взяв ceiling.

Ответ 11

private static bool IsPrime(int number) {
    if (number <= 3)
        return true;
    if ((number & 1) == 0)
        return false;
    int x = (int)Math.Sqrt(number) + 1;
    for (int i = 3; i < x; i += 2) {
        if ((number % i) == 0)
            return false;
    }
    return true;
}

Я не могу получить его быстрее...

Ответ 13

Это очень быстро работает для тестирования простых чисел (vb.net)

Dim rnd As New Random()
Const one = 1UL

    Function IsPrime(ByVal n As ULong) As Boolean
        If n Mod 3 = 0 OrElse n Mod 5 = 0 OrElse n Mod 7 = 0 OrElse n Mod 11 = 0 OrElse n Mod 13 = 0 OrElse n Mod 17 = 0 OrElse n Mod 19 = 0 OrElse n Mod 23 = 0 Then
           return false
        End If

        Dim s = n - one

        While s Mod 2 = 0
            s >>= one
        End While

        For i = 0 To 10 - 1
            Dim a = CULng(rnd.NextDouble * n + 1)
            Dim temp = s
            Dim m = Numerics.BigInteger.ModPow(a, s, n)

            While temp <> n - one AndAlso m <> one AndAlso m <> n - one
                m = (m * m) Mod n
                temp = temp * 2UL
            End While

            If m <> n - one AndAlso temp Mod 2 = 0 Then
                Return False
            End If
        Next i

        Return True
    End Function

Ответ 14

В случае, если кого-то интересует, вот мое обращение процедуры Мохаммада выше к VBA. Я добавил проверку, чтобы исключить 1, 0 и отрицательные числа, поскольку все они определены как не простые.

Я тестировал это только в Excel VBA:

Function IsPrime(input_num As Long) As Boolean
    Dim i As Long
    If input_num < 2 Then '1, 0, and negative numbers are all defined as not prime.
        IsPrime = False: Exit Function
    ElseIf input_num = 2 Then
        IsPrime = True: Exit Function '2 is a prime
    ElseIf input_num = 3 Then
        IsPrime = True: Exit Function '3 is a prime.
    ElseIf input_num Mod 2 = 0 Then
        IsPrime = False: Exit Function 'divisible by 2, so not a prime.
    ElseIf input_num Mod 3 = 0 Then
        IsPrime = False: Exit Function 'divisible by 3, so not a prime.
    Else
        'from here on, we only need to check for factors where
        '6k ± 1 = square root of input_num:
        i = 5
        Do While i * i <= input_num
            If input_num Mod i = 0 Then
                IsPrime = False: Exit Function
            ElseIf input_num Mod (i + 2) = 0 Then
                IsPrime = False: Exit Function
            End If
            i = i + 6
        Loop
        IsPrime = True
    End If
End Function

Ответ 15

Ваш цикл for должен выглядеть так:

for (int idx = 3; idx * idx <= test; idx++) { ... }

Таким образом, вы избегаете вычисления с плавающей запятой. Должен работать быстрее, и это будет более точно. Вот почему оператор for условный - это просто логическое выражение: он делает такие вещи такими, как это возможно.