SlideShare ist ein Scribd-Unternehmen logo
1 von 21
하스켈 성능 튜닝 2
목 차
1. 상황 요약
2. 성능 개선
3. 새로운 방법
4. 결론
예시로 들었던 문제
온라인 저지 사이트 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.
결국 풀었다
1년 걸림
이전에 썼던 알고리즘
• S(N)을 알면 abundant 검사는 단순하다
• semiperfect 검사는 0-1 knapsack 문제
• 약수들은 정수이므로 다이나믹 프로그래밍으로 해결 가능
• 𝑆 𝑁 = 𝑠1, 𝑠2, … , 𝑠 𝑘 (𝑠1 ≤ 𝑠2 ≤ … )
• 𝑇 𝑖, 𝑗 = 𝑆 𝑁 의 𝑠1, 𝑠2, … , 𝑠𝑖 까지 써서 만들 수 있는, j 이하의 최대 합
• 𝑇 𝑖, 𝑗 =
0 𝑖 = 0
𝑇 𝑖 − 1, 𝑗 𝑠𝑖 > 𝑗
max 𝑇 𝑖 − 1, 𝑗 , 𝑇 𝑖 − 1, 𝑗 − 𝑠𝑖 + 𝑠𝑖 𝑜𝑡ℎ𝑒𝑟𝑤𝑖𝑠𝑒
• 𝑇 𝑘, 𝑁 = 𝑁 이면 N은 semiperfect
불리언 문제로 변환
• S(N)을 알면 abundant 검사는 단순하다
• semiperfect 검사는 0-1 knapsack 문제
• 약수들은 정수이므로 다이나믹 프로그래밍으로 해결 가능
• 𝑆 𝑁 = 𝑠1, 𝑠2, … , 𝑠 𝑘 (𝑠1 ≤ 𝑠2 ≤ … )
• 𝑇 𝑖, 𝑗 = 𝑆 𝑁 의 𝑠1, 𝑠2, … , 𝑠𝑖 까지 써서 만들 수 있는 최대 합이 j인가?
• 𝑇 𝑖, 𝑗 =
𝑇𝑟𝑢𝑒 𝑖 = 1, 𝑗 = 1
𝐹𝑎𝑙𝑠𝑒 𝑖 = 1, 𝑗 ≠ 1
𝑇 𝑖 − 1, 𝑗 𝑠𝑖 > 𝑗
𝑇 𝑖 − 1, 𝑗 𝐴𝑁𝐷 𝑇 𝑖 − 1, 𝑗 − 𝑠𝑖 𝑜𝑡ℎ𝑒𝑟𝑤𝑖𝑠𝑒
• 𝑇 𝑘, 𝑁 = 𝑇𝑟𝑢𝑒 이면 N은 semiperfect
이전 0-1 KNAPSACK 코드
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
필요없는 배열이 바로 가비지 컬렉션되지만, 매 호출마다 새 배열을 생성한다
수정된 0-1 KNAPSACK 코드
knapsack :: Int -> [Int] -> Bool
knapsack n divs =
let ary = runSTUArray $ do
ary <- newArray (1,n) False
writeArray ary 1 True
forM_ divs i -> do
forM_ [n,n-1..i+1] $ j -> do
x <- readArray ary j
y <- readArray ary (j-i)
writeArray ary j (x || y)
writeArray ary i True
return ary
in ary ! n
• 약수들의 배열에서 1은 제외
(base case로 직접 처리)
• 단 하나의 배열 ary 사용
• C 코드처럼 보인다
• IO 모나드 안도 아닌데 ary가 변경 가능?
• knapsack은 순수 함수가 맞다
• Rank-2 type 덕에 가능
• 자세한 설명은 다음 기회에…
수정된 0-1 KNAPSACK 코드
knapsack :: Int -> [Int] -> Bool
knapsack n divs =
let ary = runSTUArray $ do
ary <- newArray (1,n) False
writeArray ary 1 True
forM_ divs i -> do
forM_ [n,n-1..i+1] $ j -> do
x <- readArray ary j
y <- readArray ary (j-i)
writeArray ary j (x || y)
writeArray ary i True
만약 (ary ! n = True) 이면 여기서
종료
return ary
in ary ! n
• forM, forM_은 무조건 모든 액션을 수행한다
• 일부 약수만으로 N을 완성할 수 있으면
더 계산할 필요가 없다
• C의 break 같은 구문은 없나?
• MonadPlus의 guard가 그런 역할
• 하지만 forM은 Monad에 대해 정의됨
• forM_ :: Monad m => [a] -> (a -> m b) -> m ()
• class (Monad m) => MonadPlus m
• guard 적용 불가
수정된 0-1 KNAPSACK 코드
for :: Monad m => [a] -> (b -> Bool) -> (a -> m b) -> m [b]
for [] _ _ = return []
for (x:xs) test f = f x >>= y ->
if test y
then for xs test f >>= ys -> return (y:ys)
else return []
knapsack :: Int -> [Int] -> Bool
knapsack n divs =
let ary = runSTUArray $ do
ary <- newArray (1,n) False
writeArray ary 1 True
for divs not i -> do
form_ [n,n-1..i+1] $ j -> do
x <- readArray ary j
y <- readArray ary (j-i)
writeArray ary j (x || y)
writeArray ary i True
fin <- readArray ary n
return False
return ary
in ary ! n
• 중간에 멈출 수 있는 for를 직접 구현
• 액션을 실행할 때마다 모나드 내부의 값을 추출한다
• 추출한 값에 test 함수 적용한 결과가 False면 정지
• 루프 조기 탈출을 구현
성능을 측정해보자
• 또다시 N = 500000에 대해 테스트
• 메모리 사용 18MB -> 1MB
• 실행 8.94초 -> 0.19초
이건 통과를 안 할 수가 없다
응 아냐
모든 수단을 활용했다
• 철저한 평가(Strict Evaluation)
• 초고속 계산을 위한 불리언 원시 타입
• 불필요한 값 생성 방지
• 탐색 공간 줄이기
계산 테이블 재고
N = 12 1 2 3 4 5 6 7 8 9 10 11 12
s1 = 1 1 1 1 1 1 1 1 1 1 1 1 1
s2 = 2 1 2 3 3 3 3 3 3 3 3 3 3
s3 = 3 1 2 3 4 5 6 6 6 6 6 6 6
s4 = 4 1 2 3 4 5 6 7 8 9 10 10 10
s5 = 6 1 2 3 4 5 6 7 8 9 10 11 12
• N = 12인 경우 총 계산: 6 * 12 = 72칸 / 실제 필요한 계산: 23칸
• N = 18인 경우 총 계산: 5 * 18 = 90칸 / 실제 필요한 계산: 25칸
알고리즘 대격변
knapsack2 n divs = f [n] divs where
f xs [] = any (== 0) xs
f xs (d:ds) =
let ys = filter (>= 0) (map (x -> x - d) xs)
in f (ys ++ xs) ds
• 약수 리스트에 다시 1 포함
• 약수는 큰 것부터 계산 (knapsack2를 호출할 때 약수 리스트를 뒤집어서 전달)
• 그런데 중복 원소가 있으면?
• 그냥 중복 계산 – 걸러내는 게 더 오래 걸린다
알고리즘 대격변
knapsack2 n divs = f [n] divs where
f xs [] = any (== 0) xs
f xs (d:ds) =
let ys = filter (>= 0) (map (x -> x - d) xs)
in if (any (== 0) ys) then True else f (ys ++ xs) ds
• 역시 조기 탈출 구현
알고리즘 대격변
knapsack2 n divs = f [n] divs (sum divs) where
f xs [] _ = any (== 0) xs
f xs (d:ds) lim =
let ys = filter (y -> 0 <= y && y <= lim) (map (x -> x - d) xs)
in if (any (== 0) ys) then True else f (ys ++ xs) ds (lim-d)
• 분기 한정(Branch and Bound)
• 𝑠1, 𝑠2, … , 𝑠 𝑘 의 부분합으로 M을 만들어야 하는데 sum 𝑠1, 𝑠2, … , 𝑠 𝑘 < M 이면 애초에 만들 수가 없다
SUPER FAST
main = do
forM_ [2..500000] $ i -> do
when (weird i) (print i)
• 2에서 50만까지 전수 검사해도 6.68초
• 메모리 사용량은 14MB
통과
그런데
• 철저한 평가(Strict Evaluation)
• 표현식이 너무 길어지기 전에 계산을 강제로 수행
• 원시 타입(Unboxed type)
• 정수 표현에 C와 같은 바이트 사용
• 테이블의 두 행만 메모리에 유지
• 필요없는 행은 바로 가비지 컬렉션이 되도록
다 안씀
결론
그냥 알고리즘을 잘 짜자

