Advertisement
jejones3141

Untitled

Jun 26th, 2013
127
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. {-
  2.   Here's my current attempt at the ultimate "Fair and Square" program.
  3.   (See https://code.google.com/codejam/contest/2270488/dashboard#s=p2;
  4.   the core of the problem is: given two positive integers, M and N, how many
  5.   base 10 palindromes between M and N are squares of base 10 palindromes?)
  6.  
  7.   Best time output for the L2 input file, on a 2.8 GHz AMD Athlon II X4 630:
  8.  
  9.   real    0m0.228s
  10.   user    0m0.196s
  11.   sys     0m0.028s
  12.  
  13.   The main insights to speed up the process follow.
  14.   (4)-(5) are directly from the Google Code Jam analysis of the problem at
  15.   https://code.google.com/codejam/contest/2270488/dashboard#s=a&a=2
  16.   (8) is from Twan van Laarhoven's "Search trees without sorting" at
  17.   http://twanvl.nl/blog/haskell/SemilatticeSearchTree
  18.  
  19.  1. Look for palindromes in [ceil(sqrt(M))..floor(sqrt(N))] whose squares are
  20.     palindromes. (Following Google's analysis, we'll refer to such numbers as
  21.     Ys, and their squares as Xs.)
  22.  2. Don't look for palindromes, generate them. ceil(n/2) digits determine an
  23.     n-digit palindrome, reducing the work by a factor similar to (1).
  24.  3. The most/least significant digit of a Y can't be greater than 3.
  25.  4. Ys are precisely those palindromes that, when you square them via long
  26.     multiplication, have no carries in the addition of partial products.
  27.  5. (4) implies that the middle digit of X, which is the sum of the squares
  28.     of the digits of the palindrome being squared, must be less than 10.
  29.     Hence *none* of the digits of a Y can be greater than 3, and 3 can only
  30.     appear in Y = 3.
  31.  6. By (5), Ys can be categorized as having either zero, one, or two 2s.
  32.  7. Counting d-digit Ys is faster than generating them; only generate when
  33.     you have to. (The problem only asks "how many".)
  34.  8. By treating a binary tree as a sublattice, labeling branches with the upper
  35.     bounds of the corresponding subtrees, one can quickly search for the first
  36.     value in the tree >= a specified value.
  37.  9. Given such trees of d-digit Ys with their ordinal positions, two searches
  38.     suffice to determine how many d-digit Ys are in a given range.
  39. -}
  40.  
  41. import Prelude
  42. import Data.List
  43. import SemilatticeSearchTree
  44. import qualified Data.ByteString.Char8 as B
  45.  
  46. -- It's easy to calculate the number of n-digit Ys.
  47.  
  48. numNDigitYs :: Int -> Int
  49.  
  50. numNDigitYs 1 = 3
  51. numNDigitYs n = numTwoTwos n + numOneTwos n + numNoTwos n
  52.     where numTwoTwos n = if even n then 1 else 2
  53.           numOneTwos n = if even n then 0 else n `div` 2
  54.           numNoTwos  n = if even n then s else 2 * s
  55.                          where h = n `div` 2 - 1
  56.                                s = sum [h `choose` i | i <- [0..min h 3]]
  57.  
  58. choose :: Int -> Int -> Int
  59.  
  60. m `choose` 0 = 1
  61. m `choose` n = product [m - n + 1..m] `div` product [1..n]  
  62.  
  63. -- With partial sums from 0 to n, one subtract gives the sum from m to n
  64.  
  65. nDigitYsSums         = scanl (+) 0 (map numNDigitYs [1..])
  66. ysInDigitRange d1 d2 = nDigitYsSums !! d2 - nDigitYsSums !! (d1 - 1)
  67.  
  68. -- Now the slow part: actually generating and sorting the Ys
  69.  
  70. powersOfTen = map (10 ^) [0..]
  71.  
  72. tenToThe :: Int -> Integer
  73.  
  74. tenToThe n = powersOfTen !! n
  75.  
  76. nDigitYs :: Int -> [Integer]
  77.  
  78. nDigitYs 1 = [1,2,3]
  79. nDigitYs n = sort (oneTwos n ++ noTwos n ++ twoTwos n)
  80.     where twoTwos n
  81.               | even n    = [twoShell]
  82.               | otherwise = [twoShell, twoShell + tenToThe halfN]
  83.               where twoShell = 2 * shell
  84.           oneTwos n
  85.               | even n    = []
  86.               | otherwise = map (+ (shell + 2 * tenToThe halfN))
  87.                                 (0 : map pair [1..halfN - 1])
  88.           noTwos n
  89.               | even n    = base
  90.               | otherwise = concat [[p, p + tenToThe halfN] | p <- base]
  91.               where base  = map pairSum (noTwosChoices !! (halfN - 1))
  92.           halfN      = n `div` 2
  93.           shell      = pair 0
  94.           memoPair   = zipWith (+) powersOfTen
  95.                                    (take halfN (reverse (take n powersOfTen)))
  96.           pair i     = memoPair !! i -- tenToThe i + tenToThe (n - (i + 1))
  97.           pairSum xs = foldl' (+) shell (map pair xs)
  98.  
  99. choices :: Int -> Int-> [[Int]]
  100.  
  101. m `choices` n
  102.    | n == 0           = [[]]
  103.    | m == n           = [[m, m-1..1]]
  104.    | otherwise        = [m : c | c <- (m - 1) `choices` (n - 1)]
  105.                         ++ ((m - 1) `choices` n)
  106.  
  107. -- Think of i as n `div` 2 - 1
  108. noTwosChoices = [concat [i `choices` k | k <- [0..min 3 i]] | i <- [0..]]
  109.  
  110. -- We tag nDigitYs results with their ordinal position, and create
  111. -- semilattice search trees from the resulting pairs.
  112. -- Sigh... this is a bit of cargo cult programming; we follow the blog
  113. -- example even though we only search for palindromes.
  114.  
  115. yTree :: Int -> SearchTree (Max Integer, Max Int)
  116.  
  117. yTree n = fromList [(Max x, Max y) | (x, y) <- zip (nDigitYs n) [0..]]
  118.  
  119. yTrees  = map yTree [1..]
  120.  
  121. dDigitYTree :: Int -> SearchTree (Max Integer, Max Int)
  122.  
  123. dDigitYTree d = yTrees !! (d - 1)
  124.  
  125. -- Given two d-digit values, m and n, how many Ys are in [m..n]?
  126.  
  127. ysInRange :: Integer -> Integer -> Int -> Int
  128.  
  129. ysInRange m n d
  130.    | nVal == n = nPos - mPos + 1
  131.    | otherwise = nPos - mPos
  132.    where (mVal, mPos) = findFirst' m
  133.           (nVal, nPos) = findFirst' n
  134.          findFirst' x = case findFirst (Ge x, Any) (dDigitYTree d) of
  135.                              Just (Max i, Max j) -> (i, j)
  136.                              Nothing             -> (tenToThe d, numNDigitYs d)
  137.  
  138. -- counting decimal digits and bits
  139. -- the numbers here are big enough that instead of counting digits one by one,
  140. -- we compare with b, b^2, b^4, b^8, ... to make it O(log(# of digits)).
  141.  
  142. powersOfTwo = map (2 ^) [0..]
  143.  
  144. twoToThe n = powersOfTwo !! n
  145.  
  146. bigTwos = map (2 ^) powersOfTwo
  147. bigTens = map (10 ^) powersOfTwo
  148.  
  149. bitsIn n   = bDigits n bigTwos
  150. digitsIn n = bDigits n bigTens
  151.  
  152. bDigits :: Integer -> [Integer] -> Int
  153.  
  154. bDigits n xs = bDigits' n 1
  155.    where bDigits' n s = case lastLE n xs of
  156.                            Nothing     -> s
  157.                            Just (m, p) -> bDigits' (n `div` m) (s + twoToThe p)
  158.  
  159. lastLE :: Integer -> [Integer] -> Maybe (Integer, Int)
  160.  
  161. lastLE n xs =
  162.    let lastLE' xs prevVal prevIndex
  163.             | head xs <= n = lastLE' (tail xs) (head xs) (prevIndex + 1)
  164.            | otherwise    = if prevIndex < 0 then Nothing
  165.                                              else Just (prevVal, prevIndex)
  166.    in lastLE' xs (-1) (-1)
  167.  
  168. -- The following is derived from a response to a Stack Overflow question
  169. -- http://stackoverflow.com/questions/1623375/writing-your-own-square-root-function
  170. -- citing Crandall & Pomerance, "Prime Numbers: A Computational Perspective".
  171.  
  172. floorSqrt :: Integer -> Integer
  173.  
  174. floorSqrt 0 = 0
  175. floorSqrt n = floorSqrt' (2 ^ ((1 + bitsIn n) `div` 2))
  176.    where floorSqrt' x =
  177.               let y = (x + n `div` x) `div` 2
  178.               in if y >= x then x else floorSqrt' y
  179.  
  180. ceilSqrt :: Integer -> Integer
  181.  
  182. ceilSqrt n =
  183.    let y = floorSqrt n
  184.    in if y * y == n then y else y + 1
  185.  
  186. -- The top level, using ysInDigitRange where possible. We break the interval
  187. -- down by number of digits.
  188.  
  189. numYs :: Integer -> Integer -> Int
  190.  
  191. numYs m n
  192.    | mDigits == nDigits   = ysInRange m n mDigits
  193.    | mPower10 && nPower10 = ysInDigitRange mDigits (nDigits - 1)
  194.    | mPower10             = ysInDigitRange mDigits (nDigits - 1)
  195.                             + ysInRange (tenToThe (nDigits - 1) + 1) n nDigits
  196.    | otherwise            = ysInRange m (tenToThe mDigits - 1) mDigits
  197.                             + ysInDigitRange (mDigits + 1) (nDigits - 1)
  198.                             + ysInRange (tenToThe (nDigits - 1) + 1) n nDigits
  199.    where mDigits    = digitsIn m
  200.          nDigits    = digitsIn n
  201.          mPower10 = m == tenToThe (mDigits - 1)
  202.          nPower10 = n == tenToThe (nDigits - 1)
  203.  
  204. numXs :: Integer -> Integer -> Int
  205.  
  206. numXs m n = numYs (ceilSqrt m) (floorSqrt n)
  207.  
  208. -- A new main, heavily inspired by "Haskell I/O for Imperative Programmers"
  209. -- http://www.haskell.org/haskellwiki/Haskell_IO_for_Imperative_Programmers
  210. -- (tail skips the first line that says how many lines follow)
  211.  
  212. main = do
  213.    s <- B.getContents
  214.    let r = zipWith (curry showsResult) [1 ..] (map process (tail $ B.lines s))
  215.    mapM (putStr . ($ "\n")) r
  216.  
  217. process :: B.ByteString -> Int
  218.  
  219. process line = case map B.readInteger (B.words line) of
  220.               [Just (m, _), Just (n, _)] -> numXs m n
  221.               _                          -> -1
  222.  
  223. showsResult :: (Int, Int) -> String -> String
  224.  
  225. showsResult (c, n) = ("Case #" ++) . shows c . (": " ++) . shows n
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement