"R을 이용한 데이터 처리 & 분석 실무 - 서민구 지음" 정리 자료 #4
- https://thebook.io/006723/
- 첫번째 : goo.gl/FJjOlq
- 두번째 : goo.gl/Wdb90g
- 세번째 : goo.gl/80VGcn
- 네번째 : goo.gl/lblUsR
산포된 데이터를 정합하는 직선을 찾는 것으로 이해해도 무방합니다. 주어진 데이터를 이용해 기울기와 절편을 구하는 방법입니다.
먼저 행렬연산을 통해 구하는 방법을 설명합니다. 그리고 퍼셉트론에 사용된 것과 같은 방법을 다시 설명하므로 선택적으로 듣기 바랍니다.
설명된 예제에 대한 C 언어 코드를 제공합니다. Tensorlow 코드는 정합 과정을 시각화하여 보여줍니다.
"R을 이용한 데이터 처리 & 분석 실무 - 서민구 지음" 정리 자료 #4
- https://thebook.io/006723/
- 첫번째 : goo.gl/FJjOlq
- 두번째 : goo.gl/Wdb90g
- 세번째 : goo.gl/80VGcn
- 네번째 : goo.gl/lblUsR
산포된 데이터를 정합하는 직선을 찾는 것으로 이해해도 무방합니다. 주어진 데이터를 이용해 기울기와 절편을 구하는 방법입니다.
먼저 행렬연산을 통해 구하는 방법을 설명합니다. 그리고 퍼셉트론에 사용된 것과 같은 방법을 다시 설명하므로 선택적으로 듣기 바랍니다.
설명된 예제에 대한 C 언어 코드를 제공합니다. Tensorlow 코드는 정합 과정을 시각화하여 보여줍니다.
3. 하스켈이란 무엇인가
• General-purpose purely functional programming language
• Non-strict semantics (lazy evaluation)
• Strong static type system based on Hindley-Milner type inference
4. 어느 언어나 그럴 듯한 홍보 문구를 가지고 있다
• 순수 함수만 있어서 코드를 이해하기 쉽고
• 각 함수의 정확성을 분석, 검사하기 쉽고
• 고급 타입 시스템이 실수를 줄여준다
타입
pythagoras_cps :: Int -> Int -> ((Int -> r) -> r)
코드
pythagoras_cps x y = k ->
square_cps x $ x_squared ->
square_cps y $ y_squared ->
add_cps x_squared y_squared $ k
분석
pythagoras_cps 3 4 print
= square_cps x $ x2 -> square_cps y $ y2 -> add_cps x2 y2 print
= square_cps x (x2 -> square_cps y (y2 -> add_cps x2 y2 print))
= square_cps x (x2 -> square_cps y (y2 -> print (add x2 y2))
= (x2 -> square_cps y (y2 -> print (add x2 y2)) (square x)
= ((x2 -> (y2 -> print (add x2 y2)) (square y)) (square x)
= (y2 -> print (add (square x) y2)) (square y)
= print (add (square x) (square y))
우 와! 정말 쉽다~ 대단해!
5. 수학 기호와 닮았다
𝑓: 𝑎, 𝑏 → 𝑅3
𝑓 𝑎, 𝑏 = (𝑎 𝑐𝑜𝑠𝑏, 𝑎 𝑠𝑖𝑛𝑏, 𝑎)
𝑔 𝑥 =
𝑠𝑖𝑛𝑥
𝑥
, 𝑥 < 0
1, 𝑥 = 0
𝑒 𝑥
+ 1, 𝑥 > 0
type Curve = (Double, Doule, Double)
f :: (Double, Double) -> Curve
f a b = (a * cos b, a * sin b, a)
g x
| x < 0 = sin x / x
| x == 0 = 1
| x > 0 = exp x + 1
6. 수학 기호와 닮았다
𝑆 = 𝑓(𝑥) 1 ≤ 𝑥 ≤ 10, 𝑥 𝑖𝑠 𝑖𝑛𝑡𝑒𝑔𝑒𝑟 }
where 𝑓 𝑥 = 𝑥3
− 𝑥
𝑥 𝑟, 𝜃 = 𝑟 𝑐𝑜𝑠𝜃, 𝑟 𝑠𝑖𝑛𝜃, 𝑟
Let 𝑟 𝑡 = 𝑒 𝑡/2, 𝜃 𝑡 =
𝑡
2
𝛼 𝑡 = 𝑥 𝑟 𝑡 , 𝜃 𝑡
s = [ f x | x <- [1..10] ]
where f x = x*x*x - x
x r theta = (r * cos theta, r * sin theta, r)
a t = let r = exp (t/2)
theta = t / (sqrt 2)
in x r theta
7. 수학 = = 하스켈
A real vector space is a set V, whose elements are called vectors, together with two binary operations + ∶
𝑉 × 𝑉 → 𝑉 and • ∶ 𝑅 × 𝑉 → 𝑉 , called addition and scalar multiplication, which satisfy the following eight
axioms for all 𝒖, 𝒗, 𝒘 ∈ 𝑉 and 𝑟, 𝑠 ∈ 𝑹
• 𝒖 + 𝒗 = 𝒗 + 𝒖
• 𝒖 + 𝒗 + 𝒘 = 𝒖 + 𝒗 + 𝒘
• there is an element 𝟎 in 𝑉 s.t. 𝟎 + 𝒖 = 𝒖
• 𝑟𝑠 ⋅ 𝒖 = 𝑟 ⋅ 𝑠 ⋅ 𝒖
• 𝑟 + 𝑠 ⋅ 𝒖 = 𝑟 ⋅ 𝒖 + 𝑠 ⋅ 𝒖
• r ⋅ 𝒖 + 𝒗 = 𝑟 ⋅ 𝒖 + 𝑟 ⋅ 𝒗
• 0 ⋅ 𝒖 = 𝟎
• 1 ⋅ 𝒖 = 𝟏
8. 수학 = = 하스켈
class Monoid a where
mempty :: a
mappend :: a -> a -> a
mconcat :: [a] -> a
mconcat = foldr mappend mempty
class (Functor t, Foldable t) => Traversable t where
traverse :: Applicative f => (a -> f b) -> t a -> f (t b)
sequenceA :: Applicative f => t (f a) -> f (t a)
mapM :: Monad m => (a -> m b) -> t a -> m (t b)
sequence :: Monad m => t (m a) -> m (t a)
chainCPS :: ((a -> r) -> r) -> (a -> ((b -> r) -> r)) -> ((b -> r) -> r)
chainCPS s f = k -> s $ x -> f x $ k
foldMap (g . f) = g . foldMap f
foldMap f = fold . fmap f
foldMap g . fmap f = foldMap (g . f) = g . foldMap f
instance Arrow Circuit where
arr f = Circuit $ a -> (arr f, f a)
first (Circuit cir) = Circuit $ (b, d) ->
let (cir', c) = cir b
in (first cir', (c, d))
(>=>) :: Monad m => (a -> m b) -> (b -> m c) -> a -> m c
f >=> g = x -> f x >>= g
9. 하스켈에서 가능한 것들
• 소수 구하기
primes = sieve [2..] where sieve (p:xs) = p : sieve [ n | n <- xs, n `mod` p > 0 ]
• 문자열을 공백으로 분리 “Hello world” => [“Hello”, “world”]
unfoldr (b -> fmap (const . (second $ drop 1) . break (==' ') $ b) . listToMaybe $ b)0 ]
• N-queen 알고리즘
queens n = foldM f [] [1..n] where
f qs _ = [q:qs | q <- [1..n] qs, q `notDiag` qs]
q `notDiag` qs = and [abs (q - qi) /= i | (qi,i) <- qs `zip` [1..]]
• 입력을 알파벳 순으로 정렬
main = (mapM_ putStrLn . Data.List.sort . lines) =<< getContents
10. 사건의 전말
온라인 저지 사이트 ALGOSPOT
문제 ID: WEIRD (https://www.algospot.com/judge/problem/read/WEIRD)
In mathematics, weird numbers are natural numbers that are abundant but not semiperfect. In other words, a
natural number N is a weird number if and only if:
• Sum of its proper divisors (i.e. less than N ) is greater than the number.
• No subset of its divisors sum to N.
For example, the set of proper divisors of 12 is { 1, 2, 3, 4, 6 } . The sum of these numbers exceed 12, however,
12 is not a weird number since 1 + 2 + 3 + 6 = 12.
However, 70 is a weird number since its proper divisors are {1, 2, 5, 7, 10, 14, 35} and no subset sums to 70 .
Write a program to determine if the given numbers are weird or not.
11. 문제 분석
S(N) = N의 약수들의 집합 (N은 제외)
• S의 원소들의 합이 N보다 크다 (abundant)
• S의 부분집합 중 그 합이 N이 되는 집합이 있다 (semiperfect)
• N이 abundant하면서 semiperfect하지 않으면 weired number
• 예시
• S(12) = {1, 2, 3, 4, 6}
• sum(S) = 16 > 12 -> 12는 abundant
• sum({1,2,3,6}) = 12 = N -> 12는 semiperfect
• 따라서 12는 weired number가 아니다
12. 간단한 0-1 KNAPSACK 문제
• S(N)을 알면 abundant 검사는 단순하다
• semiperfect 검사는 0-1 knapsack 문제
• 약수들은 정수이므로 다이나믹 프로그래밍으로 해결 가능
• 𝑆 𝑁 = 𝑠1, 𝑠2, … , 𝑠 𝑘 (𝑠1 ≤ 𝑠2 ≤ … )
• 𝑇 𝑖, 𝑗 = 𝑆 𝑁 의 𝑠1, 𝑠2, … , 𝑠𝑖 까지 써서 만들 수 있는, j 이하의 최대 합
• 𝑇 𝑖, 𝑗 =
0 𝑖 = 0
𝑇 𝑖 − 1, 𝑗 𝑠𝑖 > 𝑗
max 𝑇 𝑖 − 1, 𝑗 , 𝑇 𝑖 − 1, 𝑗 − 𝑠𝑖 + 𝑠𝑖 𝑜𝑡ℎ𝑒𝑟𝑤𝑖𝑠𝑒
• 𝑇 𝑘, 𝑁 = 𝑁 이면 N은 semiperfect
19. 이렇게 계산하는 이유?
• 점화식을 그대로 계산하면 같은 원소를 중복 계산
• 계산 시간이 지수함수적으로 증가 (가장 단순한 예: 피보나치 수의 재귀적 계산)
• 큰 문제가 작은 문제를 포함
• 작은 문제를 풀어두면 큰 문제를 풀 수 있다
• 테이블을 계산해두면 비슷한 문제들을 풀 때 재활용 가능
• 메모리를 희생하고 속도를 얻는다
20. 하스켈식 해법
1 divisors n = [ x | x <- [1..(n-1)], n `mod` x == 0]
2
3 knapsack n divs = table where
4 table :: Int -> Int -> Int
5 table 0 j = 0
6 table i j
7 | si > j = table (i-1) j
8 | otherwise = maximum [ table (i-1) j, table (i-1) (j-si) + si ]
9 where si = divs !! i
10
11 isWeird n = abundant && not semiperfect where
12 divs = divisors n
13 abundant = sum divs > n
14 semiperfect = (knapsack n divs) (length divs - 1) n == n
15
16 main = do
17 putStrLn $ "is 12 weird?: " ++ show (isWeird 12)
18 putStrLn $ "12 70 weird?: " ++ show (isWeird 70)
21. 하스켈식 해법
1 divisors n = [ x | x <- [1..(n-1)], n `mod` x == 0]
2
3 knapsack n divs = table where
4 table :: Int -> Int -> Int
5 table 0 j = 0
6 table i j
7 | si > j = table (i-1) j
8 | otherwise = maximum [ table (i-1) j, table (i-1) (j-si) + si ]
9 where si = divs !! i
10
11 isWeird n = abundant && not semiperfect where
12 divs = divisors n
13 abundant = sum divs > n
14 semiperfect = (knapsack n divs) (length divs - 1) n == n
15
16 main = do
17 putStrLn $ "is 12 weird?: " ++ show (isWeird 12)
18 putStrLn $ "12 70 weird?: " ++ show (isWeird 70)
점화식 중복 계산
22. 진짜 하스켈식 해법
• 테이블 자체를 재귀적으로 정의하면 안된다
• 테이블의 원소를 재귀적으로 정의하면 된다
• ???
knapsack n divs len = table where
table = array ((0,0),(len,n))
$! [((0,w),0) | w <- [0..n]]
++ [((i,w),v) | i<-[1..len], w<-[0..n],
let v = maximum [at(i-1,w), divs!i + at(i-1,w-(divs!i))]]
at (i, w) = if w < 0 then -n else table ! (i,w)
23. 메모이제이션(MEMOIZATION)
• 테이블 계산하는 게 메모이제이션
• 하스켈에서는 테이블의 원소를 재귀적으로 정의해도 메모이제이션이 가능하다
• 지연 평가(Lazy Evaluation)
• 데이터 불변성(Data Immutability)
• 고정점 연산자(Fixed Point Operator)
29. 해결 방법
• 철저한 평가(Strict Evaluation)
• 표현식이 너무 길어지기 전에 계산을 강제로 수행
• 원시 타입(Unboxed type)
• 정수 표현에 C와 같은 바이트 사용
• 테이블의 두 행만 메모리에 유지
• 필요없는 행은 바로 가비지 컬렉션이 되도록
30. 해결 방법
knapsack :: Int -> UArray Int Int -> Int -> UArray Int Int
knapsack n divs len = a 0 (runSTUArray $ newArray (0,n) (0::Int)) where
a prevRow prevA
| prevRow == len = prevA
| True = currA `seq` a (prevRow+1) currA where
currA = runSTUArray $ newListArray (0,n) [w `seq` (v `seq` v) | w <- [0..n],
let ith = divs ! prevRow, let v = maximum [at w, ith + at (w-ith)]]
at w = if w < 0 then -n else prevA ! w
31. 해결 방법
knapsack :: Int -> UArray Int Int -> Int -> UArray Int Int
knapsack n divs len = a 0 (runSTUArray $ newArray (0,n) (0::Int)) where
a prevRow prevA
| prevRow == len = prevA
| True = currA `seq` a (prevRow+1) currA where
currA = runSTUArray $ newListArray (0,n) [w `seq` (v `seq` v) | w <- [0..n],
let ith = divs ! prevRow, let v = maximum [at w, ith + at (w-ith)]]
at w = if w < 0 then -n else prevA ! w
철저한 평가
32. 해결 방법
knapsack :: Int -> UArray Int Int -> Int -> UArray Int Int
knapsack n divs len = a 0 (runSTUArray $ newArray (0,n) (0::Int)) where
a prevRow prevA
| prevRow == len = prevA
| True = currA `seq` a (prevRow+1) currA where
currA = runSTUArray $ newListArray (0,n) [w `seq` (v `seq` v) | w <- [0..n],
let ith = divs ! prevRow, let v = maximum [at w, ith + at (w-ith)]]
at w = if w < 0 then -n else prevA ! w
Unboxed 타입
33. 해결 방법
knapsack :: Int -> UArray Int Int -> Int -> UArray Int Int
knapsack n divs len = a 0 (runSTUArray $ newArray (0,n) (0::Int)) where
a prevRow prevA
| prevRow == len = prevA
| True = currA `seq` a (prevRow+1) currA where
currA = runSTUArray $ newListArray (0,n) [w `seq` (v `seq` v) | w <- [0..n],
let ith = divs ! prevRow, let v = maximum [at w, ith + at (w-ith)]]
at w = if w < 0 then -n else prevA ! w
일부 행만 유지
34. 성능을 측정해보자
• 다시 N = 500000에 대해 테스트
• 메모리 사용 1/1000
• 속도 2.5배