Weitere ähnliche Inhalte

Was ist angesagt?

알고리즘 스터디(정렬) Seungdols
알고리즘 스터디(정렬) Seungdols알고리즘 스터디(정렬) Seungdols
알고리즘 스터디(정렬) Seungdolsseungdols
 
하스켈 프로그래밍 입문 2
하스켈 프로그래밍 입문 2하스켈 프로그래밍 입문 2
하스켈 프로그래밍 입문 2Kwang Yul Seo
 
종이접기(fold) 프로그래밍
종이접기(fold) 프로그래밍종이접기(fold) 프로그래밍
종이접기(fold) 프로그래밍Kwang Yul Seo
 
함수형 프로그래밍? 그래서 어떻게
함수형 프로그래밍? 그래서 어떻게함수형 프로그래밍? 그래서 어떻게
함수형 프로그래밍? 그래서 어떻게Gyooha Kim
 
Tensorflow regression 텐서플로우 회귀
Tensorflow regression 텐서플로우 회귀Tensorflow regression 텐서플로우 회귀
Tensorflow regression 텐서플로우 회귀beom kyun choi
 
쏙 알고스터디 01
쏙 알고스터디 01쏙 알고스터디 01
쏙 알고스터디 01Jisu Lee
 
Sicp 2.2 계층 구조 데이터와 닫힘 성질
Sicp 2.2 계층 구조 데이터와 닫힘 성질Sicp 2.2 계층 구조 데이터와 닫힘 성질
Sicp 2.2 계층 구조 데이터와 닫힘 성질aceigy6322
 
