2014년 5월 13일 화요일

Project Euler : Problem.14

양의 정수 n에 대하여, 다음과 같은 계산 과정을 반복하기로 합니다.
n → n / 2 (n이 짝수일 때)
n → 3 n + 1 (n이 홀수일 때)
13에 대하여 위의 규칙을 적용해보면 아래처럼 10번의 과정을 통해 1이 됩니다.
13 → 40 → 20 → 10 → 5 → 16 → 8 → 4 → 2 → 1
아직 증명은 되지 않았지만, 이런 과정을 거치면 어떤 수로 시작해도 마지막에는 1로 끝나리라 생각됩니다.
(역주: 이것은 콜라츠 추측 Collatz Conjecture이라고 하며, 이런 수들을 우박수 hailstone sequence라 부르기도 합니다)
그러면, 백만(1,000,000) 이하의 수로 시작했을 때 1까지 도달하는데 가장 긴 과정을 거치는 숫자는 얼마입니까?
참고: 계산 과정 도중에는 숫자가 백만을 넘어가도 괜찮습니다.



규칙을 찾아보려고 했는데, hailstone sequence 로 검색을 해보니 아직까지도 연구가 되고 있는 수열인듯 했습니다. 처음엔 바이너리 트리로 구현을 해보려 했으나, 수행속도에 비해 트리 구현하는 시간이 훨씬 오래 걸릴 것 같아서 브루트 포스를 적용하였습니다. ( 실은 답이 너무 궁금했습니다. ) 후에 궁금해지면 트리를 구현 해봐야겠습니다.

포스팅 맨 아래에는 답이 있으므로, 원하지 않으시면 스크롤에 주의해주세요 :)


오류 1 : int 의 인식범위
 맨 처음에는 모든 숫자변수를 int 로 구현했습니다. C++의 int 범위는 -2,147,483,648 ~ 2,147,483,647 (약 21억) 정도인데, hailstone 그래프를 보면 20억보다 더 높이 치솟는 것을 볼 수 있습니다. 문제의 조건값 범위가 100만보다 훨씬 아래였다면 int 로도 무사히(?) 넘어갔을텐데, 이로 인해 변수타입의 범위에 대해 한번 더 경각심을 일깨우게 되네요.
 int로 구현하면, 범위 초과에 의해 답이 910107(475~6회 수행)이 나옵니다. 아무리 사이트에 답을 때려넣어도 "틀렸으니 재시도 바람" 이라는데, 코드는 아무리 봐도 정상인것 같을 뿐이고. 분명 어딘가 틀렸는데 어디가 틀린건지 알 수가 없었습니다. 결국엔 구글 검색을 해보았는데, 저와 같은 짓을 하신 분들이 꽤 계셨습니다 ㅋㅋㅋ 잠깐 동질감 느끼고, 얼른 변수형태를 바꿨습니다.

#include <iostream>
using namespace std;
int main(void)
{
    unsigned long int now = 1;      // 현재 연산 숫자
    int cnt = 0;                    // 연산수행 카운터
    unsigned long int nowMax = 0;   // 최다 연산수 숫자
    int cntMax = 0;                 // 최다 연산수
    unsigned long int tmpMax = 0;
    while ( now++ < 1000000 ) {
        cnt = 1;
        unsigned long int tmp = 0;  // 연산용 임시변수
        tmp = now;
        while(tmp > 1) {
            if ( tmp % 2 == 0 ) {
                tmp = tmp >> 1;     // 짝수이면 2로 나눔
            } else {
                tmp = tmp * 3 + 1;  // 홀수이면 n*3+1
            }
            if ( tmp > tmpMax ) tmpMax = tmp;
            cnt++;
        }
        if ( cnt > cntMax ) {       // 최다 연산수 확인
            cntMax = cnt;
            nowMax = now;
            cout << now << " " << cnt << " " << tmpMax << endl;
        }
    }
    cout << nowMax << " " << cntMax << endl;    // 출력
    return 0;
}

연산 도중 최대 값이 궁금하여 tmpMax를 삽입하였습니다.
위에 대한 출력은 다음과 같습니다.


