ru_lambda (original) (raw)

(no subject) [Nov. 13th, 2006|06:50 pm]Лямбда - функциональное программирование
Привет!Написал свою первую более-менее осмысленную программу на Хаскелле. Эта программа решает следующую задачу для данных двух целых чисел a и b найти значение a/b в виде xxx.yyy(zzzzz), где xxx - целая часть, yyy - дробная непериодическая, zzzzz - период дробной части.Вот что получилось**formFractPart :: ([Int], [Int]) -> [Char]formFractPart tup = (arr2str f) ++ "(" ++ (arr2str s) ++ ")" where f = fst tup s = snd tuparr2str arr = foldl (++) "" (map show arr)getIndex a arr = getIndex' a arr 0getIndex' a arr@(x:xs) i | notElem a arr = (-1) a == x = i otherwise = getIndex' a xs i+1makeDigits' :: Int -> Int -> [Int] -> [Int] -> ([Int], [Int])makeDigits' a b currRems curDigits elem a currRems = (take n curDigits, drop n curDigits) otherwise = makeDigits' (10*a `mod` b) b (currRems ++ [a]) (curDigits ++ [(10*a `div` b)]) where n = (getIndex a currRems)makeDigits :: Int -> Int -> IO ()makeDigits a b a == b = putStrLn "1" a < b = putStrLn ("0," ++ formFractPart (makeDigits' a b [] [])) a > b = putStrLn (show (a `div` b) ++ "," ++ formFractPart (makeDigits' (a `mod` b) b [] []))**Проблема такая:makeDigits 1 9991 отрабатывает почти мгновенно, правда периодическая часть занимает чуть меньше странички консоли..., а вот makeDigits 1 99991 считает ну очень долго - ~ 15 минут, при том что подобная прога на Python'e (писал год назад на базе) считает то же самое за 1мин 13 сек, при этом занимая памяти ~5 мегов. Haskell занимал ~40 метров. Понимаю, это скорее всего из-за неоптимальности написанной проги. Прошу указать где именно, и поправить. Мне это будет очень полезно. Спасибо.
link 21 comments|post comment
Билетики [Jan. 8th, 2006|08:40 pm]Лямбда - функциональное программирование
Я уже пыталась поднять тут такой вопрос: как бы в хаскеле хранить результаты предыдущих вычислений, чтобы не делать все подряд вычисления экспоненциально длинными? Первое, что выяснилось, сам хаскель этого «волшебно» не делает, так что надо как-то заботиться об этом самостоятельно. Пример с числами Фибоначчи был не лучшим, ибо для него надо всего-то хранить два последних значения, и можно выкрутиться всякими хитростями. А что если их надо знать аж целых N? Вот сейчас я опишу некую задачу и прошу знатоков хаскеля мне объяснить: как её следует образцово-показательно решать на хаскеле? Итак, залезая в Ленинграде в трамвай, следует кинуть три копейки в кассу-копилку и выкрутить себе из неё билетик. На нём стоит шестизначный номер. Билетик считается «счастливым», если сумма первых трёх цифр номера равняется сумме трёх последних (оставшихся) цифр. Для наступления счастья, такой билет необходимо немедленно засунуть в рот, прожевать и проглотить (а потом заесть булкой сидя на поребрике у парадной). Задача такая: сколько существует обобщённых счастливых билетиков заданного порядка n, то есть последовательностей цифр длины 2n, у которых сумма первой половины цифр равна сумме второй половины цифр (таким образом стандартный трёхкопеечный билет с шестизначным номером является обобщённым порядка 3). Есть два подхода к вычислению этого числа, которые дают алгоритмы одинаковой временной сложности — O(n²), если мы говорим о машине RAM и O(n³) если мы говорим об «обычном компьютере», который складывает и вычитает длинные числа за линейное в длине время. Оба подхода основываются на том, что достаточно лишь подсчитать, сколько существует билетиков заданной длины с заданной суммой цифр, а количество счастливых билетиков порядка n оказывается равным числу билетов длины 2n с суммой цифр 9n (что бы это увидеть, нужно представить себе, что во всех билетах заменили вторую половину цифр по формуле x↦9-x). Таким образом, всё сводится к вычислению функции B(n,s) числа билетов длины n с суммой цифр s. Дальше можно действовать по разному. Вот здесь описано как свести вычисление функции B(·,·) к вычислению нескольких биномиальных коэффициентов и их сумм. Таким образом получается простая программа на хаскеле, но не она меня сегодня интересует. ( Кому интересно, билетики можно считать такCollapse ) А интересно мне вот что: как выразить на хаскеле «алгоритм считания в лоб»? В лоб считают так: предположим, что мы знаем все значения B(n-1,·). Тогда для вычисления B(n,s) нужно просто по очереди предположить, что билетик начинается на 0, 1, 2, 3, 4, 5, 6, 7, 8 и 9, и получится, что B(n,s)=Σ B(n-1, s-i) по всем i=0,…,9. То есть, для вычисления следующих 9n+1 значений нужно знать все 9(n-1)+1 предыдущих — похитрее чем у чисел Фибоначчи. ( Вот, чисто для иллюстрации, этот алгоритм на перлеCollapse )
link 16 comments|post comment