세미나
세미나세미나
세미나Dongyi Kim
 
하스켈 모나드
하스켈 모나드하스켈 모나드
하스켈 모나드Kwang Yul Seo
 
2021 2학기 정기 세미나 4주차
2021 2학기 정기 세미나 4주차2021 2학기 정기 세미나 4주차
2021 2학기 정기 세미나 4주차Moonki Choi
 
Coursera Machine Learning으로 기계학습 배우기 : week2
Coursera Machine Learning으로 기계학습 배우기 : week2Coursera Machine Learning으로 기계학습 배우기 : week2
Coursera Machine Learning으로 기계학습 배우기 : week2Kwangsik Lee
 
해커에게 전해들은 머신러닝 #3
해커에게 전해들은 머신러닝 #3해커에게 전해들은 머신러닝 #3
해커에게 전해들은 머신러닝 #3Haesun Park
 
확통 회귀분석
확통 회귀분석확통 회귀분석
확통 회귀분석jaypi Ko
 
R 스터디 네번째
R 스터디 네번째R 스터디 네번째
R 스터디 네번째Jaeseok Park
 
차원축소 훑어보기 (PCA, SVD, NMF)
차원축소 훑어보기 (PCA, SVD, NMF)차원축소 훑어보기 (PCA, SVD, NMF)
차원축소 훑어보기 (PCA, SVD, NMF)beom kyun choi
 