2 2 1
3 8 16
6 9 16
7 17 52
9 20 52
18 21 160
25 24 160
27 112 9232
54 113 9232
73 116 9232
97 119 9232
129 122 9232
171 125 9232
231 128 9232
313 131 13120
327 144 13120
649 145 41524
703 171 250504
871 179 250504
1161 182 250504
2223 183 1276936
2463 209 1276936
2919 217 1276936
3711 238 1276936
6171 262 8153620
10971 268 27114424
13255 276 27114424
17647 279 27114424
23529 282 50143264
26623 308 106358020
34239 311 121012864
35655 324 121012864
52527 340 121012864
77031 351 593279152
106239 354 1570824736
142587 375 2798323360
156159 383 2798323360
216367 386 17202377752
230631 443 17202377752
410011 449 24648077896
511935 470 24648077896
626331 509 24648077896
837799 525 56991483520
837799 525



참조
자바스크립트 연산범위 참고 : http://lambdaexp.tistory.com/59
동일한 오류 (int범위 초과) : http://www.dreamincode.net/forums/topic/165403-project-euler-collatz-sequence/

2014년 5월 9일 금요일

[node.js] event.js:72, EN0ENT open 에러 원인

fileBegin() called
uploadFile { domain: null,
  _events: {},
  _maxListeners: 10,
  size: 0,
  path: '/uploads/2579744ac81bd1cd2c1ae298f548783e.jpg',
  name: '13_11_12_22_32_52_81172431_1350490027.jpg',
  type: 'image/jpeg',
  hash: null,
  lastModifiedDate: null,
  _writeStream: null }
recieved: 3071 expected: 72775

events.js:72
        throw er; // Unhandled 'error' event
              ^
Error: ENOENT, open '/uploads/13_11_12_22_32_52_81172431_1350490027.jpg'

외부모듈 :
formidable
 스트림으로 파일이 수신되다가 오류가 발생했는데, 무작정 모듈 사용이 잘못 된 것이라 생각하고 example과 구글링을 열심히 하였습니다. imageMagicK(=gm) 을 깔았더니 갑자기 해결되었다는 스택오버플로 글을 따라 해보았지만 현상은 동일했습니다.

 EN0ENT, open (event.js:72) 의 에러는 대부분 파일 접근에 대한 문제입니다.
디렉토리에 대한 접근이 가능한지 (접근 권한 관련)
디렉토리 경로가 올바른지 (존재하는 디렉토리인지)
파일명에 줄바꿈문자(CR or LF)가 포함되어 있는지

위 세가지를 확인해보면, 대부분 해결이 가능합니다.
예시 코드는 루트경로인 /upload가 아닌 상대경로로 수정 후 해결되었습니다. 허허

참고 :
http://stackoverflow.com/questions/21504919/node-js-error-enoent-open-file-path-when-nothing-has-been-changed

2014년 4월 29일 화요일

[Oracle] Nested Loop


오라클을 사용하면서, 실행계획(Explain plan)을 돌리면 조인 수행 방식에 따라 cost가 들쭉날쭉한 것을 볼 수 있었다.
그 중, NESTED LOOP 라는 것이 가장 많이 눈에 띄었는데, 이를 정리해보고자 했다.

