Home » 알고리듬

알고리듬

프로젝트 오일러 51

오랜만에 다시 시작하는 프로젝트 오일러 문제이다. 참고로 50번을 넘어서면서부터는 꽤 어려운 문제들이 많이 나오기 시작한다.

두자리 숫자 *3 의 첫번째 자리를 여러가지로 바꿨을 때 가능한 아홉가지의 결과 중에서 13, 23, 43, 53, 73, 83의 여섯개는 소수입니다. 56**3의 세 번째와 4번째 자리를 동일한 숫자로 바꿔서 만들어지는 10개의 다섯자리 숫자 중에는 아래에서 보듯이 7개가 소수가 되며, 이것은 이런 식으로 7개의 소수가 만들어지는 첫번째 경우입니다. 이 소수 집단의 첫번째 수인 56003은 이런 성질을 갖는 가장 작은 소수입니다.

56003, 56113, 56333, 56443, 56663, 56773, 56993

위의 예처럼 원래의 일부를 동일한 숫자로 치환했을 때 8개의 소수 집단이 만들어지는 경우를 찾고, 그 집단에 속한 가장 작은 소수를 구하세요. 치환하는 자리는 인접하지 않아도 되고, 가장 앞부분을 치환하는 경우에는 거기에 0은 올 수 없습니다.

더 보기 »프로젝트 오일러 51

정수 배열의 최대 부분합 (연습문제)

배열의 연속된 부분집합의 최대합 정수 배열이 주어질 때 배열내의 연속된 원소를 더한 부분합의 최대값을 구하는 문제이다. 구간이 아닌 최대값 자체를 구하면 되는 문제이다. 간단히 생각하면 0부터 1, 2, 3 … n 까지의 합 중 최대인 값, 그 다음은 1부터 2, 3, 4 .. n 까지의 값 중 최대인값 이렇게 비교해나갈 수 있는데 그러면 반복계산이 너무 많다. 따라서 DP를 이용한다. 0번 인덱스까지의 최대합은 0번 값 그 자체이다. i번 인덱스까지의 최대 부분합은, (i-1) 번 까지의 최대 부분합과 i번 값의 합이다. 이 때… 더 보기 »정수 배열의 최대 부분합 (연습문제)

연습문제(벽과 폭탄)

벽과 폭탄