R 프로그램의 이해와 활용 v1.1
R 프로그램의 이해와 활용 v1.1R 프로그램의 이해와 활용 v1.1
R 프로그램의 이해와 활용 v1.1happychallenge
 
[D2 CAMPUS] 숭실대 SCCC 프로그래밍 경시대회 문제 풀이
[D2 CAMPUS] 숭실대 SCCC 프로그래밍 경시대회 문제 풀이[D2 CAMPUS] 숭실대 SCCC 프로그래밍 경시대회 문제 풀이
[D2 CAMPUS] 숭실대 SCCC 프로그래밍 경시대회 문제 풀이NAVER D2
 
고등학생 R&E Python summary for test
고등학생 R&E Python summary for test고등학생 R&E Python summary for test
고등학생 R&E Python summary for testKyunghoon Kim
 
Python Programming: Function
Python Programming: FunctionPython Programming: Function
Python Programming: FunctionChan Shik Lim
 

Was ist angesagt? (20)

알고리즘 스터디(정렬) Seungdols
알고리즘 스터디(정렬) Seungdols알고리즘 스터디(정렬) Seungdols
알고리즘 스터디(정렬) Seungdols
 
하스켈 프로그래밍 입문 2
하스켈 프로그래밍 입문 2하스켈 프로그래밍 입문 2
하스켈 프로그래밍 입문 2
 
종이접기(fold) 프로그래밍
종이접기(fold) 프로그래밍종이접기(fold) 프로그래밍
종이접기(fold) 프로그래밍
 
함수형 프로그래밍? 그래서 어떻게
함수형 프로그래밍? 그래서 어떻게함수형 프로그래밍? 그래서 어떻게
함수형 프로그래밍? 그래서 어떻게
 
Tensorflow regression 텐서플로우 회귀
Tensorflow regression 텐서플로우 회귀Tensorflow regression 텐서플로우 회귀
Tensorflow regression 텐서플로우 회귀
 
쏙 알고스터디 01
쏙 알고스터디 01쏙 알고스터디 01
쏙 알고스터디 01
 
Sicp 2.2 계층 구조 데이터와 닫힘 성질
Sicp 2.2 계층 구조 데이터와 닫힘 성질Sicp 2.2 계층 구조 데이터와 닫힘 성질
Sicp 2.2 계층 구조 데이터와 닫힘 성질
 
세미나
세미나세미나
세미나
 
하스켈 모나드
하스켈 모나드하스켈 모나드
하스켈 모나드
 
2021 2학기 정기 세미나 4주차
2021 2학기 정기 세미나 4주차2021 2학기 정기 세미나 4주차
2021 2학기 정기 세미나 4주차
 
Coursera Machine Learning으로 기계학습 배우기 : week2
Coursera Machine Learning으로 기계학습 배우기 : week2Coursera Machine Learning으로 기계학습 배우기 : week2
Coursera Machine Learning으로 기계학습 배우기 : week2
 
해커에게 전해들은 머신러닝 #3
해커에게 전해들은 머신러닝 #3해커에게 전해들은 머신러닝 #3
해커에게 전해들은 머신러닝 #3
 
확통 회귀분석
확통 회귀분석확통 회귀분석
확통 회귀분석
 
R 스터디 네번째
R 스터디 네번째R 스터디 네번째
R 스터디 네번째
 
차원축소 훑어보기 (PCA, SVD, NMF)
차원축소 훑어보기 (PCA, SVD, NMF)차원축소 훑어보기 (PCA, SVD, NMF)
차원축소 훑어보기 (PCA, SVD, NMF)
 
Python 스터디
Python 스터디Python 스터디
Python 스터디
 
R 프로그램의 이해와 활용 v1.1
R 프로그램의 이해와 활용 v1.1R 프로그램의 이해와 활용 v1.1
R 프로그램의 이해와 활용 v1.1
 
