1

I've been trying to solve a problem that requires Sigma Notation (Or at least I think), but every implementation of Sigma Notation in Haskell i've come across doesn't use the index variable in its function. The particular formula i've been trying to replicate is:

enter image description here

It's used in calculating the number of trailing zeros in n!, but the best i've got is:

 sigma :: (Enum a, Num b) => a -> a -> (a -> b) -> b
 sigma i k fn = sum . map fn $ [i..k]

 zeros :: Int -> Int
 zeros n = sigma 1 n (???)

I'm trying to also create a general sigma function, that works with an index variable in f. This works, but for 100!, it gives -11 trailing zeros. Overflow?

zeros :: Int -> Int
zeros n = sigma 1 n (\i -> n `div` 5 ^ i)
        where sigma i k fn = sum $ map fn [i..k]

P.S. Im on an in-browser IDE that limits compile time. (So speed counts)

Haskelier
  • 83
  • 7
  • Isn't the `(???)` just `(\i -> floor (n / 5 ^ i))`? Assuming your formula meant to sum from `1` to `n` – J. Abrahamson Oct 13 '14 at 13:31
  • I already tried that, and it doesn't work for me, and the error messages are cryptic. – Haskelier Oct 13 '14 at 13:33
  • /tmp/haskell114913-7-1k7mzh6/Zeros.hs:7:28: No instance for (RealFrac Int) arising from a use of `floor' Possible fix: add an instance declaration for (RealFrac Int) In the expression: floor (n / 5 ^ i) In the third argument of `sigma', namely `(\ i -> floor (n / 5 ^ i))' In the expression: sigma 1 n (\ i -> floor (n / 5 ^ i)) – Haskelier Oct 13 '14 at 13:34
  • /tmp/haskell114913-7-1k7mzh6/Zeros.hs:7:37: No instance for (Fractional Int) arising from a use of `/' Possible fix: add an instance declaration for (Fractional Int) In the first argument of `floor', namely `(n / 5 ^ i)' In the expression: floor (n / 5 ^ i) In the third argument of `sigma', namely `(\ i -> floor (n / 5 ^ i))' – Haskelier Oct 13 '14 at 13:34
  • 1
    If you're using `/` with `Int`, you're going to have a bad time. Use `fromIntegral` to convert `Int` to a `Fractional` type which can be divided directly. If you want integer division, you can use `div` instead. – bheklilr Oct 13 '14 at 13:47
  • @bheklilr Yep! My code was closer to pseudocode than Haskell. – J. Abrahamson Oct 13 '14 at 13:48
  • i changed / to `div`, and floor to truncate: it rids an error, but now i've got one for truncate. – Haskelier Oct 13 '14 at 13:49
  • 1
    @Haskelier If that is the solution, please post it as such so others who have this particular problem find help and do not have to search the comments. – ThreeFx Oct 13 '14 at 13:51

2 Answers2

2

Your troubles are just coming from type mismatches. You want to do the truncate at the very end and use fractional arithmetic for the division, exponentiation, and summation, and then truncate the result at the end. Because of this, you'll need a bit more than just zeros n = sigma 1 n something:

zeros :: Integral a => a -> a
zeros n = truncate $ sigma 1 n (\i -> fromIntegral n / 5 ^ i)

The details here you need to worry about are the fromIntegral n, which converts n from an Integral a => a to a Num b => b, the ^ operator allows Integral a => a arguments as the exponent, so we don't need conversion there, and then truncate will convert back to an Integral. We could make this signature even more general and return a different Integral that we inputted, but that's not really useful in practice.

So now we can input either Int or Integer, depending on how big of a number you need to input to this function (since you mentioned 100!), but be warned that making a list of 100! elements is going to seriously eat up RAM and CPU time.

If you're really wanting to compute the trailing zeros of a large number, it's probably going to be a lot easier to convert it to a string, as in:

zeros :: (Show a, Integral a) => a -> Int
zeros = length . takeWhile (== '0') . reverse . show
bheklilr
  • 53,530
  • 6
  • 107
  • 163
  • That last one with the string needs to compute the entire factorial, so it doesn't work just by nature of that enourmous number. (The first one doesn't work either because of the in-browser IDE's time limit.). I'm pretty sure that the problem requires some sort of advanced math that's beyond my capability. – Haskelier Oct 13 '14 at 14:26
  • @Haskelier How about `fac n = product [2..n]`, and `zeros = length . takeWhile (== '0') . reverse . show . fac`? – bheklilr Oct 13 '14 at 14:41
  • no, again to slow... All the comments are saying that there is some sort of mathematical trick to it, and that it's not what you think it is. (www.codewars.com) – Haskelier Oct 13 '14 at 14:52
  • @Haskelier Are you saying that it's still too slow? Even running it in GHCi without optimizations on my computer I have to get up to `3000!` before it even takes ~0.02 seconds to compute. It actually takes longer to print out `3000!` than it does to compute the number of trailing zeros. – bheklilr Oct 13 '14 at 15:14
1
sigma i k fn = sum $ map fn [i..k]
zeros n = sigma 1 n (\i -> fromIntegral n / 5**(fromIntegral i))

Haskell does not automatically convert between integers and floating point numbers. That's why you need the fromIntegral.

svenningsson
  • 4,009
  • 1
  • 24
  • 32
  • It doesn't work, and it throws me this error: Couldn't match expected type `a0 -> [c0]' with actual type `[b0]' In the return type of a call of `map' Probable cause: `map' is applied to too many arguments In the second argument of `(.)', namely `map fn [i .. k]' In the expression: sum . map fn [i .. k] At least on the in-browser interpreter im using. – Haskelier Oct 13 '14 at 13:59
  • My bad. I changed your `sigma` function but I forgot to replace the dot with a dollar. See the edit. – svenningsson Oct 13 '14 at 14:02
  • that works, but not for numbers above 100!. Could we add the type signature to allow it something bigger than Int? I dunno. See edit on post. – Haskelier Oct 13 '14 at 14:07
  • Why are you working with numbers above `100!`? You aren't going to find any _computer_ that can efficiently handle a list of `100!` elements long. That's a massive amount of number crunching. – bheklilr Oct 13 '14 at 14:08
  • I've got a website that is giving me a test case of 100!'s trailing zeros. I think there might be a more efficient formula... – Haskelier Oct 13 '14 at 14:11
  • 1
    I think there's some confusion. The formula `f(n)` is is used in finding the number of trailing zeros in `n!`. You wouldn't pass in `100!`, you would pass in `100`. – Itai Zukerman Oct 13 '14 at 21:55