views:

115

answers:

3

Hi

I am doing problem 112 on Project Euler and came up with the following to test the example case (I'll change the number in answer to 0.99 to get the real answer):

isIncre  x | x == 99 = False
        | otherwise = isIncre' x
       where
        isIncre' x = ???

isDecre  x = isIncre (read $ reverse $ show x :: Int)
isBouncy x = (isIncre x == False) && (isDecre x == False)

bouncers x = length [n|n<-[1..x],isBouncy n]
nonBouncers x = length [n|n<-[1..x],(isBouncy n) == False] 

answer = head [x|x<-[1..],((bouncers x) / (nonBouncers x)) == 0.5]

But what I don't know how to do is define a function isIncre' which tests to see if the digits in a number are greater than or equal to the one on their left. I know it needs to be done recursively but how?

On a side note, I know I can only use / on two floating point numbers but how can I make the output of bouncers to be floating point number instead of an integer?

Edit:

Thanks for the help, but it didn't like the = when I changed isIncre to:

isIncre  x | x <= 99 = False
           | otherwise = isIncre' (mshow x)
                        where
                            isIncre' (x:y:xs) = (x <= y) && (isIncre' (y:xs))
          isIncre' _ = True
+2  A: 

If you have a string representation of an integer number, you could write the isIncre function like this (ord converts a character to an integer and string is just a list of chars):

isIncre (x:y:xs) = ord x <= ord y && isIncre (y:xs)
isIncre _ = True

It could be even nicer to write the isIncre function without ord, working on any ordered type, then combine it with "map ord" when you call it instead. The implementation would then be just:

isIncre (x:y:xs) = x <= y && isIncre (y:xs)
isIncre _ = True

That could be called like this, if x is an integer number

isIncre (map ord (show x))
Freed
Since `Char` is an instance of `Ord` and since the digits have their natural ordering in the ASCII table, there is no need to call `ord`, in this case.
Stephan202
I tried using your second function as `isIncre'` from my original but it didn't like the `=`. Edited the question to reiterate
Jonno_FTW
Jonno: just replace *all of* `isIncre`.
Stephan202
Thanks for the help
Jonno_FTW
@Stephan202, you're right. I did this from memory and couldn't remember if Char was Ord or not.
Freed
Ok, after compiling, it took 0.5 seconds to calculate the example case of %50 to be 538, but is now taking forever to get to %99. Are there an optimisations I have neglected?
Jonno_FTW
Jonno: yes, there are. I'll update my answer in a sec.
Stephan202
+3  A: 
  1. The number 0.99 cannot be represented exactly in base 2. Hence you may want to avoid the use of floating point numbers for this assignment. Instead, to see whether exactly 99% of the numbers <= x are bouncers, test whether

    100 * (x - bouncers x) == x
    

    This works because it is (mathematically) the same as (x - bouncers x) == x / 100, which is true if (x - bouncers x) (the number of non-bouncy numbers) is 1% of x. Observe that there is thus no need to define nonBouncers.

  2. Also, another way to define bouncers is

    bouncers x = length $ filter isBouncy [1..x]
    
  3. However, you should reconsider your design. Currently you are recalculating the number of bouncy numbers up to x, for every x that you try. So a lot of work is being done over and over. What you may instead want to do, is generate a sequence of tuples (x, n), where n is the number of bouncy numbers <= x. Observe that if there are n bouncy numbers <= x, then there are either n or n + 1 bouncy number <= x + 1.

    More specifically, to calculate (x + 1, n'), all you need is (x, n) and the output of isbouncy (x + 1).

Stephan202
Nice catch. To me, "100 * bouncers x == 99 * x" would be more readable. Just a matter of taste though.
Freed
I don't see how your formula for finding when it is 99% works
Jonno_FTW
I added some more explanation. Let me know whether it's clear.
Stephan202
Thanks for that. I just need to rewrite the last two lines
Jonno_FTW
+1  A: 

I would use really nice functional version of isIncre if you have string representation of intetger.

isIncre :: (Ord a) => [a] -> Bool
isIncre list = and $ zipWith (<=) list (tail list)

If not, just compose it with show.

isIncreNum :: Integer -> Bool
isIncreNum = isIncre . show
Matajon