[D2 CAMPUS] 숭실대 SCCC 프로그래밍 경시대회 문제 풀이
[D2 CAMPUS] 숭실대 SCCC 프로그래밍 경시대회 문제 풀이[D2 CAMPUS] 숭실대 SCCC 프로그래밍 경시대회 문제 풀이
[D2 CAMPUS] 숭실대 SCCC 프로그래밍 경시대회 문제 풀이
 
고등학생 R&E Python summary for test
고등학생 R&E Python summary for test고등학생 R&E Python summary for test
고등학생 R&E Python summary for test
 
Python Programming: Function
Python Programming: FunctionPython Programming: Function
Python Programming: Function
 

Ähnlich wie 하스켈 성능 튜닝 2

3.neural networks
3.neural networks3.neural networks
3.neural networksHaesun Park
 
03. linear regression
03. linear regression03. linear regression
03. linear regressionJeonghun Yoon
 
Deep Learning from scratch 5장 : backpropagation
 Deep Learning from scratch 5장 : backpropagation Deep Learning from scratch 5장 : backpropagation
Deep Learning from scratch 5장 : backpropagationJinSooKim80
 
Neural network (perceptron)
Neural network (perceptron)Neural network (perceptron)
Neural network (perceptron)Jeonghun Yoon
 
Code로 이해하는 RNN
Code로 이해하는 RNNCode로 이해하는 RNN
Code로 이해하는 RNNSANG WON PARK
 
파이썬 기본 문법
파이썬 기본 문법파이썬 기본 문법
파이썬 기본 문법SeongHyun Ahn
 
자료구조 프로젝트
자료구조 프로젝트자료구조 프로젝트
자료구조 프로젝트hyungoh kim
 
2.linear regression and logistic regression
2.linear regression and logistic regression2.linear regression and logistic regression
2.linear regression and logistic regressionHaesun Park
 
정렬 알고리즘의 성능 분석
정렬 알고리즘의 성능 분석정렬 알고리즘의 성능 분석
정렬 알고리즘의 성능 분석Young-jun Jeong
 
파이썬 스터디 2주차
파이썬 스터디 2주차파이썬 스터디 2주차
파이썬 스터디 2주차Han Sung Kim
 
RPG Maker와 Ruby로 코딩 시작하기 Day 3
RPG Maker와 Ruby로 코딩 시작하기 Day 3RPG Maker와 Ruby로 코딩 시작하기 Day 3
RPG Maker와 Ruby로 코딩 시작하기 Day 3Sunwoo Park
 
RLCode와 A3C 쉽고 깊게 이해하기
RLCode와 A3C 쉽고 깊게 이해하기RLCode와 A3C 쉽고 깊게 이해하기
RLCode와 A3C 쉽고 깊게 이해하기Woong won Lee
 
Deep Learning from scratch 3장 : neural network
Deep Learning from scratch 3장 : neural networkDeep Learning from scratch 3장 : neural network
Deep Learning from scratch 3장 : neural networkJinSooKim80
 
Adversarial Attack in Neural Machine Translation
Adversarial Attack in Neural Machine TranslationAdversarial Attack in Neural Machine Translation
Adversarial Attack in Neural Machine TranslationHyunKyu Jeon
 
[홍대 머신러닝 스터디 - 핸즈온 머신러닝] 4장. 모델 훈련
[홍대 머신러닝 스터디 - 핸즈온 머신러닝] 4장. 모델 훈련[홍대 머신러닝 스터디 - 핸즈온 머신러닝] 4장. 모델 훈련
[홍대 머신러닝 스터디 - 핸즈온 머신러닝] 4장. 모델 훈련Haesun Park
 
04. logistic regression ( 로지스틱 회귀 )
04. logistic regression ( 로지스틱 회귀 )04. logistic regression ( 로지스틱 회귀 )
04. logistic regression ( 로지스틱 회귀 )Jeonghun Yoon
 

Ähnlich wie 하스켈 성능 튜닝 2 (20)

3.neural networks
3.neural networks3.neural networks
3.neural networks
 
03. linear regression
03. linear regression03. linear regression
03. linear regression
 
