Хаскелл, понимая решение для эйлера № 3 - программирование
Подтвердить что ты не робот

Хаскелл, понимая решение для эйлера № 3

Недавно я научился изучать хакелл и очень хорошо провел время. Я работал над некоторыми проблемами Project Euler, чтобы получить синтаксис и рассмотрел решения, размещенные здесь http://www.haskell.org/haskellwiki/Euler_problems/1_to_10 в качестве инструмент обучения. Хотя я обнаружил, что не могу окунуться в решение, отправленное для проблема № 3:

-- Find the largest prime factor of 317584931803. 

primes = 2 : filter ((==1) . length . primeFactors) [3,5..]

primeFactors n = factor n primes
  where
    factor n (p:ps) 
        | p*p > n        = [n]
        | n `mod` p == 0 = p : factor (n `div` p) (p:ps)
        | otherwise      = factor n ps

problem_3 = last (primeFactors 317584931803)

Я не могу понять, насколько это работает. primes и primeFactors, кажется, звонят друг другу, чтобы создавать свои собственные списки, и попытка следовать за ним разжигает мой мозг. Кто-нибудь знает хорошее сообщение в блоге об этом решении или хочет написать здесь объяснение?

4b9b3361

Ответ 1

Это действительно озадачивает с первого взгляда. Но это не волшебство, если вы не думаете "императивно". Определения Haskell - это просто: расскажите, что что-то есть, а не какие операции нижнего уровня, которые должен выполнить компьютер.

Следовательно, список простых чисел - это список, содержащий 2 и все нечетные натуральные числа, большие 2, которые имеют только один простой множитель (а именно сам).

Список простых коэффициентов для некоторого целого числа n, с другой стороны, представляет собой список простых чисел, которые делят n.

Убедитесь, что вы поняли эти определения, прежде чем читать.

В качестве абстрактного Haskell может быть, он по-прежнему является языком программирования, поэтому нам нужно время от времени давать советы, как вычислить что-то. В частности, в приведенном выше примере мы не тестируем все простые числа, чтобы найти простые множители n, так как достаточно проверить 2..k где k*k <= n. Это также гарантирует, что мы будем использовать только ту часть расчёта, которая уже вычислена.

В начале primes выглядит следующим образом:

2 : filter ((==1) . length . primeFactors) [3,5..]

Если мы требуем следующий элемент после 2, мы вынуждаем оценку выражения right двоеточия. Это, в свою очередь, заставляет фильтр оценивать предикат для 3. Затем он идет:

primeFactors 3
  factor 3 (2 : ...)
    2*2 > 3
  [3]
[3]

Следовательно, primeFactors 3 есть [3], и нам не нужно было смотреть за пределы 2 в простых числах. (Это ключевой момент, почему это работает!) Ясно, что длина [3] равна 1 и

2 : filter ((==1) . length . primeFactors) [3,5..]

оценивается как

2 : 3 : filter ((==1) . length . primeFactors) [5, 7..]

Теперь вы можете уменьшить primeFactors 5 для себя.

Ответ 2

Это лень в действии:) Список простых чисел начинается непустым,

primes = 2 : don't_know_yet_let's_see

и primeFactors вычисляет простые множители числа, используя список простых чисел. Но чтобы найти простые множители любого числа "n", нам нужно знать только числа до sqrt(n). Итак, хвост primes,

filter ((== 1) . length . primeFactors) [3, 5 .. ]

может использовать то, что уже известно из primes. Чтобы проверить 3, мы

factor 3 (2:don't_kow_yet_let's_see)
    | 2*2 > 3 = [3]
    | don't_care_above_was_true

И если мы начнем с любого n, скажем n = 35, чтобы он был коротким,

factor 35 (2:??)
    | 2*2 > 35  -- False, next clause
    | 35 `mod` 2 == 0 -- False, next clause
    | otherwise = factor 35 ??

Теперь нам нужно выяснить, что такое ??. Мы видели выше, что filter ing позволяет 3 пройти, поэтому 3:???, следовательно,

factor 35 (3:???)
    | -- first two guards are False
    | otherwise = factor 35 ???

Теперь, что ???? Ну, filter ((== 1) . length . primeFactors) [5, 7 .. ], так что посмотрим, проходит ли 5 фильтр

factor 5 (2:3:???)  -- note, we already know the first two elements of primes
    | 2*2 > 5 -- False
    | 5 `mod` 2 == 0 -- False
    | otherwise = factor 5 (3:???)
                     | 3*3 > 5 = [5]

Итак, 5 проходит, и мы знаем первые три элемента из primes. При факторизации 35 продолжаем

factor 35 (5:????)
    | 5*5 > 35 -- False
    | 35 `mod` 5 == 0 = 5 : factor (35 `div` 5) (5:????)
                            factor 7 (5:????)
                               | 5*5 > 7 = [7]

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