views:

488

answers:

3

Hi

I am doing yet another euler project question (38). I have this function which returns a list of numbers but what I need is that list of numbers to be one number. It calculates the concatenated product of an integer.

f (a,b) = a*b
conProInt x n  = map f (zip (replicate n x) ([1..n]))

prob38 = maximum [ (conProInt (x) (n)) | x <- [100..500], n <- [1..9], (sort $ nub $ (decToList $ (conProInt x n) )) == (sort $ (decToList $ (conProInt x n) )), (sort $ nub $ (decToList $ (conProInt x n))) == [1..9] ]

eg:

conProInt 192 3

returns: [192,384,576]

what I need returned is: 192384576

I have searched around and can't find a function or think of a function I could construct that would deliver what I need. How would I go about this?

EDIT:

I have updated the script to incorporate faster concatenation, but it doesn't return the correct result:

f (a,b) = a*b
conProInt x n  =( combine (map f (zip (replicate n x) ([1..n]))))
prob38 = maximum [ (conProInt (x) (n)) | x <- [1..50000], n <- [2..40], (sort $ nub $ (decToList $ (conProInt x n) )) == (sort $ (decToList $ (conProInt x n) )), (sort $ nub $ (decToList $ (conProInt x n))) == [1..9] ]

I'm pretty sure the pandigital test:

(sort $ nub $ (decToList $ (conProInt x n) )) == (sort $ (decToList $ (conProInt x n) )), (sort $ nub $ (decToList $ (conProInt x n))) == [1..9]

won't fail and I tried to make the search as large as possible but the maximum 9-pandigital I got was 986315724, any suggestions? Is the range of values for n a very large one?

+5  A: 

You can use this function to concatenate a list of numbers:

concatNumbers :: [Int] -> String
concatNumbers = concat . map show

If you want the function to return the concatenation as a number, you can use read.

Martijn
concat . map = concatMap, no?
Chuck
Or heck, even `concatNumbers = (>>= show)` would work :)
ephemient
+6  A: 

Going via Strings is probably easiest:

read $ concat $ map (show) [192,384,576]

Though you'll probably need to add a type signature:

Prelude> (read $ concat $ map (show) [192,384,576]) :: Int
192384576
thsutton
Thanks, but now I am getting some rather strange behaviour. On typing `conProInt 93 5` as a sample, it returned the answer: -1626048847. What could be the cause?
Jonno_FTW
@Jonno: Integer overflow. 32-bit integers — even unsigned ones — can only represent numbers in the low billions. The result of that function is much higher.
Chuck
@Jonno: If `Int` is too small, try using `Integer` instead. Or, if you want to make it a function, any type in `Num` will work: concatDigits :: (Num m, Show m, Num n, Read n) => [m] -> n concatDigits = read . concat . map (show)
thsutton
+1  A: 

Here's an example of how to concatenate digits without converting to and from character strings.

-- foldl1' is a strict fold.  "foldl1" would also work...
import Data.List (foldl1')    

-- Combine two numbers such that their digits are concatenated.
-- op 1 23 = 123, op 0 12 = 12, op 12345 67 = 1234567
op :: Int -> Int -> Int
op a b = a * power 10 (numDigits b) + b

-- How many digits does a positive number have?
numDigits :: Int -> Int
numDigits x = length . takeWhile (>= 1) . iterate (`div` 10) $ x

-- Take a positive number and raise it to a positive power.
-- power 5 2 = 25, power 10 3 = 1000
power :: Int -> Int -> Int
power x y = foldl1' (*) . take y $ repeat x

-- Take a list of numbers, and concatenate all their digits.
combine :: [Int] -> Int
combine xs = foldl1' op xs

example run:

Prelude> :m +Data.List
Prelude Data.List> let power x y = foldl1' (*) . take y $ repeat x
Prelude Data.List> let numDigits = length . takeWhile (>=1) . iterate (`div` 10)
Prelude Data.List> let op a b = a * power 10 (numDigits b) + b
Prelude Data.List> let combine xs = foldl1' op xs
Prelude Data.List> combine [192, 384, 576]
192384576
Prelude Data.List>
Michael Steele
That assumes numbers between 100 and 999, actually. Which, luckily, should be the case when solving Project Euler problem 38, but still...
ephemient
ephemient: You're right. He asked for concatenation, and I gave him a way to combine digits representing different 'places' (millions, thousands, etc.) instead.It's been rewritten to concatenate any list of positive Ints.
Michael Steele
`power = (^)`, and I'd personally write `numDigits x = fromJust $ findIndex (> x) $ iterate (*10) 1` because integer multiplication is faster than integer division, but now I'm just nitpicking :)
ephemient
(^) is good to know about. It's defined in GHC.Real, which for some reason doesn't appear at http://www.haskell.org/ghc/docs/latest/html/libraries/index.html. Now I can stop writing that by hand all over the place.Both of our numDigits are pretty inefficient since they generate a list to do their work.
Michael Steele
`(^) :: (Num a, Integral b) => a -> b -> a` and `(**) :: (Floating a) => a -> a -> a` are both defined in `Prelude`. I'd like to think that my `numDigits` is also more efficient because it uses a constant list that can be lifted out and shared, but I haven't checked what GHC does -- it might be smart enough to skip constructing the list altogether.
ephemient
I posted some benchmarks at http://blog.michaelsteele.us/2009/10/optimization-with-criterion/
Michael Steele
@Michael, thank for that optimisation there, but after running the program I keep getting an incorrect solution. I have editted my main post with the updated script.
Jonno_FTW
@Jonno_FTW That would be because of a bug in my code. numDigits6 x = SF.length . SF.takeWhile (<= x) $ SF.iterate (*10) 1. I'll update the post tonight, and actually check that they all give the correct answer this time.
Michael Steele