Раскрытие возможностей Haskell: различные методы проверки того, является ли число простым

В сфере программирования простые числа занимают особое место благодаря своим уникальным свойствам и приложениям. Если вы знакомы с Haskell, функциональным языком программирования, известным своей элегантностью и выразительностью, вы будете рады изучить множество способов определить, является ли число простым. В этом сообщении блога мы рассмотрим несколько подходов к проверке простых чисел в Haskell, сопровождаемые простыми для понимания примерами кода. Итак, отправляемся в это увлекательное путешествие!

Метод 1: пробное деление
Метод пробного деления — один из самых простых способов проверить, является ли число простым. Он предполагает деление числа на все целые числа от 2 до квадратного корня числа и проверку на наличие остатка.

isPrime :: Int -> Bool
isPrime n
  | n < 2     = False
  | otherwise = all (\x -> n `mod` x /= 0) [2..isqrt n]
  where
    isqrt = floor . sqrt . fromIntegral

Метод 2: Решето Эратосфена
Решето Эратосфена — это древний алгоритм, который эффективно находит все простые числа до заданного предела. Применяя этот алгоритм, мы также можем определить, является ли конкретное число простым.

isPrime :: Int -> Bool
isPrime n
  | n < 2     = False
  | otherwise = primes !! n == n
  where
    primes = sieve [2..]
    sieve (p:xs) = p : sieve [x | x <- xs, x `mod` p /= 0]

Метод 3: тест на простоту Миллера-Рабина
Тест на простоту Миллера-Рабина — это вероятностный алгоритм, широко используемый при работе с большими числами. Хотя иногда он может давать ложные срабатывания, он очень эффективен и часто используется на практике.

import System.Random
isPrime :: Int -> IO Bool
isPrime n
  | n < 2     = return False
  | otherwise = do
      witnesses <- getRandomWitnesses (n - 1) 10 -- Adjust the number of witnesses for desired accuracy
      return $ all (primalityTest n) witnesses
getRandomWitnesses :: Int -> Int -> IO [Int]
getRandomWitnesses _ 0 = return []
getRandomWitnesses n k = do
  witness <- randomRIO (2, n)
  witnesses <- getRandomWitnesses n (k - 1)
  return (witness : witnesses)
primalityTest :: Int -> Int -> Bool
primalityTest n a
  | x == 1 || x == n' = True
  | otherwise         = any (== n') powers
  where
    n' = n - 1
    (s, d) = determineSD n'
    x = powMod a d n
    powers = take s (iterate (\x -> (x * x) `mod` n) x)
determineSD :: Int -> (Int, Int)
determineSD n = go 0 n
  where
    go s d
      | even d    = go (s + 1) (d `div` 2)
      | otherwise = (s, d)
powMod :: Int -> Int -> Int -> Int
powMod x y m = go x y 1
  where
    go _ 0 res = res
    go x' y' res
      | even y'    = go ((x' * x') `mod` m) (y' `div` 2) res
      | otherwise  = go x' (y' - 1) ((res * x') `mod` m)

В этом сообщении блога мы рассмотрели три различных метода проверки того, является ли число простым в Haskell. Метод пробного деления прост и подходит для меньших чисел, а «Решето Эратосфена» превосходно справляется с поиском простых чисел до заданного предела. Наконец, тест на простоту Миллера-Рабина обеспечивает вероятностный, но эффективный способ проверки простоты больших чисел. Используя эти методы, вы можете уверенно обрабатывать простые числа в своих программах на Haskell.