Пробел в программе списка
Я решаю некоторые проблемы Project Euler в Haskell. Я написал программу для загадки, и она не работала так, как я ожидал.
Когда я смотрел в диспетчере задач при запуске программы, я увидел, что он использует > 1 гигабайт оперативной памяти на ghc. Друг меня написал программу с тем же значением в Java и успел за 7 секунд.
import Data.List
opl = find vw $ map (\x-> fromDigits (x++[0,0,9]) )
$ sequence [[1],re,[2],re,[3],re,[4],re,[5],re,[6],re,[7],re,[8],re]
vw x = hh^2 == x
where hh = (round.sqrt.fromIntegral) x
re = [0..9]
fromDigits x = foldl1 (\n m->10*n+m) x
Я знаю, что эта программа выведет номер, который я хочу, чтобы получить достаточное количество ОЗУ и времени, но должен быть более эффективный способ.
Ответы
Ответ 1
Основная проблема здесь в том, что последовательность имеет утечку пространства. Он определяется следующим образом:
sequence [] = [[]]
sequence (xs:xss) = [ y:ys | y <- xs, ys <- sequence xss ]
поэтому проблема заключается в том, что список, созданный рекурсивным вызовом sequence xss
, повторно используется для каждого из элементов xs
, поэтому он не может быть отброшен до конца. Версия без утечки пространства
myseq :: [[a]] -> [[a]]
myseq xs = go (reverse xs) []
where
go [] acc = [acc]
go (xs:xss) acc = concat [ go xss (x:acc) | x <- xs ]
PS. ответ кажется Just 1229314359627783009
Изменить версию, избегая concat:
seqlists :: [[a]] -> [[a]]
seqlists xss = go (reverse xss) [] []
where
go [] acc rest = acc : rest
go (xs:xss) acc rest = foldr (\y r -> go xss (y:acc) r) rest xs
обратите внимание, что обе эти версии генерируют результаты в другом порядке от стандартного sequence
, поэтому, пока они работают для этой проблемы, мы не можем использовать его как специализированную версию sequence
.
Ответ 2
Следуя от ответа Симона Марлоу, здесь приведена последовательность, которая позволяет избежать утечки пространства, в то время как в противном случае работает так же, как оригинал, включая сохранение порядка.
Он по-прежнему использует красивое, простое понимание списка исходной последовательности - единственное отличие заключается в том, что вводится ложная зависимость данных, которая запрещает общий доступ рекурсивного вызова.
sequenceDummy d [] = d `seq` [[]]
sequenceDummy _ (xs:xss) = [ y:ys | y <- xs, ys <- sequenceDummy (Just y) xss ]
sequenceUnshared = sequenceDummy Nothing
Я думаю, что это лучший способ избежать совместного использования, которое приводит к утечке пространства.
Я бы обвинил излишнее участие в трансформации "полной лень". Обычно это отличная работа по созданию совместного использования, что позволяет избежать перекомпоновки, но иногда перекомпоновка намного эффективнее, чем хранение общих результатов.
Было бы неплохо, если бы был более прямой способ сообщить компилятору не делиться конкретным выражением - приведенный выше аргумент dummy Maybe
работает и эффективен, но это в основном взлом, который достаточно сложный, что ghc может Не говорите, что нет реальной зависимости. (На строгом языке у вас нет этих проблем, потому что у вас есть общий доступ, где вы явно привязываете переменную к значению.)
Ответ 3
EDIT: Я думаю, что я ошибаюсь здесь - изменение сигнатуры типа на:: Может быть, Word64 (который будет достаточным количеством бит для этой проблемы, я думаю) также занимает навсегда/имеет утечку пространства, поэтому это не может быть старая ошибка Integer.
Ваша проблема кажется старой ошибкой GHC (которая, как я полагала, была исправлена) с Integer, вызывающим утечку пространства. Следующий код заканчивается примерно за 150 мс при компиляции с -O2.
import Data.List
import Data.Word
main = print opl
opl :: Maybe Word32
opl = find vw $ map (\x-> fromDigits (x++[0,0,9]) ) $ sequence [[1],re,[2],re,[3],re,[4],re,[5],re,[6],re,[7],re,[8],re]
vw x = hh^2 == x
where hh = (round.sqrt.fromIntegral) x
re = [0..9]
fromDigits x = foldl1 (\n m->10*n+m) x
Ответ 4
Поскольку вы ищете девятнадцатизначное число с теми характеристиками, которые есть в vw, я попытаюсь упростить конструкцию в отображаемой функции, просто скажите fromDigits x*1000+9
для стартеров. К списку добавляется O (длина списка слева), поэтому бросание последних трех цифр на конец вредит времени вычисления связкой.
В качестве стороннего (для вас обоих) использование строгой версии сложения (foldl1'
) также поможет.