2014년 4월 24일 목요일

Euler Project: Problem 25

The Fibonacci sequence is defined by the recurrence relation:
Fn = Fn−1 + Fn−2, where F1 = 1 and F2 = 1.
Hence the first 12 terms will be:
F1 = 1
F2 = 1
F3 = 2
F4 = 3
F5 = 5
F6 = 8
F7 = 13
F8 = 21
F9 = 34
F10 = 55
F11 = 89
F12 = 144
The 12th term, F12, is the first term to contain three digits.
What is the first term in the Fibonacci sequence to contain 1000 digits?

Prelude Data.List Factory.Math.Fibonacci> elemIndex (head $ filter (> 10^(1000-1)) $ fibonacci) fibonacci
Just 4782

Euler Project: Problem 24

A permutation is an ordered arrangement of objects. For example, 3124 is one possible permutation of the digits 1, 2, 3 and 4. If all of the permutations are listed numerically or alphabetically, we call it lexicographic order. The lexicographic permutations of 0, 1 and 2 are:
012   021   102   120   201   210
What is the millionth lexicographic permutation of the digits 0, 1, 2, 3, 4, 5, 6, 7, 8 and 9?

Prelude Data.List> (sort $ permutations "0123456789") !! (1000000 - 1)
"2783915460"

2014년 4월 23일 수요일

Euler Project: Problem 23

A perfect number is a number for which the sum of its proper divisors is exactly equal to the number. For example, the sum of the proper divisors of 28 would be 1 + 2 + 4 + 7 + 14 = 28, which means that 28 is a perfect number.
A number n is called deficient if the sum of its proper divisors is less than n and it is called abundant if this sum exceeds n.
As 12 is the smallest abundant number, 1 + 2 + 3 + 4 + 6 = 16, the smallest number that can be written as the sum of two abundant numbers is 24. By mathematical analysis, it can be shown that all integers greater than 28123 can be written as the sum of two abundant numbers. However, this upper limit cannot be reduced any further by analysis even though it is known that the greatest number that cannot be expressed as the sum of two abundant numbers is less than this limit.
Find the sum of all the positive integers which cannot be written as the sum of two abundant numbers.

import Data.Numbers
import qualified Data.Set as Set 
main :: IO()
main = print $ sum $ Set.toList $ Set.difference (Set.fromList [1..28123]) (Set.fromList abundantSumList) 
abundantSumList :: [Integer]
abundantSumList = [ x + y | x <- abundantList, y <- abundantList, x + y <= 28123] 
abundantList :: [Integer]
abundantList = [ x | x <- [12..(28123 - 12)], isAbundant x] 
isPerfect :: Integer -> Bool
isPerfect 1 = False
isPerfect x = x == (factorSum x - x) 
isDeficient :: Integer -> Bool
isDeficient 1 = True
isDeficient x = x > (factorSum x - x) 
isAbundant :: Integer -> Bool
isAbundant 1 = False
isAbundant x = x < (factorSum x - x) 
*Main> main
4179871

2014년 4월 20일 일요일

Euler Project: Problem 22

Using names.txt (right click and 'Save Link/Target As...'), a 46K text file containing over five-thousand first names, begin by sorting it into alphabetical order. Then working out the alphabetical value for each name, multiply this value by its alphabetical position in the list to obtain a name score.
For example, when the list is sorted into alphabetical order, COLIN, which is worth 3 + 15 + 12 + 9 + 14 = 53, is the 938th name in the list. So, COLIN would obtain a score of 938 × 53 = 49714.
What is the total of all the name scores in the file?

import Data.List
import Data.List.Split
import Data.Char
import Data.Maybe 
main :: IO ()
main = do
    names <- readFile "names.txt"
    print $ sum $ map (\x -> getPosition names x * getScore x) $ sortedNamesList names 
getPosition :: String -> String -> Int
getPosition names x = fromMaybe 0 (elemIndex x $ sortedNamesList names) + 1 
getScore :: String -> Int
getScore xs = sum $ map (\x -> ord x - 64) xs 
sortedNamesList :: String -> [String]
sortedNamesList names = sort $ nameList names 
nameList :: String -> [String]
nameList = split (dropDelims . dropBlanks $ oneOf ",\"") 
*Main> main
871198282

Special thanks to Roel van Dijk.

cf) ghci에서 각 함수별로 let하여 실행하는 것, hs파일을 로드하여 실행하는 것 보다 더 빠르다. 원인은 잘 모르겠다.

Euler Project: Problem 21

Let d(n) be defined as the sum of proper divisors of n (numbers less than n which divide evenly into n).
If d(a) = b and d(b) = a, where a ≠ b, then a and b are an amicable pair and each of a and b are called amicable numbers.
For example, the proper divisors of 220 are 1, 2, 4, 5, 10, 11, 20, 22, 44, 55 and 110; therefore d(220) = 284. The proper divisors of 284 are 1, 2, 4, 71 and 142; so d(284) = 220.
Evaluate the sum of all the amicable numbers under 10000.

