Loading [MathJax]/jax/output/CommonHTML/jax.js

해시함수의 기본적인 내용에 대해서는 이전 글(http://sevity.tistory.com/21) 참조


자세한 설명에 들어가기전에 다음 예제를 살펴보자.

철수는 영희에게 중요한 문서를 전달할 예정이다.

그런데 누군가 중간에서 그 문서의 내용을 해킹으로 바꿀 수 있기 때문에, 문서내용에 대해서

(string to int) hash function을 적용해서 오프라인으로 해시값을 알려주기로 했다. 

이 해시값을 42라고 해보자.

영희는 문서를 전달받은다음, 철수와 동일한 hash function을 적용해서 42라는 값을 얻었고, 문서가 해킹당하지 않았음을 확신했다.


위와 같은 시나리오로 보안에서 해시가 사용되는 거시다.


그런데 만약 해시값인 42로 부터 원본 문서의 내용을 일부라도 유추할 수 있다면 어떻게 될까..

예를 들어 해시값이 4로 시작하면 원본 문서의 내용에 money라는 단어가 들어간다던가..

이런일이 있으면 안되기 때문에 해시값으로 부터 원본의 내용을 찾는게 계산상 어렵도록 hash function을 설계해야 한다.

이러한 공격을 제1역상 공격이라 하며, 이에 저항하기 위해 hash function이 가져야 하는 성질을 제1역상저항성(preimage resistance)이라 한다.


그런데, 또 다른 예를 살펴보자.

해커가 제1역상 공격을 통해 원본 내용을 찾는것은 불가능하더라도, 

여러가지 문서들을 hash function에 넣어보면서 42라는 같은 결과가 나오는 전혀다른 문서를 찾는다면 어떻게 될까?

이런게 가능하다면 영희는 해시값이 42로 동일하기 때문에 문서내용이 바뀌어도 모를 수 있을 것이다.

(단 이런 공격을 시도 하려면 hash function이 공개되어 철수/영희가 사용하는 것과 해커가 사용하는게 같아야 한다는 전재조건이 붙는다)

위와 같은 공격을 제2역상공격이라 하며, 이를 막기위해 입력의 해시 값을 바꾸지 않으면서 입력을 변경하는 것이 계산상 어렵도록 hash 함수를 설계해야 하며 이를 제2역상저항성(second preimage resistance)이라 한다.


위의 제2역상 공격은 사실 해시 함수의 충돌(collision)이라는 약점을 파고드는 것인데,

해시 함수는 더 큰 인풋(문서내용)을 더 작은 아웃풋(숫자하나)로 바꾸는 과정이기 때문에 충돌이 아예 없을수는 없다.

하지만 같은 해시 값을 생성하는 두 개의 입력값을 찾는 것이 계산상 어려워야 하며 이를 충돌 저항성(collision resistance)이라 부른다.

즉, 입력값과 해시 값에 대해서, 해시 값을 망가뜨리지 않으면서 입력값을 수정하는 공격에 대해 안전해야 한다. 이러한 성질을 가지는 해시 값은 원래 입력값을 의도적으로 손상시키지 않았는지에 대한 검증 장치로 사용할 수 있다.


좀더 알기쉽게 표현하자면 인풋의 일부만 바뀌어도 결과값이 크게 바뀔때 위의 속성들을 만족하는 좋은 해시라고 보통 표현을 하게된다.


또다른 예로는 패스워드를 암호화하는 것을 들 수 있다.

일방향이고 리버스 엔지니어링이 힘들다는 역상저항성을 활용해서, 주민번호나 패스워드를 직접 DB에 저장하는 대신 해시값을 저장하게 되면,

운영자에게 비밀번호나 주민번호를 노출하지는 않으면서도 로그인 보안은 구현할 수 있게 된다.

(물론 여기서도 전혀다른 비밀번호가 같은 해시값을 도출하게되는 제2역상공격이 성공하게 되면 큰일이다)

반응형

'비트코인' 카테고리의 다른 글

비트코인 논문 리뷰  (0) 2021.08.31
블럭체인의 원리  (0) 2020.07.31
md5, sha  (0) 2017.12.12
비트코인 분석 연재를 시작하며  (1) 2017.12.10
hash 기초  (0) 2017.12.10

hash라고 했을때 헷갈리는 이유는 

자료구조로서 hash map (hash table) 과 

핵심함수인 hash function 이 헷갈려서 그런 이유가 크다.


비트코인에는 hash function이 내부적으로 사용된다.



자료구조로서 hash map은

key와 value가 있는데, key를 주면 value를 빠르게 토해내는 컨테이너..

기능으로 보자면 stl map이나 python dict 등과 비슷하다고 볼 수 있다.

단 map이나 dict는 보통 tree(그 중에서도 레드-블랙트리가 일반적)로 구현된다.

tree는 O(log N)의 복잡도를 가져서 hash의 O(1)보다 느리지만, 사용량에 따라 가변적으로 메모리를 할당하기 유리하기 때문.

거꾸로 말하면 hash는 미리 상당한 양의 메모리를 덩어리째 할당해야하는 구조라 trade-off가 있다고 볼 수 있다.

 

hash map은 O(1)의 속도를 위해서 쓰는것은 자명한데.. 어떤것이 빠른거냐.

Insert,Update,Delete,Search

위 네 가지 연산 모두 O(1)이다.

(물론 메모리공간을 처음에 충분히 할당한 경우이며, worst case 시나리오에서 collision이 발생하면 실제로는 이보다 느려질 수 있음)


c++에서는 stl map 을 쓰면 내부구현에 tree가 보통 사용되고, 내부적으로 tree가 아닌 hash가 사용되도록 하고 싶으면 unordered_map을 쓰면된다.



핵심함수인 hash function은 임의의 길이의 데이터를 고정된 길이의 데이터로 매핑하는 함수이다. 

이게 무슨 뚱딴지 같은 소리인가 하겠지만.. 

일단 이해를 돕기위해 string to int hash function의 예를 들어보자.

임의의 string이 들어왔을때 1024이하의 숫자하나로 매핑이 된다고 해보자.

예를 들면 "Kim Wonil"이 들어왔을때 42로 매핑이 되고,

"Lee Hana"가 들어왔을때 27이 매핑되는 식이다.

이런걸 만들어서 어따가 쓸까..

1. hash map 자료구조를 hash function을 통해서 만들면 O(1)로 매우 빠른 자료구조를 만들수 있다.

2. 긴 길이의 데이터를 짧은 길이의 숫자로 바꿔주는 구조로 인해서 변경내용이 있는지 확인하는데 쓸 수 있다.

예를 들어서 겉보기에는 매우 비슷한 소스코드 두 개가 있다고 해보자.

그런데 둘의 내용이 일치하는지 아닌지 확실하지 않다고 해보자.

그러면 나이브한 방법으로는 두 파일을 바이트 단위로 비교하는 방법이 있을 것이다.

그런데 이렇게 나이브하게 하지 않고, 두 파일의 내용에 대해 각각 (string to int) hash function을 적용한다음 결과로 나온 숫자를 비교하는 방법도 있다.

예를 들어 첫번째 파일에서는 내용으로 해시한 결과값이 42인데 두번째 파일로 나온 값은 27이라면 두 파일의 내용은 확실히 다르다고 판정할 수 있다.

단, 해시값이 예를 들어 42로 동일하다고 해도 반드시 두 파일의 내용이 같다고 역으로 보장해주지 않는다.

이는, 큰 숫자를 작은숫자로 매핑하는 과정에서 반드시 충돌이 발생할 수 있다는 점을 상기해 보면 쉽게 이해가 된다.


이를 다시 적어보면 다음과 같다.

두 해시 값이 다르다면 그 해시값에 대한 원래 데이터도 달라야 한다. (역은 성립하지 않는다)

해시 함수의 가장 기본적인 성질은 두 해시 값이 다르다면 원래의 데이터도 다르다는 것이다. 이 특징은 해시 함수가 결정적이기 때문이다. 

위의 내용중 바이트 단위로 비교한다는 나이브 접근이 심각해보이지 않을 수도 있는데, 

단 두개의 문서를 비교할때는 심각성이 크지 않을 수 있지만,

예를 들어 특정폴더에 있는 파일중 내용이 같은파일이 있는지 검사하는 프로그램을 짠다고 해보자.

나이브한 방법으로는 파일의 갯수가 n개라면 모든 pair에 대해서 n(n-1)/2번 비교를 해야 중복파일 여부를 알 수 있다. 즉 O(n2)

그런데 위의 해시함수를 사용한 방법으로는 해시함수값 끼리만 숫자비교를 하면 되기 때문에 O(n) 으로 수행속도를 줄일 수 있다.


3. 함호학이나 비트코인에서 사용되는 해시 함수는 관련글(http://sevity.tistory.com/22) 참조

 

http://www.partow.net/programming/hashfunctions/index.html

위링크에 가보면 엄청 많은 종류의 해시펑션을 제공함..

반응형

'비트코인' 카테고리의 다른 글

비트코인 논문 리뷰  (0) 2021.08.31
블럭체인의 원리  (0) 2020.07.31
md5, sha  (0) 2017.12.12
비트코인 분석 연재를 시작하며  (1) 2017.12.10
암호화 해시 함수(cryptographic hash function)  (0) 2017.12.10

sudo su로 먼저 root권한을 얻는다.


firewall-cmd --permanent --zone=public --add-port=80/tcp  이걸로 원하는 리슨포트를 추가한다.
firewall-cmd --reload 이거 해주면 반영 된다.

설정된 값 확인은
vi /etc/firewalld/zones/public.xml 이걸로 할 수 있다.


CentOS 6는 아래 확인

https://blog.miyam.net/7



반응형

'Programming > Linux' 카테고리의 다른 글

crontab  (0) 2019.09.27
리눅스 계정 관리  (0) 2019.09.19
리눅스 port 확인  (0) 2017.12.04
sudo 관련  (0) 2017.11.07
디스크/폴더 사용량, 남은용량 확인  (0) 2017.11.07

서버의 특정 포트가 열려있는지 확인하려면 다음 명령어를 사용하면 된다 


nc

예전에는 nc -z가 유용했는데 -z옵션이 없어져버림

nc -z 대신에 nc --send-only </dev/null  {ip} {port} 하면 connect 여부를 확인할 수 있다.

바로 다음 커멘드라인 떨어지면 connect 성공한거고 connect가 안되면 hang이 걸린다.


nc -v {ip} {port} 이걸로 되는것 같기도 하다.


nmap

nmap -Pn -p80 naver.com


위처럼 하면 naver.com에서 80포트가 열려있는지 확인할 수 있다.

열려있다면 다음처럼 보인다.


$ nmap -Pn -p80 naver.com


Starting Nmap 7.01 ( https://nmap.org ) at 2017-12-04 13:26 KST

Nmap scan report for naver.com (125.209.222.142)

Host is up (0.0044s latency).

Other addresses for naver.com (not scanned): 202.179.177.21 202.179.177.22 125.209.222.141

PORT   STATE SERVICE

80/tcp open  http


Nmap done: 1 IP address (1 host up) scanned in 0.03 seconds



위에서 open 또는 closed로 겁색되면 접근자체는 되는것인데 filtered로 검색되면 방화벽 등에서 접근을 차단한 것이다!


telnet


telnet naver.com 80

반응형

'Programming > Linux' 카테고리의 다른 글

crontab  (0) 2019.09.27
리눅스 계정 관리  (0) 2019.09.19
CentOS 7 방화벽  (0) 2017.12.04
sudo 관련  (0) 2017.11.07
디스크/폴더 사용량, 남은용량 확인  (0) 2017.11.07

펀드: 뮤추얼 펀드 + 수익증권인데 복잡하니 이 글에서는 뮤추얼 펀드를 생략하도록 하자.


주식: 직접투자

펀드: 간접투자



운용: 투자신탁운용
판매: 투자신탁증권, 증권사, 은행
수탁회사: 은행
대략 상식적으로 이해 잘되는 그림인데. 중요한건 판매와 운용, 그리고 수탁이 분리되어 있다는 것.
예를 들면 미래에셋디스커버리주식형펀드의 경우 미래에셋증권에서 판매하고 미래에셋자산운용에서 운용하는 식
(앞으로 자산통합법이 발효되면 운용사에서도 판매가 가능해진다고 한다)
수탁회사는 운용사가 고객 돈을 남용하는걸 방지하기 위해 존재(은행에 넣고 관리 감독)



수익증권: 펀드에 가입하면 주는 증권

수익증권을 세는 단위는 '좌'이다. 주식이 '주' 인것과 비슷


배당과 기준가 개념

배당

배당으로 인해 순자산가치는 하락하게됨(응?)


기준가(NAV : Net Asset Value) = (총순자산가치-수수료)/잔존수량 x 1000좌

총순자산가치가 상승하면 기준가도 상승하는 공식임을 알 수 있다.

펀드는 증권시장에서 주식 및 채권 등에 투자하는 자금의 집합체로서 ‘기준가’는 이러한 운용의 결과가 녹아있는 ‘펀드의 적정가격’입니다. 

또 기준가는 투자자들이 펀드에 투자 또는 환매할 때 거래의  '기준'이 되는 '가격'이기도 합니다.

예로 들어보면, 기준가격이 1,500원인 수익증권 펀드에 1,500만원을 투자하게 되면, 해당 고객은 1천만좌의 수익증권을 매입하게 되는 것입니다. 

이 펀드의 기준가격이 2,000원이 되면 해당 고객은 2,000만원(세금 납부 전)을 되찾게 됩니다. 

이 기준가격은 장 중에 주가가 변하더라도 변동하지 않으며 당일 증권시장 종료 후 당일 거래내역 등을 감안해 저녁 늦게 사무관리회사(혹은 자산운용사)가 펀드의 기준가를 다시 계산하고 다음날 아침에 공시하게 됩니다(공휴일 제외)

전일종가: 주식시장 오후 3시, 채권시장 오후 5시


익일매입방식

펀드의 매입제도가 과거 당일 매입방식에서 익일매입방식으로 변경됐습니다. 

매입가격을 모르고 투자한다고 해서 이를 '블라인드-Blind 매매방식'이라고 합니다. 

이 같은 펀드매입제도는 펀드가 주식처럼 단타매매 대상이 되는 것을 방지하고 이미 해당펀드에 투자한 가입자를 보호하기 위한 차원에서 마련됐습니다.

주가는 외부 쇼크로 급락하게 되면 다음날 곧바로 반등하는 사례가 많습니다. 따라서 다음날 주가가 강하게 오를 것이 뻔한 상황에서 주식시장이 종료된 3~5시쯤 판매사 창구를 통해 펀드에 투자해 두면 다음날 주가반등 차익을 고스란히 얻을수 있을 것입니다. 이외에도 증시의 대형호재가 장종료후 발표되는 경우에도 동일한 결과가 발생하게 됩니다.

이에 따라 정부는 주식이 50%이상 편입되는 펀드에 한해 오후 3시 이전 가입청약자와 이후 가입청약자로 구분하고 이전 청약자는 익일기준가격으로, 이후 청약자는 이틀후 기준가격으로 매입하도록 제도를 변경한 것입니다. 


펀드의 가격 계산

어떤 펀드가 발생 됐다고 해보자. 

처음 자산이 총 100만원이라고 하면 1원에 1좌이므로 총 100만좌가 발행된다.

기준가는 1000좌당 가격이므로 천원이된다.



기준가가 결정되는 시점

판매회사에서는 보통 16:30까지 펀드 매입 신청을 받음

이때 적용되는 기준가는 전일 종가임


주식과 펀드의 차이

주가는 하루종일 변하지만 펀드의 가격은 장 종료시 한번만 변함

펀드를 매수하면 전일종가로 사게됨




총 순자산가치


기준가: '주가'와 비슷

펀드는 주로 주식이나 채권에 투자하지만, 보유 주식이나 채권을 직접 나눠 주는 것이 아님.

개별 투자종목의 소유권을 갖는게 아님


펀드를 하나만들면 처음 1좌의 가격은 1원

1좌의 가치가 1원에서 2원으로 오르면 두배버는 것

근데 1좌는 주식에 비해 너무 단위가 작아 불편함. 그래서 1000좌단위로 묶어서 표시하고 이를 '기준가'라고 함


여기서 중요한 개념이 등장하는데..

펀드의 기준가는 매년 실시하는 결산 때 그간 번 돈을 배당하는 식으로 대부분 1000원에서 다시 출발!


좋은펀드는 결산때 배당을 많이 한 펀드라고 볼 수 있음



수익률 계산


펀드(수익증권)의 수익률을 계산하는 방법을 보면, "펀드수익률 = 현재의 기준가/ 가입시점의 기준가 -1"가 됩니다. (단순 수익률)

그러나 펀드의 경우에는 중간에 결산을 통해 배당을 실시하고 기준가를 낮추는 경우가 있습니다. 

이러한 경우에는 실제 투자금액을 기준으로 수익률을 계산하는 것이 정확합니다. 

즉, "펀드수익률 = 현재의 기준가 x 현재보유좌수 / 가입금액 -1"으로 계산하는 것이 정확한 수익률이 됩니다.

(배당을 하는 경우 추가로 수익증권을 받게됩니다.



자본이득배분, 재투자


외부링크

모닝스타..괜찮게 정리된듯하다.

http://www.morningstar.co.kr/fundSchool/ViewCourseArticle.asp?ctype=F&cseq=103&csub=1&Cnum=1&tab=6&subTap=1


반응형

'재무 금융' 카테고리의 다른 글

위험조정수익률(risk-adjusted return)  (0) 2018.01.26
z-score, Sharpe's ratio(샤프 비율)  (0) 2018.01.25
선물  (0) 2018.01.21
미국 Ticker Symbol 시스템 정리  (0) 2017.12.12
수익률  (0) 2017.11.21

다양한 수익률 개념에 대해 알아보자

 

1. 단순 수익률

단순 수익률은 투자금액이 투입된 시점을 고려하지 않고 기말평가때 단순하게 한 번에 계산하는 방법이다.

 

수익률이란 기본적으로 수익금액/투자금액 - 1.0 으로 계산된다. 즉, 100만원 투자해서 200만원이 되었으면 200()/100()1.0=1.0=100% 요렇게 계산하는 것

 

수익금액은 좀 더 정확하게 기말평가금액이라고 표현할 수 있다.

수익률 = 기말평가금액/투자금액 - 1.0

위에서 기말이란 평가기간말이란 의미이다. 

연간수익률이면 연말, 월간수익률이면 월말시점이 되겠다.

 

 

예를 들어 연간수익률을 계산한다고 하고, 1월1일에 50만원 5월3일에 추가로 50만원을 투자하고 연말에 200만원이 되었으면 200/(50+50)1.0=1.0=100% 이렇게 계산

 

2. 평잔수익률

위 단순 수익률의 문제점은 무엇일까?

투자시점을 고려하지 않은 계산이다보니, 수익률이 왜곡될 수 있다.

예를 들어서 1월1일에 100만원을 투자하여 12월30일에 200만원이 되었으면 단순수익률로 수익률은 100%이다.

그런데 마지막 날인 12월31일에 100만원을 추가로 투입했다고 해보자, 그러면 300/(100+100)1.0=50%가 되어 갑자기 수익률이 50%가 되어버린다.

금융회사에서 수익률에 따라 수수료를 받는다면 이런장난(?)을 허용하면 안될 것이다.

 

이를 회피할 수 있는 방법은 무엇일까?

바로 날짜별 잔고의 평균값을 투자금액으로 사용하자는 아이디어가 있을 수 있다.

예를 들어 위의 예의 경우는 1년 365일중 363일은 잔고(투자금액)가 100만원 이었다가 12월30일에 200만원이 되고, 12월31일에 추가 100만원 투입으로 300만원이 되었으므로, 평균잔고는

(100363+2001+3001)/365=100.82가 되어 수익률은 300/100.821.0=1.97=197%가 될것이고, 고객에게 불리한 결과이므로 위의 장난(?)이 통하지 않는 공정한 계산이 되었다.

 

평잔수익률 = 기말평가금액/평균잔고 - 1.0

 

 

3. 누적수익률(=시간가중수익률)과 평균수익률

자 위의 방법으로 수익률 까지는 구했다. 그런데 여러해 투자가 반복되다보면 수년간에 걸친 평균수익률이 궁금하게 될 것이다.

예를들어 첫해는 수익률이 20%이고 다음해는 30% 였으면 평균 수익률은 (20+30)/2=25%가 될까?

단순히 산술평균을 내면 그렇다. 하지만 다음 예제를 보자.

 

첫해는 100%수익을 올렸고, 다음해는 -100% 수익을 올렸다(-100%란 가진돈을 모두 잃는걸 의미한다)

그렇다면 평균수익률은 (100100)/2=0%라고 해야할까?

뭔가 이상하다.. 왜냐하면 일반적으로 0%수익률이면 본전치기를 의미하는데 지금 내 수중에 남은 돈은 하나도 없기 때문이다.(두번째 해에 모든걸 잃었기 때문)

 

이렇게 이상한 값이 나오는 이유는 산술평균을 썼기 때문이며 기하평균을 사용하면 이 문제는 해결된다.

위 예제를 다시 살펴보자.

 

첫해 100%수익, 다음해 -100%수익이면 누적수익률은 다음 공식에 따라 (1.0+1.0)×(1.01.0)1.0=1.0=100%가 된다.

(산술평균을 사용할때와 다르게 누적평균을 사용할때는 더하는게 아니라 곱한다)

 

누적수익률 = (1.0 + 첫해수익률) x (1.0 + 둘째해수익률) x ... x (1.0 + 마지막해수익률) - 1.0

= (1+R1) * (1+R2) * (1+R3)* ... (1+Rn) - 1

결과를 보면 나는 땡전한푼 없는데 누적수익률이 -100%로 나왔으므로 make sense 하다.

 

다른 예를 살펴보자

3년간 수익률이 10%증가 > 20%증가 > 15% 감소라고 한다면 누적수익률은 1.1×1.2×0.851=0.122(12.2%)이 된다.

그런데 기하평균을 해보면 31.1×1.2×0.851.0391..이고 의미를 따져보면 연평균 3.91% 증가했다는게 된다.

검증삼아 1.0391 x 1.0391 x 1.0391 - 1 해보면 0.122가 다시 나온다.. 과연 ㅎㅎ

 

그렇다면 평균수익률을 계산하는데 있어 산술평균보다 기하평균이 더 적합한 이유는 무엇일까?

이는 기본적으로 수익률이 비대칭이란 점을 생각해보면 힌트를 얻을 수 있다.

즉, 100%를 넘는 1000%수익률이라는 것은 있을 수 있지만 (100만원을 투자해서 1,100만원이 되면 1,000%수익률이다.),

-100%를 넘어 -1000%수익률(-1000%손해)이라는 것은 존재할 수 없다. 가진돈을 다 잃는 것이 바로 -100%수익률인데 어찌 더 떨어질 수 있으랴.

 

심화학습: 산술평균과 기하평균의 비교

2회의 투자를 했는데 한 번은 900%의 이익을 보고 한 번은 90% 손실을 보았다고 하자. 

 
이 2회의 투자가 동시에 일어났다면 평균 수익률은 (900+(90))2=405%이다. 
차례로 일어났다면 평균 수익률은  =94.8% 이다. 
 
산술평균은 모든 투자가 동시에 이루어질 경우의 수익이므로 대개 과대평가된다. 
기하평균은 모든 투자가 순차적으로 이루어지는 경우의 식이므로 대개 실제보다는 좀 과소 평가된다. 
 
대부분의 투자는 이 두가지 성질이 섞여 있어 실제 수익은 대개 산술평균과 기하평균 사이 어딘가에 위치한다. 
 
주의할점:
 
반드시 순차투자라고 해서 무조건 기하평균이 적용되는 것은 아니다. 
자산의 일정비율을 항시 투자하는 정률배팅의 경우는 적용되지만.. 
항상 100만원을 투자한다는 식의 정액배팅이 되면 순차적이더라도 산술평균이 적용된다!
 

 

4. 금액가중수익률

TBD

 

반응형

'재무 금융' 카테고리의 다른 글

위험조정수익률(risk-adjusted return)  (0) 2018.01.26
z-score, Sharpe's ratio(샤프 비율)  (0) 2018.01.25
선물  (0) 2018.01.21
미국 Ticker Symbol 시스템 정리  (0) 2017.12.12
펀드 이해하기  (0) 2017.11.24

다음의 문제를 생각해보자.

배열이 하나 주어진다.

{3, -2, 5, 7, -3, 1}


이 때 이 배열의 일부 원소를 더해서 0이 되는 경우가 있는지 알아내는 프로그램을 작성한다고 해보자.

위 배열에 대한 답은 True이다 {-2, 5, -3}을 부분집합으로 취해 더하면 0을 만드는 것이 가능하기 때문이다.


subset sum 문제는 NP-complete 문제중 가장 단순한 형태로서, brute-force 이외에 P솔루션이 존재하지 않는다.

brute-force의 시간 복잡도는 모든 부분집합을 구해서 더해보는 것이기 때문에 O(2n)이 된다.

다만, dynamic-programming을 쓸 경우, 반복계산을 cache하는 방법으로 수행속도를 빠르게 할 수 있으나 바로 그 cache한다는 속성 때문에 n의 범위가 커지면 메모리 초과로 쓸 수 없다.



오늘은 subset sum문제를 푸는 세 가지 방법을 검토해보자.


첫번째 방법은 재귀를 이용해서 푸는 방법이다.

bool go(vector<int>&v, int ix, int cur_sum, int count)
{
    if(ix==(int)v.size())
    {
        if(cur_sum==0&&count>0) return true;
        return false;
    }
    bool r1 = go(v, ix+1, cur_sum+v[ix], count+1);
    bool r2 = go(v, ix+1, cur_sum, count);
    return r1 || r2;
}

bool recursive(vector<int>& v)
{
    return go(v, 0, 0, 0);
}



두번째 방법은 binary operation을 사용해 재귀없이 loop 로 푸는 방법이다.

bool naive(vector<int>& v)
{
    int n = (int)v.size();
    for(int i=1;i<(1<<n);i++)
    {
        int s = 0;
        for(int j=0;j<n;j++) if((1<<j)&i) s+=v[j];
        if(s==0) return true;
    }
    return false;
}


위 두 가지방법은 시간 복잡도가 O(2n)이기 때문에 n이 20만 넘어도 너무 느려져서 쓸 수 없게 된다.


다음 세번째 방법은 dynamic programming(이하 dp)을 사용해서 푸는 방법이다.

bool dp(vector<int>& v)
{
    map<int, bool> m, m2;
    for(int i=0;i<(int)v.size();i++)
    {
        m2 = m;
        for(auto it=m.begin();it!=m.end();it++)
        {
            m2[it->first + v[i]] = true;
        }
        m2[v[i]] = true;
        m = m2;
    }
    if (m[0]) return true;
    return false;

}


위의 세번째 방법은 n의 범위가 너무 크지 않은 경우 상당히 빠른시간을 보장한다(O(n×m))

m은 n의 범위(최대값)이 되겠다. 최대값이 작은 경우는 다항시간에 가까운 실행시간을 보장한다.


testcase를 포함한 전체 소스코드는 다음과 같다.

#include <stdio.h>
#include <stdlib.h>
#include <vector>
#include <map>
#include <cassert>
#include <cstring>
using namespace std;
// problem statement
// implement a function which receive vector<int> v
// and return true if some elements of v can sum up to zero
// ex) v: {3, 7, 2, -5} ==> {3, 2, -5} can be sum up to zero ==> return true
//#define NOT_IMPLMENTED(exp) do {if(!exp) assert(false);} while(0)
//end not-inclusive
//[start, end)
//if b_allow_duplicate is false, elements will not duplicated
vector<int> get_random_array(int start, int end, int count, bool b_allow_duplicate = true, bool b_avoid_zero = true)
{
assert(b_allow_duplicate == true); // false, not implemented
int offset = 0;
if(start <0) offset=start;
start += offset;
end += offset;
vector<int> r;
for(int i=0;i<count;i++)
{
int n1 = rand() % (end - start) + start - offset;
if(b_avoid_zero && n1==0) n1=1;
r.push_back(n1);
}
return r;
}
void print(vector<int>& v)
{
printf("input: ");
for(int i=0;i<(int)v.size();i++) printf("%d ", v[i]);
printf("\n");
}
bool go(vector<int>&v, int ix, int cur_sum, int count)
{
if(ix==(int)v.size())
{
if(cur_sum==0&&count>0) return true;
return false;
}
bool r1 = go(v, ix+1, cur_sum+v[ix], count+1);
bool r2 = go(v, ix+1, cur_sum, count);
return r1 || r2;
}
bool recursive(vector<int>& v)
{
return go(v, 0, 0, 0);
}
bool naive(vector<int>& v)
{
int n = (int)v.size();
assert(n < 10);
for(int i=1;i<(1<<n);i++)
{
int s = 0;
for(int j=0;j<n;j++) if((1<<j)&i) s+=v[j];
if(s==0) return true;
}
return false;
}
bool dp(vector<int>& v)
{
map<int, bool> m, m2;
for(int i=0;i<(int)v.size();i++)
{
m2 = m;
for(auto it=m.begin();it!=m.end();it++)
{
m2[it->first + v[i]] = true;
}
m2[v[i]] = true;
m = m2;
}
if (m[0]) return true;
return false;
}
int main()
{
for(int i=0;i<100000;i++)
{
vector<int> v = get_random_array(-20, 20, 6);
print(v);
bool r1 = naive(v);
bool r2 = recursive(v);
bool r3 = dp(v);
assert(r1 == r2 && r2 == r3);
printf("result: %s\n", r1?"possible":"impossible");
}
return 0;
}
view raw subset_sum.cpp hosted with ❤ by GitHub


반응형

참고사항: python3.3부터는 venv를 공식지원하므로 가능하면 venv사용을 먼저 고려하는게 좋음

 

virtual environment가 필요한 이유

이거 없으면, 한 PC에서 여러프로젝트 운영할려고 할때 꼬임

프로젝트마다 python2, python3사용여부가 다르거나, 필요한 라이브러리 버전이 다르거나 할 때, 

여러개의 파이썬 프로젝트가 하나의 컴퓨터에서 충동을 일으키지 않고 존재할 수 있도록 도와줌

 

virtual environment 동작 원리

여기서 환경이란 파이썬 프로그램을 실행시키는데 필요한 모든것의 복사본을 가지고 있는 단순한 폴더입니다. 

전체 파이썬 스탠다드 라이브러리 복사본, pip 설치 프로그램 복사본, 그리고 site-packages 복사본 등을 포함. 

pip install을 사용하면, virtualenv 폴더 내부의 site-packages 폴더에 이를 설치합니다. 

 

virtualenv 설치하기

# sudo pip3 install virtualenv virtualenvwrapper

pip와 virtualenv는 일반적으로 글로벌 설치가 되어야하는 유일한 패키지입니다. 

이 두개를 설치하고 나면 나머지 패키지들은 가상 환경에 설치하면 되기 때문입니다.

virtualenvwrapper는 virtualenv를 사용하기 쉽게 만들어주는 추가 툴이라고 보면 됨

 

virtualenvwrapper를 위한 추가 설정

여기가 약간 골때림 1회성작업이긴 하지만, ~/.bashrc에 다음 5줄을 추가해주고 source ~/.bashrc해줘야 mkvirtualenv 커맨드가 먹음

virtualenvwrapper를 사용하지 않아도 가상환경사용은 가능하지만, 사용하기 직관적이지 않아 비추

 

export WORKON_HOME=~/.virtualenvs
export VIRTUALENVWRAPPER_PYTHON=/usr/bin/python
export VIRTUALENVWRAPPER_VIRTUALENV=/usr/local/bin/virtualenv
source /usr/local/bin/virtualenvwrapper.sh
export VIRTUALENVWRAPPER_ENV_BIN_DIR=bin

 

위 과정에서 no module named virtualenvwrapper 라는 오류가 나면 위의 sudo pip3 를 sudo pip로 해서 다시해본다.

혹은 sudo ln -sf /usr/bin/python3 /usr/bin/python 으로 기본 python 버전을 2에서 3으로 바꿔준다.

python3를 사용하는 virtual environment 만들기

프로젝트 루트 폴더(예를 들면 ~/workspace)로 이동후 아래처럼 하면 python3를 기본으로 하는 env라는 이름의 virtual environment가 만들어 진다.

# mkvirtualenv -p python3 env_name

이렇게 하면 ~/.virtualenv/env_name 여기에 환경이 만들어진다.

지울때는 rmvirtualenv env_name 하면 된다.

 

 

virtual environment에 진입하기

# workon env_name (coin) #

빠져 나올때는 deactivate 하면 된다.

 

requirements.txt관련해서는 여기참조

 

numpy에러나면 다음 명령어 수행(라즈베리파이의 경우)

sudo apt-get install libatlas-base-dev

 

반응형

'Programming > Python' 카테고리의 다른 글

Anaconda & Jupyter  (0) 2018.03.23
python numpy  (0) 2018.02.28
python에서 doxygen 사용해보기  (0) 2017.11.14
python import  (0) 2017.11.14
python 시간관련 함수  (0) 2017.10.31

mac에서 올ㅋ사전의 편리함에 감탄하고 있던중, 윈도우에서도 그 편리함을 느끼고 싶어서 검색해봤다.


일단 크롬 브라우저에서 사전 이용은 네이버사전 확장프로그램이 좋긴한데 직접 타이핑으로 입력할때 결과가 안뜨는 치명적인 버그가 상당히 오랬동안 고쳐지지 않고 있다.


바로 다음과 같은 현상..


확인해 보니 LINGOES사전이 가장 편리한 것을 확인할 수 있었다.
설치 과정은 여기, 여기를 참고했다.

사용하는 방법은 언제든지 미니사전 팝업이 필요하면 Ctrl+Alt+L을 누르면 된다.
그러면 다음처럼 팝업사전이 뜬다. (최고다!)

위 기능이외에도 Ctrl+우클릭을 하면 현재 커서위치에 있는 단어를 검색해준다.
단, 크롬에서는 기본적으로 안되는데 크롬에서도 사용하려면 다음 extension을 깔아야 한다.

반응형

'utility' 카테고리의 다른 글

텔레그램 봇생성  (0) 2021.06.19

머릿말

doxygen을 사용하면, 프로젝트의 전반적인 내용을 파악할 수 있는 document가 자동 생성된다.

doxygen을 오랜만에 사용해보고, 게다가 c/c++ 프로젝트가 아닌 python project에 사용해보는건 처음이라 그 과정을 적어본다.

전체 과정은 https://www.stack.nl/~dimitri/doxygen/manual/starting.html 이곳을 참조했다


독시젠 설치

먼저 doxygen을 설치하자, 


CentOS면 sudo yum install doxygen

Ubuntu면 sudo apt-get install doxygen

macOS면 brew install doxygen

이런식으로 간단하게 설치가 가능하다.


독시젠 configuration

독세젠을 돌리려면 먼저 configuration file을 만들어야 한다.


doxygen -g 라고 하면 Doxyfile 이라는 configuration 파일이 만들어진다. (에디터로 열어서 수정 가능하다)


주의할점: Doxyfile을 열어서 EXTRACT_ALL = NO를 YES로 변경해야 결과가 제대로 나온다.

(If the EXTRACT_ALL option is set to NO in the configuration file (the default), then doxygen will only generate documentation for documentedentities.)


독시젠 돌리기

간단하게

doxygen 이라고만 하면 자동으로 결과가 생성된다.


독시젠 생성 결과물 보기

html/index.html을 열어보면 결과를 확인할 수 있다.


여기까지만 해도 좋지만, graphviz를 설치하여 call graph를 추가하고, 여러가지 추가 옵션을 줘보자.


결과에 call graph 추가




위와 같은 call graph를 결과물에 포함시키기 위해서는, 먼저 graphviz라는 툴을 설치해야한다.

macOS의 경우 brew install graphviz로 설치 가능하다.


그다음 Doxyfile을 다시 열어서 아래 내용을 수정해준다.


-그래프 설정
CALL_GRAPH             = YES
CALLER_GRAPH           = YES


- Graphviz 바이너리 위치 설정
DOT_PATH               = /usr/bin/graphml2gv
HAVE_DOT               = YES

(위 graphml2gv 경로설정은 which graphml2gv 명령어를 통해서 확인한 다음에 넣으면 된다.)

- 클래스 상속 구조 포함
CLASS_DIAGRAMS          = YES

- 그래프를 text가 아니라 그래픽 버전으로 보여줌
GRAPHICAL_HIERARCHY     = YES 


- 대상 폴더의 하위 폴더도 대상으로 지정
RECURSIVE              = YES

- 생성되는 doc 파일이 많아지므로 하위폴더 생성
CREATE_SUBDIRS         = YES

- 문서에 구현 소스도 포함
INLINE_SOURCES         = YES

- 문서에 소스파일 추가 (Files에서 조회 가능)
SOURCE_BROWSER         = YES


(위 내용은 http://heavensbus.blogspot.kr/2014/07/doxygen-call-caller.html 이곳을 참조했습니다.)


반응형

'Programming > Python' 카테고리의 다른 글

Anaconda & Jupyter  (0) 2018.03.23
python numpy  (0) 2018.02.28
python virtualenv 가상환경  (0) 2017.11.16
python import  (0) 2017.11.14
python 시간관련 함수  (0) 2017.10.31

+ Recent posts