Variational AutoEncoder(VAE)
Variational AutoEncoder(VAE)Variational AutoEncoder(VAE)
Variational AutoEncoder(VAE)
 
Deep Learning from scratch 5장 : backpropagation
 Deep Learning from scratch 5장 : backpropagation Deep Learning from scratch 5장 : backpropagation
Deep Learning from scratch 5장 : backpropagation
 
Neural network (perceptron)
Neural network (perceptron)Neural network (perceptron)
Neural network (perceptron)
 
알고리즘2
알고리즘2알고리즘2
알고리즘2
 
Code로 이해하는 RNN
Code로 이해하는 RNNCode로 이해하는 RNN
Code로 이해하는 RNN
 
Rdatamining
Rdatamining Rdatamining
Rdatamining
 
파이썬 기본 문법
파이썬 기본 문법파이썬 기본 문법
파이썬 기본 문법
 
자료구조 프로젝트
자료구조 프로젝트자료구조 프로젝트
자료구조 프로젝트
 
2.linear regression and logistic regression
2.linear regression and logistic regression2.linear regression and logistic regression
2.linear regression and logistic regression
 
정렬 알고리즘의 성능 분석
정렬 알고리즘의 성능 분석정렬 알고리즘의 성능 분석
정렬 알고리즘의 성능 분석
 
파이썬 스터디 2주차
파이썬 스터디 2주차파이썬 스터디 2주차
파이썬 스터디 2주차
 
RPG Maker와 Ruby로 코딩 시작하기 Day 3
RPG Maker와 Ruby로 코딩 시작하기 Day 3RPG Maker와 Ruby로 코딩 시작하기 Day 3
RPG Maker와 Ruby로 코딩 시작하기 Day 3
 
RLCode와 A3C 쉽고 깊게 이해하기
RLCode와 A3C 쉽고 깊게 이해하기RLCode와 A3C 쉽고 깊게 이해하기
RLCode와 A3C 쉽고 깊게 이해하기
 
Deep Learning from scratch 3장 : neural network
Deep Learning from scratch 3장 : neural networkDeep Learning from scratch 3장 : neural network
Deep Learning from scratch 3장 : neural network
 
Adversarial Attack in Neural Machine Translation
Adversarial Attack in Neural Machine TranslationAdversarial Attack in Neural Machine Translation
Adversarial Attack in Neural Machine Translation
 
[홍대 머신러닝 스터디 - 핸즈온 머신러닝] 4장. 모델 훈련
[홍대 머신러닝 스터디 - 핸즈온 머신러닝] 4장. 모델 훈련[홍대 머신러닝 스터디 - 핸즈온 머신러닝] 4장. 모델 훈련
[홍대 머신러닝 스터디 - 핸즈온 머신러닝] 4장. 모델 훈련
 
Digit recognizer
Digit recognizerDigit recognizer
Digit recognizer
 
04. logistic regression ( 로지스틱 회귀 )
04. logistic regression ( 로지스틱 회귀 )04. logistic regression ( 로지스틱 회귀 )
04. logistic regression ( 로지스틱 회귀 )
 