각 테스트 케이스마다 ‘W’, ‘B’로 이루어진 문자열이 입력된다. B는 폭탄을, W는 벽을 의미하는데, 폭탄이 하나 터지면 폭탄을 중심으로 반경 2칸 이내의 벽이 모두 폭파된다. 이 때 폭탄이 일시에 터졌을 때 폭파되는 벽의 개수를 출력하라. (https://www.hackerearth.com/problem/algorithm/bob-and-bombs-cake-walk/description/)

BWW, BW, WB, WWB에 해당하는 W의 수를 세는 문제이므로, 이는 간단하게 정규식으로 해결할 수 있다.
더 보기 »연습문제(벽과 폭탄)

Fast Fibonacci Transform

빠른 피보나치 항 구하기 피보나치 수열은 0, 1, 1, 2, 3, 5, 8,… 과 같이 앞의 두 항의 합이 그 앞의 항이 되는 수열이다. 피보나츼 수열의 n 항을 구하는 함수는 보통 이렇게 많이 작성한다. def fib(n): if n < 3: return (0, 1)[n] return fib(n-1) + fib(n-2) 하지만 이 함수는 지극히 비효율적이라 %time fib(35) Wall time: 7.05 s Out[7]: 9227465 이런 처참한 성능이 나온다. 이는 한 번 계산했던 값을 계속 재계산하기 때문인데, 이런 문제를 피하기위해서는 메모이제인션이라는 캐싱 기법을 쓴다. #… 더 보기 »Fast Fibonacci Transform

체를 사용하여 소수 집합 구하기

가장 빠른 알고리듬 현재까지 순수 파이썬으로 작성된 알고리듬 중 가장 빠른 것으로 알려진 것은 @Robert William Hanks가 개발한 아래 알고리듬이다. def primes(n): """Return a list of primes under n""" sieve = [True] * (n/2) for i in xrange(3, int(n**0.5)+1, 2): if sieve[i/2]: sieve[i*i/2::i] = [False] * ((n-i*i-1)/(2*i)+1) return [2] + [2*i+1 for i in xrange(1, n/2) if sieve[i]]

셸 정렬 (Shell Sort)

셸 정렬은 정렬 방법의 원리로만 보자면 “분할 증분 정렬”(diminishing incremental sort)이라고도 불리는 방법이며, 굉장히 오래된 정렬 알고리듬 중 하나이다. 이 정렬 방법은 삽입 정렬을 근간으로 하면서 삽입 정렬의 성능을 간단한 아이디어로부터 크게 증가시키는 방법이다.

삽입 정렬은 왼쪽에 있는 원소부터, 그 원소가 이동할 수 있는 ‘가장 왼쪽 자리’에 그 값을 삽입하여 정렬해 나가는 방식이다. 이 때 ‘삽입’을 위한 공간을 만들기 위해 원래자리부터 삽입될 자리 사이의 값들이 하나씩 오른쪽으로 이동해야 한다. 따라서 삽입 정렬은 어떤 원소가 이동해야 할 거리가 크면 클수록 효율이 떨어지며 배열이 반대로 정렬되어 있는 경우가 이에 해당할 것이다.

더 보기 »셸 정렬 (Shell Sort)

병합정렬

병합정렬은 기초적인 정렬 알고리듬 중에서 널리 알려진 알고리듬 중 하나이며, 대표적인 분정복 알고리듬의 예인 동시에 재귀 알고리듬의 좋은 예이다. 이름에 ‘병합'(merge)이 들어가는 이유는 배열을 2개 혹은 그 이상의 작은 조각으로 나누고 각각의 조각을 정렬한 다음, 각 조각의 앞에서부터 가장 작은 값을 순서대로 골라서 정렬된 결과를 생성하기 때문이다.

원래의 배열을 쪼갠 각각의 조각 역시 똑같은 병합 정렬을 이용해서 정렬하는 재귀적인 동작을 수행한다. 재귀적 알고리듬의 수행 과정을 복잡하고 어렵게 여기는 사람들이 있는데, 입력과 결과에 집중하는 방식으로 바라보면 오히려 더욱 명료하고 간단하다는 것을 알 수 있다.

더 보기 »병합정렬

동적계획법

동적 프로그래밍

동적 프로그래밍은 세부 계산으로 나뉘어지는 하나의 큰 문제를 세부 계산 결과를 미리 구해서 저장한 후 큰 계산의 결과를 빠르게 도출해내는 문제해결 기법이다.(이름과는 달리 프로그래밍 테크닉은 아니다.) 흔히 피보나치 수열을 계산할 때 memoization도 동적 프로그래밍의 범주로 볼 수 있다. 더 보기 »동적계획법

순열생성로직연구

순열생성로직

순열을 만드는 가장 간단한 방법으로는 특정 원소를 하나씩 뺀 후 배열의 나머지를 순열한 각 결과에 빼냈던 원소를 앞에 붙여준 결과를 모으는 방법이 있다. 대략의 코드는 다음과 같다.

def rec_perm(iterable):
    if len(iterable) == 1:
        return [list(iterable)]
    result = []
    for i in range(len(iterable)):
        head = iterable[i]
        tail = iterable[:i] + iterable[i+1:]
        result += [[head] + x for x in rec_perm(tail)]
    return result

이 알고리듬의 가장 큰 문제는 한 번에 모든 순열을 다 생성해서 그 리스트를 만환한다는 말이다. 만약 원소가 100개라면?1 메모리 부족으로 컴퓨터가 뻗을 수 있다. 물론 10개 미만의 연속열에 대해서는 나름 나쁘지 않은 성능을 낼 수 있다. 더 보기 »순열생성로직연구

삽입정렬

삽입정렬(insertion sort)은 기본적인 제자리 정렬 알고리듬 중 하나로, 배열 내의 어떤 위치의 원소를 해당 배열의 가능한 가장 왼쪽 자리에 ‘삽입’하는 동작을 통해 정렬을 수행한다. 삽입 정렬의 이론적인 성능은 O_{(n^2)}이지만, 데이터가 정렬된 상태에 가깝다면 삽입 동작이 그 만큼 적게 일어나므로 더 빨라질 수 있다. 현실 세계의 데이터는 완전히 랜덤하기보다는 약간은 정렬된 경향을 가지므로 같은 O_{(n^2)}인 버블정렬 알고리듬보다는 더 빠르게 동작하는 것으로 알려져 있다.

더 보기 »삽입정렬