In my attempt to learn Haskell,
i have written the following piece of code to solve a classic optimization problem.
The problem at hand is to compute the sales maximizing prices, where the price is monotonically increasing,
given a sequence of i buyers, each of which will buy at a maximup price of v_i.
In mathematical terms:
given [v_i] , find [p_i] s.t. p_{i+1} >= p_i that maximises \sum_i q(v_i,p_i)
where q(a,b)=0, if b>a, q(a,b)=b b<=a
I have implemented the following code, solving the problem using what i think is a top-down dynamic programming approach.
The algorithm decides at each step whether it will increase the price, by maximising all over the remaining sequence
maxP::Int->[Int]->(Int,Int)
maxP p [] = (0,p)
maxP p q
| a<p = (fst (maxP p (tail q)),p)
| a==p = (p+fst (maxP p (tail q)),p)
| otherwise =(maximum l,p+argmax l)
where a=head q
pc=[p..a]
l=zipWith (+) pc $ map (fst) ( map ((flip maxP) (tail q)) pc )
The code is -as expected when using Haskell- an almost 1-1 implementation of a DP algorithm.
The code returns the ( Sales Sum,Price Level)
And, in order to have all the price sequence, a function is called for all [v_i]
maxPI::Int->[Int]->[Int]->[Int]
maxPI a [] b = reverse b
maxPI a c b = maxPI q (tail c) (q:b)
where (p,q) = maxP a c
I have also implemented helper functions
argmax::[Int]->Int
argmax x = mP x (-1) 0 0
mP::[Int]->Int->Int->Int->Int
mP [] maxi pos cpos = pos
mP a maxi pos cpos
| ((head a)> maxi) = mP (tail a) (head a) cpos (cpos+1)
|otherwise = mP (tail a) maxi pos (cpos+1)
Obviously, the function could (should) be optimized, to use only one run of the algorithm over the list
But my question is that, even without the aforemention optimization, the algorithm runs surprisingly fast.
So my question is the following:
Why this algorithm works so fast?
Am i simply mis-understanding the complexity of the DP algortihm?
Does Haskell employs a by default memoization of the function maxP?
Furthermore, i dislike my Haskell-ness of my code. Could you please make any suggestions?
I was expecting a much slower performance
I don't know why your intuition about how fast it should be is wrong. But I'll answer the concrete questions you have that don't require me to live inside your head:
Does Haskell employs a by default memoization of the function maxP?
Haskell, the language, has no opinion on whether maxP should be memoized or not by language implementations. GHC, the most popular implementation, will not memoize maxP as it is written here.
i dislike my Haskell-ness of my code. Could you please make any suggestions?
I have a few suggestions. The most obvious one is to use pattern-matching instead of head and tail. Like this:
maxP::Int->[Int]->(Int,Int)
maxP p [] = (0,p)
maxP p (a:as)
| a<p = (fst (maxP p as),p)
| a==p = (p+fst (maxP p as),p)
| otherwise =(maximum l,p+argmax l)
where pc=[p..a]
l=zipWith (+) pc $ map (fst) ( map ((flip maxP) as) pc )
maxPI::Int->[Int]->[Int]->[Int]
maxPI a [] b = reverse b
maxPI a c#(_:ct) b = maxPI q ct (q:b)
where (p,q) = maxP a c
mP::[Int]->Int->Int->Int->Int
mP [] maxi pos cpos = pos
mP (a:as) maxi pos cpos
| (a> maxi) = mP as a cpos (cpos+1)
|otherwise = mP as maxi pos (cpos+1)
You have quite a few extraneous parentheses. Sometimes that can be useful for readability, but they're not really doing it for me in this situation.
- l=zipWith (+) pc $ map (fst) ( map ((flip maxP) as) pc )
+ l=zipWith (+) pc $ map fst (map (flip maxP as) pc)
- | (a> maxi) = mP as a cpos (cpos+1)
+ | a>maxi = mP as a cpos (cpos+1)
In maxP, you can use compare to compute all three guards at once.
maxP::Int->[Int]->(Int,Int)
maxP p [] = (0,p)
maxP p (a:as) = case compare a p of
LT -> (fst (maxP p as),p)
EQ -> (p+fst (maxP p as),p)
GT -> (maximum l,p+argmax l)
where pc=[p..a]
l=zipWith (+) pc $ map fst (map (flip maxP as) pc)
Your computation of l can be done more clearly with a single map, or via list comprehension.
- where pc=[p..a]
- l=zipWith (+) pc $ map fst (map (flip maxP as) pc)
+ where l=map (\p' -> p'+fst (maxP p' as)) [p..a] -- OR
+ where l=[p'+fst (maxP p' as) | p' <- [p..a]]
You could consider computing the index of the maximum value and the maximum value itself in the same traversal, and reuse existing library functions, like this:
GT -> (maxv,p+maxi)
where l=[p'+fst (maxP p' as) | p' <- [p..a]]
(maxv, maxi) = maximum (zip l [0..])
This argmax will return the latest maximum, unlike your solution, which returns the earliest maximum. I'm not sure whether that matters. If it does, you could use Arg to avoid using the index in the comparison or Down to use it "the other way".
You mention fst (maxP _ as) a couple times. Might be worth doing a DRY thing there.
maxP::Int->[Int]->(Int,Int)
maxP p [] = (0,p)
maxP p (a:as) = case compare a p of
LT -> (go p,p)
EQ -> (p+go p,p)
GT -> (maxv,p+maxi)
where l=[p'+go p' | p' <- [p..a]]
(maxv, maxi) = maximum (zip l [0..])
go p' = fst (maxP p' as)
Actually, reading more carefully, it occurs to me that the whole p+maxi computation you're doing is just to recover the p' value you already have in hand! So, better:
GT -> maximum [(p'+go p', p') | p' <- [p..a]]
I keep twisting myself in knots over this last bit. Is that the same as maximum [maxP p' as | p' <- [p..a]]? Anyway, at this point, it's clear now that the EQ and GT cases are actually doing the same thing. So let's merge them. We'll move back to guards now, actually, hah!
maxP::Int->[Int]->(Int,Int)
maxP p [] = (0,p)
maxP p (a:as)
| a<p = (go p,p)
| otherwise = maximum [(p'+go p', p') | p' <- [p..a]]
where go p' = fst (maxP p' as)
In maxPI, there's no real reason to build up the list backwards and reverse when you can just build it forwards to begin with.
maxPI::Int->[Int]->[Int]
maxPI a [] = []
maxPI a c#(_:ct) = q:maxPI q ct
where (p,q) = maxP a c
This can probably be done as a scan, though I'm a bit less confident of this transformation.
maxPI::Int->[Int]->[Int]
maxPI a cs = scanl (\a' cs' -> snd (maxP a' cs')) a (tails cs)
I'm not sure I love that change, but it's one to be aware of. All told, that leaves us with this code:
maxP::Int->[Int]->(Int,Int)
maxP p [] = (0,p)
maxP p (a:as)
| a<p = (go p,p)
| otherwise = maximum [(p'+go p', p') | p' <- [p..a]]
where go p' = fst (maxP p' as)
maxPI::Int->[Int]->[Int]
maxPI a cs = scanl (\a' cs' -> snd (maxP a' cs')) a (tails cs)
This feels like pretty idiomatic Haskell to me. If you want to go up from here, you need to start thinking about algorithmic changes, not style changes.
Here's what it might look like to tweak this algorithm so that it shares computations appropriately, i.e. is a dynamic programming solution. First we define a type for tracking the info we're interested in, namely, a minimal price the current solution applies for, the payout we can get assuming we always pay more than the minimum, and the actual prices we should offer to get that payout.
import Data.List
data Path = Path
{ payout :: Int
, minPrice :: Int
, prices :: [Int]
} deriving (Eq, Ord, Read, Show)
We'll name some simple operations on these table entries. The first is to extend the current table entry under the assumption that we demand the minimum price from the current customer, given the maximum price the current customer is willing to pay.
demandMin :: Int -> Path -> Path
demandMin maxPrice path = path
{ payout = payout path + if curPrice <= maxPrice then curPrice else 0
, prices = curPrice : prices path
} where
curPrice = minPrice path
The second operation expresses our preference on payouts. It takes two entries in our table and picks the one with a better payout. If we were being robust, we'd also take the smaller minimum price, but we're going to arrange that the first argument always has the smaller minimum, so we can cheat and always take that.
maxPayout :: Path -> Path -> Path
maxPayout p p' = if payout p >= payout p' then p else p' { minPrice = minPrice p }
With these operations in place, we can write our table-update operator. Each column of our table has an entry for each possible minimum price, and we will assume the incoming column has them in order of lowest minimum price to highest. Given that, we can fill in the next column to the left by, for each row, taking the better of demanding the current minimum price or whatever excellent plan the row below came up with. Like this:
maxPayouts :: Int -> [Path] -> [Path]
maxPayouts maxPrice = scanr1 maxPayout . map (demandMin maxPrice)
Now, to run the algorithm, we can just initialize our rightmost column, then iteratively fill in columns to the left, finally taking the top-left element of the table as our answer. We have to set up the assumed invariant that rows come in sorted order, but otherwise there is almost no code to write here. So:
top :: [Int] -> Path
top prices = head $ foldr maxPayouts [Path 0 price [] | price <- sort prices] prices
Try it in ghci:
> top [1,2]
Path {payout = 3, startingPrice = 1, prices = [1,2]}
> top [1,3]
Path {payout = 4, startingPrice = 1, prices = [1,3]}
> top [2,1]
Path {payout = 2, startingPrice = 1, prices = [1,1]}
> top [3,1]
Path {payout = 3, startingPrice = 1, prices = [3,3]}
> top [1,5,3]
Path {payout = 7, startingPrice = 1, prices = [1,3,3]}
> top [1,7,3]
Path {payout = 8, startingPrice = 1, prices = [1,7,7]}
(Generally you will not care about the startingPrice field, but it's easier to just return it than to make a fresh data type that doesn't have it to return.)
It scales well; for example, top [5,10..1000] returns essentially instantly for me even without compiling or optimizing. Theoretically it should scale approximately as O(n^2), with n the length of the input list, although I didn't attempt to verify this empirically.
Related
A bit of background:
I am an amateur programmer, having picked up Haskell a few months ago, on my spare time, after a period of Mathematica programmning (my first language). I am currently going through my second Haskell book, by Will Kurt, but I still have miles to go to call myself comfortable around Haskell code. Codeabbey has been my platform for experimentation and learning so far.
I have written a piece of code to generate permutations of a given number, that deals with possible duplicate numbers, so for 588 it will internally generate 588, 858 and 885.
However, because I want to scale to pretty big input numbers (think perhaps even a hundred digits long), I don't want to output the whole list and then perform calculations on it, instead every number that is generated is checked on the spot for a certain property and if it has it, well, we have a winner, the number is returned as output and there's no need to go through the rest of the humongous list. If sadly no desired number is found and we unsuccessfully go through all possible permutations, it outputs a "0".
I have also opted to make it a command line program to feed values to it via gnu parallel for faster work.
So here is the code
import System.Environment
import Data.List
toDigits :: Integer -> [Integer]
toDigits n = map (\n -> read [n]) (show n)
fromDigits :: Integral a => [a] -> Integer
fromDigits list = fromDigitsHelperFunction list 0
fromDigitsHelperFunction :: Integral a => [a] -> Integer -> Integer
fromDigitsHelperFunction [] acc = acc
fromDigitsHelperFunction (x:[]) acc = (fromIntegral x) + acc
fromDigitsHelperFunction digits#(x:xs) acc = fromDigitsHelperFunction xs (acc + ((fromIntegral x) * 10 ^((length digits) - 1 )))
testPermutationsWithRepetition :: ([Integer],Int,[Int],[(Int,Integer)]) -> [Integer]
testPermutationsWithRepetition (digits, index, rotationMap, registeredPositions)
| index == 0 && rotationMap !! index == 0 = [0,0,0] --finish state (no more recursion). Nothing more to do
| index == digitsLength - 1 && beautyCheck (fromDigits digits) = digits
| index == digitsLength - 1 = testPermutationsWithRepetition (digits, index-1, rotationMap, registeredPositions)
| not ((index,digits!!index) `elem` registeredPositions) = testPermutationsWithRepetition (digits, index+1, rotationMap, (index,digits!!index):registeredPositions)
| rotationMap!!index == 0 = testPermutationsWithRepetition (digits, index-1, restoredRotMap, restoredRegPositions)
| rotationMap!!index > 0 && (index,digits!!index) `elem` registeredPositions = testPermutationsWithRepetition (shiftLDigits, index, subtractRot, registeredPositions)
where digitsLength = length digits
shiftLDigits = (fst splitDigits) ++ (tail $ snd splitDigits) ++ [head $ snd splitDigits]
splitDigits = splitAt index digits
restoredRotMap = (fst splitRotMap) ++ [digitsLength - index] ++ (tail $ snd splitRotMap)
splitRotMap = splitAt index rotationMap
restoredRegPositions = filter (\pos -> fst pos < index) registeredPositions --clear everything below the parent index
subtractRot = (fst splitRotMap) ++ [(head $ snd splitRotMap) - 1] ++ (tail $ snd splitRotMap)
--Frontend function for testing permutations by inputting a single parameter (the number in digit form)
testPermsWithRep :: [Integer] -> [Integer]
testPermsWithRep digits = testPermutationsWithRepetition (digits, 0, [length $ digits, (length $ digits) -1 .. 1], [])
main :: IO ()
main = do
args <- getArgs
let number = read (head args) :: Integer
let checkResult = fromDigits $ testPermsWithRep $ toDigits number
print checkResult
It's really a sequential process with an index variable that points to a certain number on the digit list and performs a recursive call on that list based on my rules. The functions tracks its progress through the digit list for visited numbers in certain positions so far (to avoid repetition following already visited paths until it gets to the last digit (index == length -1). If the number that we get there passes the beauty check, it exits with the number produced.
Now, in a Mathematica (or I guess any imperative language) I would probably implement this with a While loop and Cases for its checks, and by the logic of the program, however long it took to compute (generate the permutations and check them for validity) it would take a moderate amount of memory, just enough to hold the list of "registeredPositions" really (you could call it the record of visited digits in specific positions, so it's a variable list as we go deeper in index but gets cleaned up as we move back up). However in this case, the recursive calls stack up as it seems and the whole thing acts as a fork bomb for sufficiently large numbers (e.g 27777772222222222222222223333) and eventually crashes. Is this behaviour something that can be handled differently in Haskell or is there no way to avoid the recursion and memory hogging?
I really like Haskell because the programs make logical sense, but I would like to use it also for cases like this where performance (and resources) matters.
As a side note, my brother pointed to this Algorithm to print all permutations with repetition of numbers in C that is reasonably fast (only generates a list though) and most importantly has minimal memory footprint, although I can tell there's also recursion used in it. Other that that I'm clueless when it comes to C and I would like to stick to Haskell, if it can do what I want at the end of the day, that is.
Any help is welcome. Have a good day!
Edit:
Per Soleil's suggestion I update my post with additional info provided in the comments. Specifically:
After compiling with "ghc checking_program.hs" I run the program with "./checking program 27777772222222222222222223333". On an i5 3470 with 4GB RAM it runs for about 10 minutes and exits with a segmentation fault. On my brothers 32GB machine he let it run until it took up 20GB of RAM. No need to go further I guess. My tests were on Ubuntu via Win10 WSL. His is bare Linux
testPermsWithRep is just a front end for testPermutationsWithRepetition, so that I can only provide the number and testPermsWithRep creates the initial parameters and calls testPermutationsWithRepetition with those. It outputs exactly what testPermutationsWithRepetition outputs, either a number (in digit form) that passes the test, or [0,0,0]. Now the test, the beautyCheck function is simply a test for single digit divisors of that number, that returns True or False. I didn't include it because it really is inconsequential. It could even be just a "bigger than x number" test.
An an example, calling "testPermsWithRep [2,6,7,3]" will call "testPermutationsWithRepetition ([2,6,7,3], 0, [4,3,2,1],[])" and whatever comes out of that function, testPermsWithRep will return that as well.
The performance issue with your program doesn't have anything to do with recursion. Rather, you seem to be running up against an accumulation of a partially evaluated, lazy data structure in your rotation map. Your program will run in constant memory if you use the deepseq package to fully force evaluation of the restoredRotMap:
-- Install the `deepseq` package and add this import
import Control.DeepSeq
-- And then change this one case
... | rotationMap!!index == 0 = restoredRotMap `deepseq`
testPermutationsWithRepetition (digits, index-1, restoredRotMap, restoredRegPositions)
Compiled with ghc -O2 and using beautyMap _ = False, this runs with a fixed resident memory usage of about 6 megs.
Some other performance targets:
You might want to replace most of your Integer types with Int, as this will be faster. I think you only need Integer for the input to toDigits and the output of fromDigits, and everything else can be Int, since it's all indexes and digits.
An even bigger win will be to replace your rotation map and registered positions with better data structures. If you find yourself splicing up lists with lots of listpart1 ++ [x] ++ listpart2 calls, there are going to be enormous performance costs to that, and the linear lookups with (!!) aren't helping either.
So I am not 100% sure of this and I am also not 100% sure I understand your code.
But as far as I understand you are generating permutations without duplicates and then you are checking for some predicate wanting whatever single number that fulfils it.
I think it should help to use as many of the prelude functions as possible because afaik then the compiler understands it can optimize recursion into a loop. As a rule of thumb I was taught to avoid explicit recursion as much as possible and instead use prelude functions like map, filter and fold. Mainly you avoid reinventing the wheel this way but there also should be a higher chance of the compiler optimizing things.
So to solve your problem try generating a list of all permutations, then filter it using filter and then just do take 1 if you want the result that is found first. Because of Haskell's lazy evaluation take 1 makes it so that we are interested only in the first x in (x:xs) that a filter would return. Therefore filter will keep dropping elements from the, again lazily evaluated, list of permutations and when it finds one it stops.
I found a permutation implementation on https://rosettacode.org/wiki/Permutations#Haskell
and used it to try this call:
take 1 $ filter ((> 67890123456789012345) . fromDigits) $ permutations' $ toDigits 12345678901234567890
it has been running for like 20 minutes now and RAM usage has stayed around 230 MB.
I hope that has answered/helped you at least in some way.
+ a bonus tip: you can simplify your fromDigits to this beautiful thing:
fromDigits :: Integral a => [a] -> Integer
fromDigits = foldl shiftAndAdd 0
where shiftAndAdd acc d = 10 * acc + fromIntegral d
EDIT:
I read some more of the comments and I see you care about ignoring duplicates but I am afraid you'll have to go smarter about that, since if I understand correctly your implementation still generates all the duplicates it only throws them away after checking if they are in a list (which has O(n) complexity). And when you only care about finding one permutation that fits your predicate you drop the not fitting ones anyway.
And people have already correctly pointed out that !! is generally also very bad.
Thanks to everyone for your helpful answers and comments.
#lordQuick permuations used with filter is still terrible but that fromDigits code is a beauty, so I used it.
#k-a-buhr That's exactly what I did yesterday, also per others suggestion, I replaced all use of !! and ++. When I did the latter all memory problems disappeared. Wow! I mean I knew ++ is bad I just didn't realise how bad! We're talking orders of magnitude bad! 3M of RAM vs several GB. Also, valid point about integers. I will try that.
Oh, also a very important thing. I replaced recursive calls with until. This is the approach I would have followed in Mathematica (a NestWhile function to be exact), and I'm glad I found it in Haskell. It seemed to make things a bit faster too.
Anyway, the revised code, that solves my memory issues is here for anyone if interested.
{-compiled with "ghc -Rghc-timing -O2 checking_program_v3.hs"-}
import System.Environment
import Data.List
--A little help with triples
fstOfThree (a, _, _) = a
sndOfThree (_, b, _) = b
thrOfThree (_, _, c) = c
--And then some with quads
fstOfFour (a, _, _, _) = a
sndOfFour (_, b, _, _) = b
thrOfFour (_, _, c, _) = c
--This function is a single pass test for single digit factors
--It will be called as many times as needed by pryForSDFactors
trySingleDigitsFactors :: (Bool, Integer, [Integer]) -> (Bool, Integer, [Integer])
trySingleDigitsFactors (True, n, f) = (True, n, f)
trySingleDigitsFactors (b, n, []) = (b, n, [])
trySingleDigitsFactors (b, n, (f:fs))
| mod n f == 0 = (True, div n f, fs)
| otherwise = trySingleDigitsFactors (False, n, fs)
--This function will take a number and repeatedly divide by single digits till it gets to a single digit if possible
--Then it will return True
pryForSDFactors :: Integer -> Bool
pryForSDFactors n
| sndOfThree sdfTry < 10 = True
| fstOfThree sdfTry == True = pryForSDFactors $ sndOfThree sdfTry
| otherwise = False
where sdfTry = trySingleDigitsFactors (False, n, [7,5,3,2])
toDigits :: Integer -> [Integer]
toDigits n = map (\n -> read [n]) (show n)
fromDigits :: Integral a => [a] -> Integer
fromDigits = foldl shiftAndAdd 0
where shiftAndAdd acc d = 10 * acc + fromIntegral d
replaceElementAtPos :: a -> Int -> [a] -> [a]
replaceElementAtPos newElement pos [] = []
replaceElementAtPos newElement 0 (x:xs) = newElement:xs
replaceElementAtPos newElement pos (x:xs) = x : replaceElementAtPos newElement (pos-1) xs
checkPermutationsStep :: ([Integer],Int,[Int],[(Int,Integer)]) -> ([Integer],Int,[Int],[(Int,Integer)])
checkPermutationsStep (digits, index, rotationMap, registeredPositions)
| index == digitsLength - 1 = (digits, index-1, rotationMap, registeredPositions)
| not ((index, digitAtIndex) `elem` registeredPositions) = (digits, index+1, rotationMap, (index,digitAtIndex):registeredPositions)
| rotationAtIndex == 0 = (digits, index-1, restoredRotMap, restoredRegPositions)
| rotationAtIndex > 0 && (index, digitAtIndex) `elem` registeredPositions = (shiftLDigits, index, subtractRot, registeredPositions)
where digitsLength = length digits
digitAtIndex = head $ drop index digits
rotationAtIndex = head $ drop index rotationMap
--restoredRotMap = (fst splitRotMap) ++ [digitsLength - index] ++ (tail $ snd splitRotMap)
restoredRotMap = replaceElementAtPos (digitsLength - index) index rotationMap
--splitRotMap = splitAt index rotationMap
restoredRegPositions = filter (\pos -> fst pos < index) registeredPositions --clear everything below the parent index
shiftLDigits = (fst splitDigits) ++ (tail $ snd splitDigits) ++ [head $ snd splitDigits]
splitDigits = splitAt index digits
--subtractRot = (fst splitRotMap) ++ [(head $ snd splitRotMap) - 1] ++ (tail $ snd splitRotMap)
subtractRot = replaceElementAtPos (rotationDigitAtIndex - 1) index rotationMap
rotationDigitAtIndex = head $ drop index rotationMap
checkConditions :: ([Integer],Int,[Int],[(Int,Integer)]) -> Bool
checkConditions (digits, index, rotationMap, registeredPositions)
| (index == 0 && rotationAtIndex == 0) || ((index == (length digits) - 1) && pryForSDFactors (fromDigits digits)) = True
| otherwise = False
where rotationAtIndex = head $ drop index rotationMap
testPermsWithRep :: Integer -> Integer
testPermsWithRep n
| sndOfFour computationResult == 0 && (head . thrOfFour) computationResult == 0 = 0
| otherwise = (fromDigits . fstOfFour) computationResult
where computationResult = until checkConditions checkPermutationsStep (digitsOfn, 0 , [digitsLength, digitsLength -1 .. 1], [])
digitsOfn = toDigits n
digitsLength = length digitsOfn
main :: IO ()
main = do
args <- getArgs
let inputNumber = read (head args) :: Integer
let checkResult = testPermsWithRep inputNumber
print checkResult
Now, bear in mind that this code, as I've mentioned, checks for a condition of each generated permutation (single digit factors) on the spot, and moves on if False, but it's pretty easy to repurpose it for output list generation.
Sure it's now just inefficient in terms of big O complexity (scales terribly), and I was at first thinking of replacing lists with Data.Map because that's what I've learned so far (though not so comfortable with maps yet).
I've also read that there's a more efficient replacement for read since that's also called a lot for numbers-to-digits conversions.
# lordQuick I don't know about HashMaps or vectors yet but I'm still learning. Every little optimization will pay off in computation time because this is my first piece of "practical" code, not just Codeabbey credit
Cheers!
Here is a solution using a more efficient, insertion-based algorithm to compute unique permutations:
import Data.List
permutationsNub :: Eq a => [a] -> [[a]]
permutationsNub = foldr (concatMap . insert) [[]]
where insert y = foldr combine [[y]] . (zip <*> tail . tails)
where combine (x, xs) xss = (y : x : xs) :
if y == x then [] else map (x :) xss
headDef :: a -> [a] -> a
headDef x [] = x
headDef x (h : t) = h
fromDigits :: Integral a => [a] -> Integer
fromDigits = foldl1' ((+) . (10 *)) . map fromIntegral
toDigits :: Integer -> [Int]
toDigits = map (read . pure) . show
firstValidPermutation :: (Integer -> Bool) -> Integer -> Integer
firstValidPermutation p =
headDef 0 .
filter p .
map fromDigits .
permutationsNub .
toDigits
The basic idea is that, given the unique permutations of a list's tail, we can compute the unique permutations of the whole list by inserting its head into all of the tail's permutations, in every position that doesn't follow an occurrence of the head (to avoid creating duplicates). From my tests, permutationsNub seems to be faster than permutations from Data.List even when the input contains no repetitions. However, unlike that function, it consumes its input eagerly and thus cannot handle an infinite input. Exercise: Prove this algorithm's correctness.
to be continued
I'm relatively new to haskell, but in my searching I couldn't find an easy way to conditionally fold a list. i.e. When an element satisfies a condition (like in filter) to fold that element by a function (like foldr and foldl).
My workaround was to write the following helper function, then apply map to change the resulting list of pairs as my situation required.
-- This function returns tuples containing the elements which
-- satisfy `cond` folded right, adding 1 to the second value
-- in each pair. (`snd` pair starts at 0)
-- Condition takes a single value (similar to `filter`)
-- NOTE: list cannot end with token
foldrOn cond list =
if (length list) > 0 then
if cond (head list) then
do
let tmp = foldrOn cond (tail list)
(fst (head tmp), snd (head tmp) + 1) : (tail tmp)
-- fold token into char after it
else
(head list, 0) : (foldrOn cond (tail list))
-- don't fold token
else
[] -- base case len list = 0
foldlOn cond list = ...
For example, the use-case would be something along the lines of wanting to remove the zeros in the following lists but remember how many were removed between each value.
-- the second value in each resultant pair represents the number of
-- zeroes preceding the corresponding first value in the original list.
foldrOn (== 0) [1,0,0,0,0,0,1,0,0,0,1] -- [(1,0),(1,5),(1,3)]
foldrOn (== 0) [1,0,0,12,0,13] -- [(1,0),(12,2),(13,1)]
Is there a better way to accomplish this?
Additionally, can this be done more optimally?
First of all,
foldrOn :: Num t => (a -> Bool) -> [a] -> [(a, t)]
-- foldrOn (== 0) [1,0,0,0,0,0,1,0,0,0,1] -- [(1,0),(1,5),(1,3)]
foldrOn p xs = foldr g [] xs
where
g x [] = [(x,0)]
g x ((y,n):r)
| p x = ((y,n+1):r)
g x r = ((x,0):r)
This is the simplest, though it is recursive, i.e. will force the whole list to the end before starting returning its result.
To make it maximally lazy we'd have to use a lazy left fold. The skipping over the p-satisfying elements is still a recursive step, but at least the process will pause between each such span.
Lazy left fold is usually implemented as a foldr with additional argument being passed left to right along the list:
foldlOn :: Num t => (a -> Bool) -> [a] -> [(a, t)]
-- foldlOn (== 0) [1,0,0,0,0,0,1,0,0,0,1] -- [(1,0),(1,5),(1,3)]
foldlOn p xs = foldr g z xs 0
where
g x r i | p x = r (i+1)
| otherwise = (x,i) : r 0
z _i = []
Or you could combine span/break and unfoldr to do the same.
You might find a way to use groupBy with some post-processing step:
GHCi> groupBy (\a b -> (==0) b) [1,0,0,0,0,0,1,0,0,0,1]
[[1,0,0,0,0,0],[1,0,0,0],[1]]
GHCi> groupBy (const (==0)) [1,2,0,0,1,0,1]
[[1],[2,0,0],[1,0],[1]]
Finishing this should not be a problem.
You can always bring some builtin machinery. The Data.List library is quite powerful:
import Data.List(mapAccumL)
import Data.Maybe(catMaybes)
foldrOn cond = catMaybes . snd . mapAccumL combine 0 where
combine a el =
if cond el then (a + 1, Nothing)
else (0, Just (el, a))
What's going on
Essentially, foldrOn cond is a composition of the following functions:
mapAccumL combine 0 which advances along the list modifying each element by information about the number of recently skipped entities (starting the count at 0 and resetting it whenever we find something that doesn't match the cond predicate).
snd which discards the final state from the mapAccumL's result
catMaybes which removes the Maybe layer and leaves only the "present" values.
Let's start by using pattern matching to make your own implementation more idiomatic, more obviously correct, and also (much) faster. We can also use guards in an idiomatic fashion rather than if/then/else; this is rather less important. There's also no reason to use do here, so we won't.
foldrOn _cond [] = []
foldrOn cond (hd : tl)
| cond hd
= case foldrOn cond tl of
(x, y) : tl' -> (x, y + 1) : tl'
-- fold token into char after it
[] -> error "String ended on token."
| otherwise
= (hd, 0) : foldrOn cond tl
-- don't fold token
This is ... okay. But as Will Ness suggests, we don't actually gain anything by consing an "incomplete" element onto the result list. We can instead count up the cond-satisfying tokens until we reach the end of the block, and then produce a complete element. I think this makes the code a little easier to understand, and it should also run a little bit faster.
foldrOn cond = go 0
where
go count (hd : tl)
| cond hd
= go (count + 1) tl -- Don't produce anything; just bump the count
| otherwise
= (hd, count) : go 0 tl -- Produce the element and the count; reset the count to 0
go count []
| count == 0
= []
| otherwise
= error "List ended on a token."
To actually run faster, you might need to tell the compiler explicitly that you really want to calculate the counts. You probably don't need to understand this part just yet, but it looks like this:
-- At the top of the file, add this line:
{-# LANGUAGE BangPatterns #-}
foldrOn cond = go 0
where
go !count (hd : tl)
| cond hd
= go (count + 1) tl -- Don't produce anything; just bump the count
| otherwise
= (hd, count) : go 0 tl -- Produce the element and the count; reset the count to 0
go count []
| count == 0
= []
| otherwise
= error "List ended on a token."
This can be written as a fold in the manner Will Ness demonstrates.
Note: while it's possible to avoid the BangPatterns language extension, doing so is a bit annoying.
I'm trying to write some haskell code that calculates a property given some inputs to it. I'll be generic since I have a few different applications I'd like to do with this style of program.
--some random calculation
longCalc a b c d e f g = a*b+c-d/e*f+g
I want to find the combination of inputs that gives the lowest result. So I generate a list of pairs.
allPairs = [(show [a,b,c,d,e,f,g], longCalc a b c d e f g) | a<-[1..40],b<-[1..40],c<-[1..40],d<-[1..40],e<-[1..40],f<-[1..40],g<-[1..40]]
minimum' :: Ord a => [(t, a)] -> (t, a)
minimum' [] = error "minimum of empty list"
minimum' (x:xs) = minTail x xs
where minTail currentMin [] = currentMin
minTail (m, n) (p:ps)
| n > (snd p) = minTail p ps
| otherwise = minTail (m, n) ps
Now I have a list with a descriptor of what the inputs were along with the answer.
bestAnswer = minimum' allPairs
I'm not so sure this is the most efficient way of doing this calculation.
In addition I would like to see a progressive calculation of the best answer.
onlyLessFunc f _ [] = []
onlyLessFunc f y (x:xs)
| f(x) < f(y) = [x] ++ onlyLessFunc f x xs
| otherwise = onlyLessFunc f x xs
This function should produce a list where only better results get added to the end.
How might I go about making this more efficient? I am currently seeing that my approach uses tons of memory (all of it to be specific).
You can boost the efficiency of your proposed minimum' with:
minimum' :: Ord a => [(t, a)] -> (t, a)
minimum' [] = error "minimum of empty list"
minimum' (x:xs) = minTail x xs
where minTail m#(xm, fm) (xf#(x, f): xs) | f < fm = minTail xf xs
| otherwise = minTail m xs
minTail cm [] = cm
with some extra techniques, we can definitely boost it a but further, but in general this will not boost performance significantly.
A second optimization that will have a huge impact on the use of memory, is not to construct such function with allPairs, but to pass the list immediately into the function. Now Haskell can garbage collect the list elements that are already evaluated. So we can call it with:
minimum' [([a,b,c,d,e,f,g], longCalc a b c d e f g) | a<-[1..40],b<-[1..40],c<-[1..40],d<-[1..40],e<-[1..40],f<-[1..40],g<-[1..40]]
But still we will not boost the algorithm to the levels that constraint programming and integer linear programming can. Usually those mechanisms aim to exploit relations in the function, and thus restrict the domains of the variables from the moment a first value is calculated. For integer linear programming (ILP) this is usually done with an optimization technique called cutting plane.
As for your second question, again we can not boost this in terms of time complexity, but we can boost performance with:
onlyLessFunc f y = go (f y)
where go _ [] = []
go fy (x:xs) | fx < fy = x : go fx xs
| otherwise = go fy xs
where fx = f x
But again, as said, you are actually trying to optimize the wrong part of the algorithm. Usually it is more beneficial to prevent generating all those values in the first place.
How can I improve the the following rolling sum implementation?
type Buffer = State BufferState (Maybe Double)
type BufferState = ( [Double] , Int, Int )
-- circular buffer
buff :: Double -> Buffer
buff newVal = do
( list, ptr, len) <- get
-- if the list is not full yet just accumulate the new value
if length list < len
then do
put ( newVal : list , ptr, len)
return Nothing
else do
let nptr = (ptr - 1) `mod` len
(as,(v:bs)) = splitAt ptr list
nlist = as ++ (newVal : bs)
put (nlist, nptr, len)
return $ Just v
-- create intial state for circular buffer
initBuff l = ( [] , l-1 , l)
-- use the circular buffer to calculate a rolling sum
rollSum :: Double -> State (Double,BufferState) (Maybe Double)
rollSum newVal = do
(acc,bState) <- get
let (lv , bState' ) = runState (buff newVal) bState
acc' = acc + newVal
-- subtract the old value if the circular buffer is full
case lv of
Just x -> put ( acc' - x , bState') >> (return $ Just (acc' - x))
Nothing -> put ( acc' , bState') >> return Nothing
test :: (Double,BufferState) -> [Double] -> [Maybe Double] -> [Maybe Double]
test state [] acc = acc
test state (x:xs) acc =
let (a,s) = runState (rollSum x) state
in test s xs (a:acc)
main :: IO()
main = print $ test (0,initBuff 3) [1,1,1,2,2,0] []
Buffer uses the State monad to implement a circular buffer. rollSum uses the State monad again to keep track of the rolling sum value and the state of the circular buffer.
How could I make this more elegant?
I'd like to implement other functions like rolling average or a difference, what could I do to make this easy?
Thanks!
EDIT
I forgot to mention I am using a circular buffer as I intend to use this code on-line and process updates as they arrive - hence the need to record state. Something like
newRollingSum = update rollingSum newValue
I haven't managed to decipher all of your code, but here is the plan I would take for solving this problem. First, an English description of the plan:
We need windows into the list of length n starting at each index.
Make windows of arbitrary length.
Truncate long windows to length n.
Drop the last n-1 of these, which will be too short.
For each window, add up the entries.
This was the first idea I had; for windows of length three it's an okay approach because step 2 is cheap on such a short list. For longer windows, you may want an alternate approach, which I will discuss below; but this approach has the benefit that it generalizes smoothly to functions other than sum. The code might look like this:
import Data.List
rollingSums n xs
= map sum -- add up the entries
. zipWith (flip const) (drop (n-1) xs) -- drop the last n-1
. map (take n) -- truncate long windows
. tails -- make arbitrarily long windows
$ xs
If you're familiar with the "equational reasoning" approach to optimization, you might spot a first place we can improve the performance of this function: by swapping the first map and zipWith, we can produce a function with the same behavior but with a map f . map g subterm, which can be replaced by map (f . g) to get slightly less allocation.
Unfortunately, for large n, this adds n numbers together in the inner loop; we would prefer to simply add the value at the "front" of the window and subtract the one at the "back". So we need to get trickier. Here's a new idea: we'll traverse the list twice in parallel, n positions apart. Then we'll use a simple function for getting the rolling sum (of unbounded window length) of prefixes of a list, namely, scanl (+), to convert this traversal into the actual sums we're interested in.
rollingSumsEfficient n xs = scanl (+) firstSum deltas where
firstSum = sum (take n xs)
deltas = zipWith (-) (drop n xs) xs -- front - back
There's one twist, which is that scanl never returns an empty list. So if it's important that you be able to handle short lists, you'll want another equation that checks for these. Don't use length, as that forces the entire input list into memory before starting the computation -- a potentially lethal performance mistake. Instead add a line like this above the previous definition:
rollingSumsEfficient n xs | null (drop (n-1) xs) = []
We can try these two out in ghci. You'll notice that they do not quite have the same behavior as yours:
*Main> rollingSums 3 [10^n | n <- [0..5]]
[111,1110,11100,111000]
*Main> rollingSumsEfficient 3 [10^n | n <- [0..5]]
[111,1110,11100,111000]
On the other hand, the implementations are considerably more concise and are fully lazy in the sense that they work on infinite lists:
*Main> take 5 . rollingSums 10 $ [1..]
[55,65,75,85,95]
*Main> take 5 . rollingSumsEfficient 10 $ [1..]
[55,65,75,85,95]
Efficient implementation for rolling sum in haskell-
rollingSums :: Num a => Int -> [a] -> Maybe [a]
rollingSums n xs | n <= 0 = Nothing
| otherwise = Just $ if length as == n then go (sum as) xs bs else []
where
(as, bs) = splitAt n xs
go s xs [] = [s]
go s xs (y:ys) = s : go (s + y - head xs) (tail xs) ys
Asuming that - sum((i+1)...(i+1+n)) = sum(i..(i+n)) - arr[i] + arr[i+n+1]
I want to split [a] into ([a], [a]) by a pivot value and I have my code
splitList :: (Ord a) => a -> [a] -> ([a],[a])
splitList pivot list =
([x | x <- list, x <= pivot], [x | x <- list, x > pivot])
But it iterates the list twice to generate two lists, is there a way to iterate only once?
There are two possibilities, depending on if you want a tail recursive solution (and don't care about reversing the order of elements), or a solution that consumes its argument lazily.
The lazy solution decides if the first element of the list goes into the first or into the second part and uses a simple recursion to process the rest of the list. This would be the preferred solution in most cases as laziness is usually more important than tail recursion:
splitList :: (Ord a) => a -> [a] -> ([a],[a])
splitList _ [] = ([], [])
splitList p (x : xs)
| x <= p = (x : l, r)
| otherwise = (l, x : r)
where
~(l, r) = splitList p xs
However in some cases you care neither for the ordering of elements nor for laziness, but instead for speed. (For example when implementing a sorting algorithm.) Then a variant that uses an accumulator to build the result (see Accumulating Parameters: Getting rid of the 'almost' in "almost tail recursive" ) to achieve tail recursion would be more appropriate:
splitListR :: (Ord a) => a -> [a] -> ([a],[a])
splitListR pivot = sl ([], [])
where
sl acc [] = acc
sl (l, g) (x : xs)
| x <= pivot = sl (x : l, g) xs
| otherwise = sl (l, x : g) xs
It's generally considered good style to avoid hand-rolling your recursion; instead you can use a folding function like so:
splitList pivot = foldr triage ([],[])
where
triage x ~(lows, highs)
| x <= pivot = (x:lows, highs)
| otherwise = (lows, x:highs)
Of course it's even better style to make use of a preexisting function that does exactly what you need, i.e. partition. :)
If you want to write this from scratch, you can maintain two lists, one for small items, one for large. First I'll write the wrapper:
splitList :: (Ord a) => a -> [a] -> ([a],[a])
splitList pivot input = spL input [] [] where
OK, so I"m just calling spL and giving it two empty lists to start off with. Because I'm using a where block, I'll not need to pass the pivot around, so only the three lists that are changing get passed. If we haven't got anything left in the input, we're done and should return the answer:
spL [] smalls larges = (smalls,larges)
Now as you'll see, we'll actually make smalls and larges backwards, so if you don't like that, replace the final answer pair there with (reverse smalls,reverse larges). Let's deal with some input now:
spL (i:input) smalls larges | i <= pivot = spL input (i:smalls) larges
| otherwise = spL input smalls (i:larges)
So we pop it on the front of the smalls if it's small enough.
The reason for pushing on the front of the list is it saves us iterating through to the end of the list every time. You can always reverse to obtain the original ordering if that matters to you, like I said.
All together we get:
splitList :: (Ord a) => a -> [a] -> ([a],[a])
splitList pivot input = spL input [] [] where
spL [] smalls larges = (smalls,larges)
spL (i:input) smalls larges | i <= pivot = spL input (i:smalls) larges
| otherwise = spL input smalls (i:larges)
import Data.List (partition)
splitList pivot = partition (<= pivot)
http://www.cs.indiana.edu/pub/techreports/TR27.pdf of 19761 suggests the following:
import Control.Applicative
partition3 [] p = ZipList [[], [], []]
partition3 (x:xs) p
| x < p = ZipList [(x:),id,id] <*> partition3 xs p
| x > p = ZipList [id,id,(x:)] <*> partition3 xs p
| True = ZipList [id,(x:),id] <*> partition3 xs p
using it, we write
splitList pivot list = (a++b, c)
where
[a,b,c] = getZipList $ partition3 list pivot
1 as seen here.