Nested Loop ( NL )

    쉽게 설명하면 다중루프 라고 볼 수 있다.
    for ( int i = 0 ; i < rows; i ++ ) {
      for ( int j = 0 ; j < columns; j++ ) {
      // 데이터 처리
      }
    }
    

    위키에선 다음과 같이 설명하고 있다.
    수행속도 = 드라이빙 테이블의 튜플 수 * 후행 테이블의 자료 량 + 드라이빙 테이블의 자료 량
    BigO 표기법으로 표현 시, 수행속도는 O( |R| |S| )
    ( R: 드라이빙 테이블 튜플 수, S: 후행 테이블 튜플 수 )

    •  수행에 가장 영향을 미치는 것은 '드라이빙 테이블의 튜플 수' 이다.

    수행속도를 위해서는 드라이빙 테이블이 작을 수록 좋다고 한다. 후행 테이블의 크기 역시 상관이 있을 것이라 생각하지만, 위의 수행속도 식은 다음과 같이 표현되기도 한다.
    수행속도 = 드라이빙 테이블의 튜플 수 * 후행 테이블 접근 횟수
    여기서, 후행 테이블 접근 횟수는 인덱스를 통해서 줄이는 방식으로 튜닝하는 것이 옳다고 한다.


    2014년 4월 22일 화요일

    Project Euler : 발견

    얼마 전에, 흥미로운 사이트를 찾아내었다.

    오일러 프로젝트(Project Euler)라는 사이트이다.
    (http://projecteuler.net)

    수학자 오일러에 관련된 공식부터 시작하여, 중고등학교 때 배웠던 여러 이론들이 다소 응용된 문제들을
    약간의(?) 수학과 프로그래밍으로 풀어나가는 곳이다.

    사이냅소프트라는 곳에서, 이 재미있는 사이트를 한글로 번역해주고 있다.
    (http://euler.synap.co.kr)
    원본 사이트에는 434번까지 게시되어 있고, 위 사이트엔 110번 문제까지 번역되었으며,
    나는 현재 13번 문제까지 완료하였다.

    완료한 문제에 대해서는 해당문제의 댓글로 자신의 소스코드를 공유할 수 있다는 것도 굉장히 큰 매력포인트이다.


    뒤로 갈수록 난이도도 높아지고, 수학적인 지식이 더욱 많이 필요한 것으로 보인다.
    하지만 앞부분이라 하여, 그리 쉽게쉽게 풀리는 문제들은 아니다.

    프로그램만 믿고 30초~1시간 동안 정답을 계산해내도록 짤 수는 없지 않은가?

    물론 답을 구하는 데에만 집중한다면, 계산시간에 대한 의미가 없지만
    프로그래밍을 하는 사람으로서 답을 최대한 빠르게 연산해내고자 하는 욕구가 샘솟을 수 밖에 없다.


    따라서 나도 내 스스로 머리를 굴려보도록 코드를 올려보고자 한다.


    시작! ㅋ

    [ Linux / Ubuntu ] apt-get 커널 의존성 문제가 해결됨

    $ sudo apt-get -f autoremove
    패키지 목록을 읽는 중입니다... 완료
    의존성 트리를 만드는 중입니다       
    상태 정보를 읽는 중입니다... 완료
    의존성을 바로잡는 중입니다... 완료
    다음 패키지를 더 설치할 것입니다:
      linux-server
    다음 패키지를 업그레이드할 것입니다:
      linux-server
    1개 업그레이드, 0개 새로 설치, 0개 제거 및 0개 업그레이드 안 함.
    1개를 완전히 설치하지 못했거나 지움.
    1,734 바이트 아카이브를 받아야 합니다.
    이 작업 후 0 바이트의 디스크 공간을 더 사용하게 됩니다.
    계속 하시겠습니까 [Y/n]? y
    받기:1 http://kr.archive.ubuntu.com/ubuntu/ precise-updates/main linux-server amd64 3.2.0.60.71 [1,734 B]
    내려받기 1,734 바이트, 소요시간 0초 (4,508 바이트/초)
    dpkg: 의존성 문제로 linux-server을(를) 설정할 수 없습니다:
     linux-server 패키지는 다음 패키지에 의존: linux-image-server (= 3.2.0.58.69): 하지만:
      시스템에 있는 linux-image-server의 버전은 3.2.0.60.71입니다.
     linux-server 패키지는 다음 패키지에 의존: linux-headers-server (= 3.2.0.58.69): 하지만:
      시스템에 있는 linux-headers-server의 버전은 3.2.0.60.71입니다.
    dpkg: linux-server을(를) 처리하는데 오류가 발생했습니다 (--configure):
     의존성 문제 - 설정하지 않고 남겨둠
    보고서를 작성하지 않습니다. 이미 MaxReports 값에 도달했습니다.
                                                                  처리하는데 오류가 발생했습니다:
     linux-server
    E: Sub-process /usr/bin/dpkg returned an error code (1)

    위처럼 linux-server 패키지에 의존성 문제가 생겼다.
    상황 )
    /boot/ 파티션의 용량이 가득 차서, apt-get upgrade가 안되는 상황이었고
    이를 해결하기 위해 가장 오래된 버전의 이미지들을 루트로 잠시 옮긴 뒤 apt-get autoremove 를 실행하였는데, 그 과정에서 위처럼 linux-server 의 의존성이 꼬임
    아래 명령들을 모두 실행해 보았다.
    sudo apt-get -f autoremove
    sudo apt-get autoclean
    sudo apt-get clean

    달리 상황이 나아지지 않아서, apt-get install --reinstall 도 시도해보았지만, 저장소에 데이터가 없다며 실패함 ㅠㅠ

    결국 패키지 의존성을 무시하고 삭제하는 방법을 택했다.

    sudo dpkg --purge --ignore-depends=linux-server linux-server
    커널 패키지중 일부를 날려버리는 엄청난(?) 일이었지만, 데이터도 그리 많지 않아 감행해보았다.
    그 결과, 위와 같은 의존성 문제가 해결되었다.

    $ sudo dpkg --purge --ignore-depends=linux-server linux-server
    (데이터베이스 읽는중 ...현재 135471개의 파일과 디렉터리가 설치되어 있습니다.)
    linux-server 패키지를 지우는 중입니다 ...
    $

    그리고 다시 apt-get -f install 을 확인해 본 결과, 깨끗해졌다.
    $ sudo apt-get -f install
    패키지 목록을 읽는 중입니다... 완료
    의존성 트리를 만드는 중입니다       
    상태 정보를 읽는 중입니다... 완료
    0개 업그레이드, 0개 새로 설치, 0개 제거 및 0개 업그레이드 안 함.

    좀 위험해보이는 방법이라 추천하지는 않지만, 데이터 백업이 충분하고 원인을 도저히 못찾겠다면 시도해 보아도 될 것 같다.

    참조 블로그 :
    http://seoz.egloos.com/viewer/3818586

    2013년 11월 23일 토요일

    [C/C++] Reading. "Effective C++ by Scott Meyers, 2006"

    This book is introduced by all the developers for C++,  as a required reading.

    now I'm reading on half of this, and I say

    "You Must have read before you want to build something real"

    and I feel regretful to spending last time without reading this.

    Still half, but I learned a lot. and this teaches me the concept of OOP with examples of codes. 

    And now I'm REALLY recommend this book if you having a job or interesting to C++ programming.

    what if you have skills, read first. and Keep on reading and reading.


    2013년 10월 7일 월요일

    [VI/VIM] 찾기 & 바꾸기 정리

    vi에서 찾기 및 바꾸기를 정리합니다.
    워낙 사용방법이 무궁무진하여, 개략적으로 정리했습니다.
    KLDP의 스레드를 참조하였습니다. (https://kldp.org/node/74138)

    1. 사용법
    vi 화면에서 command mode ( ':' 입력 ) 으로 전환 후, 입력합니다.

    2. 일반적인 형식
    :[범위]s/[찾을문자열]/[바꿀문자열]/[옵션]
    - 간단예시
     :%s/abc/efg/g : 문서 전체에서 abc를 efg로 치환



    - 범위
    • 일반 형식 : "[시작라인],[끝라인]" , "[특수문자]"
    • 예제 
      • 1,25 : 1행부터 25행까지
        • :1,25s/a/b/g : 1행부터 25행까지 a를 b로 모두 치환
      • % : 문서 처음부터 마지막까지
        • :%s/a/c/g : 문서 전체에서 a를 c로 모두 치환
      • .,200 : 현재 커서 행부터 200행까지
        • :.,200s/a/d/g : 현재 커서 행부터 200행까지 a를 d로 모두 치환
      • .,$ : 현재 커서 행부터 문서 마지막 까지
        • :.,$s/a/e/g : 현재 커서 행부터 문서 마지막까지 a를 e로 모두 치환
      •  : [무입력] 현재 행만
        • :s/a/g/g : 현재 행만 a를 g로 모두 치환

    - 찾을문자열, 바꿀문자열
    • 일반 형식 : [문자열], [특수문자][문자열]....
    • 예제
      • & : 특수문자. 찾을문자열을 대입한다.
        • char/_&_ : 'char'를 '_char_'로 치환
      • a/b : 일반문자열. a를 b로 치환한다.
      • [정규표현식] : 문자열에는 정규표현식이 가능하며, 이를 통해 다양한 응용이 가능하다.
      • \(ab\)\(cd\)\(ef\)/\3\1\2 : 각 문자열의 순서를 정렬한다. 예시의 경우 결과는 'efabcd'가 된다.
    - 옵션
    • g : grep. 해당 조건에 만족하는 모든 행에 수행한다. g! 인 경우, 해당 조건에 만족하는 행을 제외한 모든 행에 수행한다.
    • c : choose(?). 바꿀 문자열마다 확인 및 취소를 선택할 수 있다.
    • ":help s_flags" 로 옵션 조회 가능.


    정규표현식을 사용하면, 정말 무궁무진한 수식이 다 나오지만, 쓰이는 빈도는 그리 많지 않을 것 같습니다. 포스트 앞부분의 링크를 따라가 보시면, 정말 많은 표현이 가능하다는 것을 보실 수 있습니다.