import Data.Numbers 
main :: IO()
main = print $ sum $ filter isAmicable [2..10000] 
isAmicable :: Integer -> Bool
isAmicable x = (x == d (d x)) && (x /= d x) 
d :: Integer -> Integer
d x = factorSum x - x 
*Main> main
31626

Euler Project: Problem 20

n! means n × (n − 1) × ... × 3 × 2 × 1
For example, 10! = 10 × 9 × ... × 3 × 2 × 1 = 3628800,
and the sum of the digits in the number 10! is 3 + 6 + 2 + 8 + 8 + 0 + 0 = 27.
Find the sum of the digits in the number 100!

Prelude Data.List.Split> let factorial = product $ enumFromTo 1
Prelude Data.List.Split> sum $ map (\x -> read x :: Int) $ chunksOf 1 $ show $ factorial 100
Loading package split-0.2.2 ... linking ... done.
648

Euler Project: Problem 19

You are given the following information, but you may prefer to do some research for yourself.
  • 1 Jan 1900 was a Monday.
  • Thirty days has September,
    April, June and November.
    All the rest have thirty-one,
    Saving February alone,
    Which has twenty-eight, rain or shine.
    And on leap years, twenty-nine.
  • A leap year occurs on any year evenly divisible by 4, but not on a century unless it is divisible by 400.
How many Sundays fell on the first of the month during the twentieth century (1 Jan 1901 to 31 Dec 2000)?

이놈의 짧디 짧은 영어... (T_T)

import Data.Time.Calendar
import Data.Time.Calendar.WeekDate
import Data.Maybe 
main :: IO()
main = print $ length $ filter (== 1) $ sundayDateList $ sundayList diffdaysList 
sundayDateList :: [Integer] -> [Int]
sundayDateList = map (`calDay` toGregorian) 
sundayList :: [Integer] -> [Integer]
sundayList = mapMaybe (\x -> if calDay x toWeekDate == 7 then Just x else Nothing) 
calDay :: Integer -> (Day -> (Integer, Int, Int)) -> Int
calDay x myFn = take3rd $ myFn $ addDays x $ fromGregorian 1901 1 1 
take3rd :: (Integer, Int, Int) -> Int
take3rd (_, _, a) = a 
diffdaysList :: [Integer]
diffdaysList = [0..(diffDays (fromGregorian 2000 12 31) (fromGregorian 1901 1 1))] 
*Main> main
171

2014년 4월 6일 일요일

제멋대로 Functor, Monad & Arrow 개념

Monad에 대한 공포가 조금 없어지게 되어, 멋대로 이해해 본 functor, monad, arrow에 대해서 메모해 둔다.

먼저 그 유명하신 monad 먼저.
흔히 monad란 문맥을 고려한 value의 적용이라고도 말한다.

쉽게 말하면,
가령 Int형 데이터에 이용할 수 있는 연산(또는 함수)가 있다고 하자.
프로그래밍을 하다 보면, 이 함수를 String에도 이용하고 싶을 때가 있다(고 하자).
이를 가능하게 해 주는 것이 monad라고 생각할 수 있다.

조금더 정확(?)하게 말해 보면... (어짜피 내 이해가 수학적이지는 못하지만...)
monad는 value를 포장해 주는 것이다.
위의 예로 설명해 보면,
String을 Int형으로 포장해서 연산(또는 함수)에 적용시키는 것이다.



monad는 권론에서 그 어떤 추상화된 개념이라고 말을 하는데...
조금 수학적(?)으로 말해 본다면,
우리가 방정식을 풀 때, 모르는 수를 x라고 '말하면서' 방정식을 세우고 풀어간다. 즉, 우리가 몰라서 그렇지, x는 구체적인 어떤 숫자다. 이것도 일종의 추상화라고 할 수 있다. 숫자를 x라고 추상화하였듯이, 숫자 x를 추상화한 것이 monad라고 생각할 수 있겠다. 즉, Int Type, Char Type, String Type등등을 추상화한 그런 개념인 듯 하다. 다르게 말하면 '뭔가가 들어있는 정체불명의 value'.

monad와 functor는 유사하지만 다음과 같은 개념상의 차이가 있다.

Functor는 기존 문맥의 의미를 보존한다. 위의 예를 가지고 설명하자면, functor는 String형을 Int형으로 포장했으면, 결과도 Int형으로 된다. 다른 type으로 전환하지 못한다.


Functor

함수를 거친 value가 Int Type의 상자에 들어있는 String임을 기억하자. 함수의 입력값의 Type과 변화가 없다.


반면 Monad는 다른 type으로 전환할 수 있다. String형을 Int형으로 포장하여 연산(또는 함수)에 적용한 다음, Double형으로 결과물을 출력할 수 있다.


Monad

함수의 출력값이 Functor 그림과 달리 "?"으로 바뀌어 있다. 함수의 출력값은 그 어떤 Type도 가질 수 있다는 것을 나타낸다. 이것이 monad와 functor와의 차이이다.