하스켈 성능 튜닝 2

  • 2. 목 차 1. 상황 요약 2. 성능 개선 3. 새로운 방법 4. 결론
  • 3. 예시로 들었던 문제 온라인 저지 사이트 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.
  • 5. 이전에 썼던 알고리즘 • S(N)을 알면 abundant 검사는 단순하다 • semiperfect 검사는 0-1 knapsack 문제 • 약수들은 정수이므로 다이나믹 프로그래밍으로 해결 가능 • 𝑆 𝑁 = 𝑠1, 𝑠2, … , 𝑠 𝑘 (𝑠1 ≤ 𝑠2 ≤ … ) • 𝑇 𝑖, 𝑗 = 𝑆 𝑁 의 𝑠1, 𝑠2, … , 𝑠𝑖 까지 써서 만들 수 있는, j 이하의 최대 합 • 𝑇 𝑖, 𝑗 = 0 𝑖 = 0 𝑇 𝑖 − 1, 𝑗 𝑠𝑖 > 𝑗 max 𝑇 𝑖 − 1, 𝑗 , 𝑇 𝑖 − 1, 𝑗 − 𝑠𝑖 + 𝑠𝑖 𝑜𝑡ℎ𝑒𝑟𝑤𝑖𝑠𝑒 • 𝑇 𝑘, 𝑁 = 𝑁 이면 N은 semiperfect
  • 6. 불리언 문제로 변환 • S(N)을 알면 abundant 검사는 단순하다 • semiperfect 검사는 0-1 knapsack 문제 • 약수들은 정수이므로 다이나믹 프로그래밍으로 해결 가능 • 𝑆 𝑁 = 𝑠1, 𝑠2, … , 𝑠 𝑘 (𝑠1 ≤ 𝑠2 ≤ … ) • 𝑇 𝑖, 𝑗 = 𝑆 𝑁 의 𝑠1, 𝑠2, … , 𝑠𝑖 까지 써서 만들 수 있는 최대 합이 j인가? • 𝑇 𝑖, 𝑗 = 𝑇𝑟𝑢𝑒 𝑖 = 1, 𝑗 = 1 𝐹𝑎𝑙𝑠𝑒 𝑖 = 1, 𝑗 ≠ 1 𝑇 𝑖 − 1, 𝑗 𝑠𝑖 > 𝑗 𝑇 𝑖 − 1, 𝑗 𝐴𝑁𝐷 𝑇 𝑖 − 1, 𝑗 − 𝑠𝑖 𝑜𝑡ℎ𝑒𝑟𝑤𝑖𝑠𝑒 • 𝑇 𝑘, 𝑁 = 𝑇𝑟𝑢𝑒 이면 N은 semiperfect
  • 7. 이전 0-1 KNAPSACK 코드 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 필요없는 배열이 바로 가비지 컬렉션되지만, 매 호출마다 새 배열을 생성한다
  • 8. 수정된 0-1 KNAPSACK 코드 knapsack :: Int -> [Int] -> Bool knapsack n divs = let ary = runSTUArray $ do ary <- newArray (1,n) False writeArray ary 1 True forM_ divs i -> do forM_ [n,n-1..i+1] $ j -> do x <- readArray ary j y <- readArray ary (j-i) writeArray ary j (x || y) writeArray ary i True return ary in ary ! n • 약수들의 배열에서 1은 제외 (base case로 직접 처리) • 단 하나의 배열 ary 사용 • C 코드처럼 보인다 • IO 모나드 안도 아닌데 ary가 변경 가능? • knapsack은 순수 함수가 맞다 • Rank-2 type 덕에 가능 • 자세한 설명은 다음 기회에…
  • 9. 수정된 0-1 KNAPSACK 코드 knapsack :: Int -> [Int] -> Bool knapsack n divs = let ary = runSTUArray $ do ary <- newArray (1,n) False writeArray ary 1 True forM_ divs i -> do forM_ [n,n-1..i+1] $ j -> do x <- readArray ary j y <- readArray ary (j-i) writeArray ary j (x || y) writeArray ary i True 만약 (ary ! n = True) 이면 여기서 종료 return ary in ary ! n • forM, forM_은 무조건 모든 액션을 수행한다 • 일부 약수만으로 N을 완성할 수 있으면 더 계산할 필요가 없다 • C의 break 같은 구문은 없나? • MonadPlus의 guard가 그런 역할 • 하지만 forM은 Monad에 대해 정의됨 • forM_ :: Monad m => [a] -> (a -> m b) -> m () • class (Monad m) => MonadPlus m • guard 적용 불가
  • 10. 수정된 0-1 KNAPSACK 코드 for :: Monad m => [a] -> (b -> Bool) -> (a -> m b) -> m [b] for [] _ _ = return [] for (x:xs) test f = f x >>= y -> if test y then for xs test f >>= ys -> return (y:ys) else return [] knapsack :: Int -> [Int] -> Bool knapsack n divs = let ary = runSTUArray $ do ary <- newArray (1,n) False writeArray ary 1 True for divs not i -> do form_ [n,n-1..i+1] $ j -> do x <- readArray ary j y <- readArray ary (j-i) writeArray ary j (x || y) writeArray ary i True fin <- readArray ary n return False return ary in ary ! n • 중간에 멈출 수 있는 for를 직접 구현 • 액션을 실행할 때마다 모나드 내부의 값을 추출한다 • 추출한 값에 test 함수 적용한 결과가 False면 정지 • 루프 조기 탈출을 구현
  • 11. 성능을 측정해보자 • 또다시 N = 500000에 대해 테스트 • 메모리 사용 18MB -> 1MB • 실행 8.94초 -> 0.19초
  • 12. 이건 통과를 안 할 수가 없다 응 아냐
  • 13. 모든 수단을 활용했다 • 철저한 평가(Strict Evaluation) • 초고속 계산을 위한 불리언 원시 타입 • 불필요한 값 생성 방지 • 탐색 공간 줄이기
  • 14. 계산 테이블 재고 N = 12 1 2 3 4 5 6 7 8 9 10 11 12 s1 = 1 1 1 1 1 1 1 1 1 1 1 1 1 s2 = 2 1 2 3 3 3 3 3 3 3 3 3 3 s3 = 3 1 2 3 4 5 6 6 6 6 6 6 6 s4 = 4 1 2 3 4 5 6 7 8 9 10 10 10 s5 = 6 1 2 3 4 5 6 7 8 9 10 11 12 • N = 12인 경우 총 계산: 6 * 12 = 72칸 / 실제 필요한 계산: 23칸 • N = 18인 경우 총 계산: 5 * 18 = 90칸 / 실제 필요한 계산: 25칸
  • 15. 알고리즘 대격변 knapsack2 n divs = f [n] divs where f xs [] = any (== 0) xs f xs (d:ds) = let ys = filter (>= 0) (map (x -> x - d) xs) in f (ys ++ xs) ds • 약수 리스트에 다시 1 포함 • 약수는 큰 것부터 계산 (knapsack2를 호출할 때 약수 리스트를 뒤집어서 전달) • 그런데 중복 원소가 있으면? • 그냥 중복 계산 – 걸러내는 게 더 오래 걸린다
  • 16. 알고리즘 대격변 knapsack2 n divs = f [n] divs where f xs [] = any (== 0) xs f xs (d:ds) = let ys = filter (>= 0) (map (x -> x - d) xs) in if (any (== 0) ys) then True else f (ys ++ xs) ds • 역시 조기 탈출 구현
  • 17. 알고리즘 대격변 knapsack2 n divs = f [n] divs (sum divs) where f xs [] _ = any (== 0) xs f xs (d:ds) lim = let ys = filter (y -> 0 <= y && y <= lim) (map (x -> x - d) xs) in if (any (== 0) ys) then True else f (ys ++ xs) ds (lim-d) • 분기 한정(Branch and Bound) • 𝑠1, 𝑠2, … , 𝑠 𝑘 의 부분합으로 M을 만들어야 하는데 sum 𝑠1, 𝑠2, … , 𝑠 𝑘 < M 이면 애초에 만들 수가 없다
  • 18. SUPER FAST main = do forM_ [2..500000] $ i -> do when (weird i) (print i) • 2에서 50만까지 전수 검사해도 6.68초 • 메모리 사용량은 14MB
  • 20. 그런데 • 철저한 평가(Strict Evaluation) • 표현식이 너무 길어지기 전에 계산을 강제로 수행 • 원시 타입(Unboxed type) • 정수 표현에 C와 같은 바이트 사용 • 테이블의 두 행만 메모리에 유지 • 필요없는 행은 바로 가비지 컬렉션이 되도록 다 안씀