Как получить пары элементов из бесконечных списков в Haskell?
Общие Проблемы
у меня есть бесконечный список, и я хочу выбрать пару (a,b)
здесь a
и b
из списка и пара удовлетворяет какую-то собственность. Используя списочные включения не работает, потому что список бесконечный.
Конкретного Экземпляра
я пытаюсь найти пару простых чисел, которые складываются в заданное число (см. этот код гольф проблема).
я определил primes
, которым это бесконечный список простых чисел, но когда я наивно пытаюсь выбрать пару простых чисел, как показано ниже, процесс никогда не заканчивается.
goldbach n = head [(a,b) | a<-primes, b<-primes, a+b==n]
я понимаю, что это потому, что список генерируемых простых чисел [(2,2), (2,3), (2,5)...]
. В основном, a
становится первым элементом из primes
, а после b
исчерпан, он перейдет на второй элемент. Потому что primes
бесконечен, он никогда не будет исчерпан!
есть ли простой способ использовать понимание списка для решить эту проблему? В противном случае, есть ли простое решение?
4 ответов
goldbach n = head [(a,b) | let ps = takeWhile (<n) primes, a<-ps, b<-ps, a+b==n]
это было бы намного быстрее, хотя.
goldbach2 n = aux ps (reverse ps) where
ps = takeWhile (<n) primes
aux [] _ = []
aux _ [] = []
aux (a:as) (b:bs)
| a > b = []
| otherwise = case compare (a+b) n of
EQ -> (a,b):aux as bs
LT -> aux as (b:bs)
GT -> aux (a:as) bs
самый хороший способ-это использовать ширина-первый список монады. Поскольку список можно рассматривать как просто синтаксический сахар монады (так же, как do
), что позволяет вам сделать это выглядеть ровно как то, что вы сейчас:
{-# LANGUAGE MonadComprehensions #-}
import Control.Monad.Omega
goldbach n = head $ runOmega [(a,b) | a<-ps, b<-ps, a+b==n]
where ps = each primes
жаль, что вы не приняли решение @leftaroundabout. В этом есть много чего еще. Например, другое решение имеет эвристику "все числа должны быть меньше n" (что вы делаете для проблем, где у вас нет этой эвристики?), и несколько других шагов, которые затрудняют доказательство того, что это действительно решение проблемы Гольдбаха - например, можете ли вы продемонстрировать, что способ перечисления простых чисел собирается собрать все полезные пары простых чисел? (Да, но можете ли вы это продемонстрировать? Это слабость это решение)
Итак, здесь я покажу, как вы можете построить решение @leftaroundabout, не говоря слова "монада".
проблема с пониманием списка, которое вы построили сначала, заключается в том, что он ищет решение "глубина-сначала" - возьмите один элемент из первого списка, а затем попробуйте все комбинации с этим элементом. Однако, если существует бесконечное количество комбинаций, вы никогда не сможете перечислить какие-либо пары со вторым элементом из первого списка. Именно здесь нам нужно поговорить о поиске решений" широты первой".
скажем gen
генератор решений:
gen x = map (\y -> (x,y)) -- construct pairs for a given element x
скажем gens
является генератором генераторов решений:
gens xs ys = map (\x -> gen x ys) xs
все, что нам нужно сделать сейчас, это перечислить решения, взяв по одному решению от каждого генератора за раз. Имейте в виду, если число генераторов бесконечно, мы не можем просто взять их один за другим - это таким образом, мы никогда не получим второе решение от первого генератора, поэтому нам нужно" шагнуть " их как-то:
om xs = f [] [] xs where -- this is your omega enumerating solutions
-- from generator of generators
f [] [] [] = [] -- this is where the pot stops cooking
f [] gs [] = f gs [] []
f [] gs (g:gens) = f (g:gs) [] gens -- ok, took one solution from all generators -
-- add one generator to the list of generators we enumerate
-- next time - this is how we "step" the generators
f ([]:gs) gs' gens = f gs gs' gens -- generator eliminator: last solution was taken
-- from one generator
f ((x:xs):gs) gs' gens = x : f gs (xs:gs') gens -- take one solution from the first
-- generator, and move the rest of the generator to the list
-- for the next iteration
вот и все.
> find (\(x,y) -> x+y==190) $ om $ gens primes primes
Just (179,11)
сейчас, к эффективности. Это все в gens
и gen
. Добавьте условие gens
to takeWhile ((<=n) . (x+))
, и это будет работать, даже если гипотеза Гольдбаха неверна (но вам не нужно добавлять ее вообще). Добавьте условие gen
до takeWhile (<=x)
, и вы будете перечислять только пары, где первое простое число больше, чем другой.
или лямбда, дающая бесконечный список пар из бесконечного списка чего угодно
(\x->let p=(\(a:b:oo)->(a,b):p oo) in p x)
на ghci
let p=(\x->let p=(\(a:b:oo)->(a,b):p oo) in p x)
take 10 (p [1..])