Лениво посчитать сумму элементов бесконечных множеств
infSum :: (Num a, Ord a) => [a] -> [a] -> [a]
infSum (a:as') (b:_) = infSum as' b (a + b) as bs where
infSum (h:hs) b lo as bs = let
hi = h + b
aPref = takeWhile (< hi) as
bPref = map (\a -> takeWhile (< hi) $dropWhile (< lo) $map (+ a) bs) aPref
in merge bPref ++ infSum hs b hi as bs
merge :: Ord a => [[a]] -> [a]
merge (as:as':ass) = merge (mergeTwo as as':ass) where
mergeTwo as [] = as
mergeTwo [] as = as
mergeTwo (a:as) (b:bs) | a <= b = a:mergeTwo as (b:bs)
| otherwise = b:mergeTwo (a:as) bs
merge [as] = as
merge [] = []
Для императивного случая легко придумать решение.я так понял что формулировка нечеткая, то есть таких списков C может быть бесконечное число. тебе не принципиально как именно будет строиться список C ? или предложи императивное решение, его можно будет попробовать переписать на функциональщину
тебе не принципиально как именно будет строиться список C ?
задачу понял неправильно
задачу понял неправильно =\
static Chain<int> Sum(Chain<int> chain1, Chain<int> chain2)
{
return => {
var unit1 = chain1;
var unit2 = chain2;
return Unit.Create(unit1.Value + unit2.Value, Sum(unit1.Next, unit2.Next;
};
}
В чем проблема?
(Если я всё правильно понял, то) у тебя каждый элемент одной последовтельности суммируется с тем же по порядку элементом другой, не? А надо, чтоб каждый с каждым.
Посмотри на диззеденовский вариант, он корректный. Только громоздкий
take 10 $ infSum [1, 7 ..] [1, 6 ..]
[2,7,8,12,13,14,17,18,19,20]
module Main where
import System.Environment (getArgs)
import Data.List
-- Оформление ввода из командной строки
-- нужного пользователю числа элементов в конечном списке
main = do
args <- getArgs
case args of
[n] -> print (sumList (read n) [13, 26 ..] [2, 4 ..])
_ -> putStrLn "error: exactly one integer argument needed!"
-- Код искомой функции
sumList :: Int -> [Int] -> [Int] -> [Int]
sumList n a b = take n (sort $ concat $ square n a b)
square :: Int -> [Int] -> [Int] -> [[Int]]
square n a b = take n [ take n [x + y | x <- a] | y <- b]
P.S. Для тех, кто забыл, напомню, что знак доллара в Haskell — это композиция.
А что здесь думать? Тем то и хороши функциональные языки программирования, что думать особо не надо, пишем как есть:там написано, что список бесконечный. Значит ты n на вход получать не будешь, алгоритм должен генерировать бесконечную последовательность
take 10 $ infSum [1, 7 ..] [1, 6 ..]
[2,7,8,12,13,14,17,18,19,20]
module Main where
import System.Environment (getArgs)
import Data.List
-- Оформление ввода из командной строки
-- нужного пользователю числа элементов в конечном списке
main = do
args <- getArgs
case args of
[n] -> print ( take (read n) $ infSum [13, 26 ..] [2, 4 ..])
_ -> putStrLn "error: exactly one integer argument needed!"
-- Код искомой функции
infSum :: [Int] -> [Int] -> [Int]
infSum a b = [last $ sumList n a b | n <- [1..]]
sumList :: Int -> [Int] -> [Int] -> [Int]
sumList n a b = take n (sort $ concat $ square n a b)
square :: Int -> [Int] -> [Int] -> [[Int]]
square n a b = take n [ take n [x + y | x <- a] | y <- b]
Зато очень просто и не надо думать
один кэшированный с упорядочиванием и представляет собой набор "уже открытых" подпоследовательностей,
второй стрим на открытие новой подпоследовательности
сложность - n*log n
если делать без упорядоченного кэша, то сложность будет n^2
class Program
{
static IEnumerable<double> OrderedSum(IEnumerable<double> lefts, IEnumerable<double> rights)
{
var l = lefts.GetEnumerator;
var r_first = rights.FirstOrDefault;
var ordered_cache = new[]{ new {n = 0.0, s = 0.0, r = (IEnumerator<double>)null }}.Take(0).ToArray;
Action<double, IEnumerator<double>> next_item = (s, r) =>
{
r.MoveNext;
ordered_cache = Ordered_Add(ordered_cache, new { n = s + r.Current, s, r }, item => item.n);
};
Action next_seq = =>
{
l.MoveNext;
next_item(l.Current, rights.GetEnumerator;
};
next_seq;
for (; ; )
{
var first_cache = ordered_cache.FirstOrDefault;
if (first_cache.n < l.Current + r_first)
{
yield return first_cache.n;
ordered_cache = Ordered_RemoveFirst(ordered_cache);
next_item(first_cache.s, first_cache.r);
}
else
{
yield return l.Current + r_first;
next_seq;
}
}
}
static T[] Ordered_RemoveFirst<T>(T[] items)
{
return items.Skip(1).ToArray;
}
static T[] Ordered_Add<T, TKey>(T[] items, T item, Func<T, TKey> keyer)
{
return items.Concat(new[] { item }).OrderBy(keyer).ToArray;
}
static void Main(string[] args)
{
foreach (var v in OrderedSum(Enumerable.Range(0, 100000).Select(i => i / 2.0 Enumerable.Range(0, 100000).Select(i => i * 2.0 + 1
{
Console.WriteLine(v);
}
}
}
ps
под рукой не оказалось готовой сортированной коллекцией, поэтому вместо неё "заготовка".
module Main where
import System.Environment (getArgs)
import Data.List
-- Оформление ввода из командной строки
-- нужного пользователю числа элементов в конечном списке
main = do
args <- getArgs
case args of
[n] -> print ( take (read n) $ infSum [1, 7 ..] [1, 6 ..])
_ -> putStrLn "error: exactly one integer argument needed!"
-- Код искомой функции
infSum :: [Int] -> [Int] -> [Int]
infSum a b = [getElem n a b | n <- [0..]]
getElem :: Int -> [Int] -> [Int] -> Int
getElem n a b = (sort $ concat $ square (n + 1) a b) ! n
square :: Int -> [Int] -> [Int] -> [[Int]]
square n a b = take n [ take n [x + y | x <- a] | y <- b]
Напомню, что значок ! — это операция получения элемента списка по индексу.
Посмотри на диззеденовский вариант, он корректный.пытаюсь понять задачу: нужно для списков A, B выдать список C, который задается как sort { a + b | forall a in A, forall b in B}; ? (это метакод, не надо пытаться угадать язык )
sort { a + b | forall a in A, forall b in B}; ?да
Задача состоит в том, чтобы по двум бесконечным неубывающим спискам A и B построить бесконечный неубывающий список, который состоит из ВСЕХ возможных попарных сумм вида a + b, где a из A, b из B.
ВСЕХ возможных попарных сумм вида a + bспасибо, это самое важное уточнение
module Main where
import System.Environment (getArgs)
import Data.List
-- Оформление ввода из командной строки
-- нужного пользователю числа элементов в конечном списке
main = do
args <- getArgs
case args of
[n] -> print ( take (read n) $ infSum [1, 7 ..] [1, 6 ..])
_ -> putStrLn "error: exactly one integer argument needed!"
-- Код искомой функции
infSum :: [Int] -> [Int] -> [Int]
infSum a b =
let table = [ [x + y | x <- a] | y <- b]
in [getElem n table | n <- [0..]]
getElem :: Int -> [[Int]] -> Int
getElem n t = (sort $ concat $ square (n + 1) t) ! n
square :: Int -> [[Int]] -> [[Int]]
square n tab =
let t = take n tab
in [take n row | row <- t]
Разница здесь только в том, что для получения очередного элемента списка, мы заново не пересчитываем попарные суммы в бесконечной таблице:Какая в итоге сложность получилась? n?
module X where
import Data.List
infSum xs ys = process [fstSum (xs,ys)] where
process zs = s : process (zs' ++ map fstSum (goOn z where
s,z):zs') = sortBy (\(x,_) (y,_) -> compare x y) zs
fstSum (x:xs, y:ys) = (x+y, z)
goOn ([_], [_]) = []
goOn ([x], y:ys) = [(xs, ys)]
goOn (x:xs, [y]) = [(xs, ys)]
goOn (x:xs, y:ys) = [ ([x],ys (xs,[y] (xs,ys) ]
Но так видимо будет медленно, по хорошему надо не пересортировывать каждый раз, а поддерживать пирамиду. Ладно, на первый раз и так сойдет
P.S. Без работающего тега math чувствуешь себя древним греком: они не знали формул и писали все словами
работает примерно в 10 раз быстрееЫыы Твой вариант
[xoft hugs]$ time ./y 500 >/dev/null
real 0m5.094s
user 0m5.056s
sys 0m0.028s
Мой
[xoft hugs]$ time ./x 500 > /dev/null
real 0m0.005s
user 0m0.004s
sys 0m0.000s
[xoft hugs]$ time ./x 100000 >/dev/null
real 0m1.384s
user 0m1.376s
sys 0m0.004s
ри получении k-го элемента (k = 1,..., n) искомого списка проводится сортировка элементов подтаблицы размера k X k.нафига такая жесть?
по факту, мы имеем сортировку слиняем упорядоченных ленточек, где на каждом шаге с номером i есть i ленточек. Между i ленточками можно выбрать минимальный элемент за i шагов (если не поддерживать ленточки в отсортированном порядке или за log i (если ленточки упорядочивать по первому элементу еще не вошедшему в выходную последовательность)
соответственно, выборка 1..n элементов будет или n^2, или n*log n
Не совсем понятно, как реализовать это функционально, например, на Haskell-е? Конечно, можно извернуться и вместо использования внутреннего соcтояния (как в императивном алгоритме) усложнить структуры списков, "навесить" на них дополнительные данные и т.п. Но нельзя ли найти "исконно функциональное", красивое и идейно простое решение с лучшими характеристиками, чем тривиальное решение "в лоб", приведенное мной выше?
Поясните, пожалуйста, в чем идея алгоритма? А то через рассматривание Haskell-кода сразу не понятно, является ли это решение искусственным и "императивно вдохновленным" или оно естественное и "исконно функциональное".
unionS :: (Ord a) => [a] -> [a] -> [a]
unionS [] ys = ys
unionS xs [] = xs
unionS (x:xs) (y:ys) = case compare x y of
EQ -> x : unionS xs ys
LT -> x : unionS xs (y:ys)
GT -> y : unionS (x:xs) ys
-- | Computes the set union of a list @ of sorted lists assuming that @map head is also sorted
flattenS :: (Ord a) => [[a]] -> [a]
flattenS x:xs):ys:zss) = x : flattenS unionS xs ys):zss)
flattenS [xs] = xs
flattenS [] = []
sumS :: (Ord a, Num a) => [a] -> [a] -> [a]
sumS xs ys = flattenS $ [[x + y | y <- ys] | x <- xs]
потрясающе выглядит. Лаконично и универсально.
Еще вариант:я так понял в этом варианте дубликаты глотаются
если заюзать http://hackage.haskell.org/package/data-ordlist-0.2/docs/Dat... , то должно получиться еще проще (не проверял =\ ):
import Data.List.Ordered
-- | Computes the set union of a list @ of sorted lists assuming that @map head is also sorted
flattenS :: (Ord a) => [[a]] -> [a]
flattenS x:xs):ys:zss) = x : flattenS merge xs ys):zss)
flattenS [xs] = xs
flattenS [] = []
sumS :: (Ord a, Num a) => [a] -> [a] -> [a]
sumS xs ys = flattenS $ [[x + y | y <- ys] | x <- xs]
должно получиться еще прощев одну строчку
import Data.List.Ordered
sumS :: (Ord a, Num a) => [a] -> [a] -> [a]
sumS xs ys = foldr (\(a:as) b -> a : merge as b) [] [[x + y | y <- ys] | x <- xs]
main = print $ take 20 $ sumS [1, 7..] [1, 6..]
он корректный.Единственно, что все элементы правого списка должны быть неотрицательны.
Коротко все равно не получается.
main = print $take 20 $infSum [1 ..] [2, 4 ..]
infSum :: [Integer] -> [Integer] -> [Integer]
infSum (a:_) (b:_) = [a' + b' | x <- [(a + b) ..]
, a' <- takeWhile (<= (x - b as
, b' <- takeWhile (<= (x - a' bs
, a' + b' == x]
import Data.List
main = do
print $take 20 $infSum [1 ..] [2, 4 ..]
print $take 20 $infSum [0.5, 1.0 ..] [1.0, 2.5 ..]
infSum :: (Num a, Ord a, Enum a) => [a] -> [a] -> [a]
infSum (a':_) (b':_) = let slice l = takeWhile (< (l + 1 . dropWhile (< l) in
concatMap (\lo -> sort [a + b | a <- takeWhile (< (lo + 1 - b' as
, b <- slice (lo - a) bs]) [(a' + b') ..]
http://ideone.com/Y3QIen
import Data.List
infSum :: (Num a, Ord a, Enum a) => [a] -> [a] -> [a]
infSum (a':_) (b':_) = let slice l = takeWhile (< (l + 1 . dropWhile (< l) in
concatMap (\lo -> sort [a + b | a <- takeWhile (< (lo + 1 - b' as
, b <- slice (lo - a) bs]) [(a' + b') ..]
в одну строчкуДа, это лучшее, самое короткое и идейно простое решение из всех предложенных! Если использовать функцию merge (слияние) для упорядоченных списков чисел и правоассоциативную свертку списка (т.е. таблицы который состоит из бесконечных списков чисел, то искомая функция получается в одну строчку:
infSum :: [Int] -> [Int] -> [Int]
infSum a b = foldr (\(x:xs) ys -> x : merge xs ys) [] [ [x + y | x <- a] | y <- b]
И работает молниеносно!
Для читателей раздела напомню определения использующихся здесь функций:
foldr :: (a -> b -> b) -> b -> [a] -> b
foldr _ z [] = z
foldr f z (x:xs) = f x (foldr f z xs)
merge :: Ord a => [a] -> [a] -> [a]
merge = mergeBy compare
mergeBy :: (a -> a -> Ordering) -> [a] -> [a] -> [a]
mergeBy cmp = loop
where
loop [] ys = ys
loop xs [] = xs
loop (x:xs) (y:ys)
= case cmp x y of
GT -> y : loop (x:xs) ys
_ -> x : loop xs (y:ys)
Самым нетривиальным моментом в твоем решении мне показалось использование для свертки списков вместо обычного merge (что не работает) лямба-функции:
(\(x:xs) ys -> x : merge xs ys)
Ты не мог пояснить поподробнее?
Ты не мог пояснить поподробнее?там вся штука в том, что на каждом шаге должно вычисляться хотя бы одно значение. Тогда мы будем это значение извлекать из бесконечного цикла по-одному. И вот такое вынесение головы списка (гарантированного минимального элемента) и отвечает за конечность расчётов. Вот вам голова, а всё остальное посчитаем потом в хвосте, нагородив merge поверх merge
там вся штука в том, что на каждом шаге должно вычисляться хотя бы одно значение. Тогда мы будем это значение извлекать из бесконечного цикла по-одному. И вот такое вынесение головы списка (гарантированного минимального элемента) и отвечает за конечность расчётов. Вот вам голова, а всё остальное посчитаем потом в хвосте, нагородив merge поверх mergeДа, понятно, это важная, нетривиальная идея, связанная с ленивыми вычислениями. Спасибо, за пояснения и вообще за хороший, познавательный тред!
Оставить комментарий
yroslavasako
Я вот пытаюсь понять как по-красивому в функциональном стиле решить следующую задачу.Есть бесконечные списки A и B, каждый из которых упорядочен по возрастанию. Требуется лениво вычислить бесконечный список C, элементы которого являются суммой двух произвольных элементов списка A и B, упорядоченный по возрастанию элементов.
Для императивного случая легко придумать решение. А как его писать в функциональном стиле - не представляю. Имеется в виду получить лаконичную запись вроде решета эратосфена.