변수에 대해서 작용하는 것이 Monad, Functor라면,
연산(또는 함수)에 대해서 작용하는 것이 Arrow라고 생각하면 된다.
Monad 보다 좀더 추상화된 개념이라고 한다.
Arrow에 대해서는 Wiki Book
Haskell/Understanding arrows(링크)에 있는 그림만이라도 보시면 개략을 알 수 있다.


여담: monad는 어떻게 보면, C에서 말하는 cast같은 역할을 한다고도 할 수 있다. 간단한 유틸이나 입력값이 제한적인 게임 프로그래밍에서는 그닥 중요하지 않으나, 실무 프로그램에서는 입력값에서 묻어오는 예상하지 못한 문자나 숫자로 인해서 엉뚱한 결과를 가져오는 경우가 있다. 특히 문자열을 처리하는 프로그램에서는 escape 문자처리에 종종 골머리를 앓게 된다.
지극히 개인적인 사견이지만...
C컴파일러를 구입할 돈이 없던 시절, 어셈블러로 프로그래밍을 했었다. 가장 짜증나는 것은 타인이 만든 라이브러리를 사용할 때 예상치 못한 결과값이 나오는 경우였다. 버그의 원인을 찾아보면, 거의 항상, 내가 의도한 data type과 다른 data type으로 라이브러리의 함수가 변수를 처리를 함으로서 생기는 문제들이었다. 특히 escape sequence가 묻어 들어가는 버그는 항상 생기는 것도 아니어서 찾기도 어려웠다. C와 같은 고급언어들은 data type을 강조함으로서, 메모리에서 읽어들이는 bit를 data type에 따라서 "강제"함으로서, 이러한 버그들을 회피했다. OOP라는 기법을 구조화 프로그래밍을 다른 관점으로 본다면, 계승등의 기법을 통해 함수에 적용되는 data type을 강제하는 방법이라고도 할 수 있다.
Haskell과 같은 Type theory의 첨병을 실험하고 있는 언어들은, 원천적으로 이러한 버그들을 배제한다. (Haskell이 순수 함수형 언어의 전형처럼 이야기 하지만, 많은 논문들은 type theory와 language의 표현력의 한계를 실험하는데 집중되는 것 처럼 보인다... 적어도 내게는...)
개인적으로는... Haskell은 변수에 얽매이지 않는 자유로운 프로그래밍이 가능하다는 것이 너무 매력적이다. 그래서 변수를 추상화 해주는 monad가 중요해지는 이유라고 생각한다.


Euler Project: Problem 18

By starting at the top of the triangle below and moving to adjacent numbers on the row below, the maximum total from top to bottom is 23.
3
7 4
2 4 6
8 5 9 3
That is, 3 + 7 + 4 + 9 = 23.
Find the maximum total from top to bottom of the triangle below:
75
95 64
17 47 82
18 35 87 10
20 04 82 47 65
19 01 23 75 03 34
88 02 77 73 07 63 67
99 65 04 28 06 16 70 92
41 41 26 56 83 40 80 70 33
41 48 72 33 47 32 37 16 94 29
53 71 44 65 25 43 91 52 97 51 14
70 11 33 28 77 73 17 78 39 68 17 57
91 71 52 38 17 14 91 43 58 50 27 29 48
63 66 04 68 89 53 67 30 73 16 69 87 40 31
04 62 98 27 23 09 70 98 73 93 38 53 60 04 23
NOTE: As there are only 16384 routes, it is possible to solve this problem by trying every route. However, Problem 67, is the same challenge with a triangle containing one-hundred rows; it cannot be solved by brute force, and requires a clever method! ;o)

main :: IO()
main = print $ foldr1 maxEleList rowData 
maxEleList :: [Int] -> [Int] -> [Int]
maxEleList xs ys = maxOfTwo upperList lowerList
                    where   upperList = zipWith (curry turpleSum) xs ys
                                 lowerList = zipWith (curry turpleSum) xs (tail ys)
maxOfTwo :: [Int] -> [Int] -> [Int]
maxOfTwo [] [] = []
maxOfTwo (x:xs) (y:ys) = max x y : maxOfTwo xs ys 
turpleSum :: (Int, Int) -> Int
turpleSum (x, y) = x + y 
rowData :: [[Int]]
rowData = map (map (\x -> read x :: Int) . words) [r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, r13, r14, r15] 
r1 = "75"
r2 = "95 64"
r3 = "17 47 82"
r4 = "18 35 87 10"
r5 = "20 04 82 47 65"
r6 = "19 01 23 75 03 34"
r7 = "88 02 77 73 07 63 67"
r8 = "99 65 04 28 06 16 70 92"
r9 = "41 41 26 56 83 40 80 70 33"
r10 = "41 48 72 33 47 32 37 16 94 29"
r11 = "53 71 44 65 25 43 91 52 97 51 14"
r12 = "70 11 33 28 77 73 17 78 39 68 17 57"
r13 = "91 71 52 38 17 14 91 43 58 50 27 29 48"
r14 = "63 66 04 68 89 53 67 30 73 16 69 87 40 31"
r15 = "04 62 98 27 23 09 70 98 73 93 38 53 60 04 23" 
*Main> main
[1074]