Skip to content

Latest commit

 

History

History
155 lines (108 loc) · 4.36 KB

2022.07.31.md

File metadata and controls

155 lines (108 loc) · 4.36 KB

퀵 정렬에서 파티션 구현하기


파티션함수에서 파라미터를 받을 때 3가지를 받는다. 리스트, start, end 로, 리스트는 파티션을 실행할 리스트고, start와 end는 파티션하려는 범위를 나타낸다.

처음엔 i와 b 포인터 모두 start인덱스에서 시작한다.

그리고 아직 아무 값도 판별하지 않았기 때문에 모든 값은 unknown 그룹에 존재한다.

[16(i, b), 11, 6, 13, 1, 4, 10, 7(pivot)]

이렇게 있을 때 첫 번째로 16을 판별하게 되면 pivot 인 7보다 크므로 big 그룹에 들어가야 한다.

그럼 i 포인터가 가리키는 인덱스만 1 증가하게 된다.

그럼 이렇게 된다.

[16(b), 11(i), 6, 13, 1, 4, 10, 7(pivot)]

b 전까지가 small 그룹,

b부터 i전까지가 big 그룹,

i부터 p전까지가 unknown 그룹,

p가 pivot그룹이라고 유추할 수 있겠다.

다음으로 11을 판별하게 되면 6은 pivot보다 작으므로 16과 6의 자리를 바꿔줘 6이 small그룹에 들어갈 수 있게 해준 뒤 i와 b의 인덱스를 모두 1씩 증가시켜 준다.

[6, 11(b), 16, 13(i), 1, 4, 10, 7(pivot)]

i의 인덱스가 pivot 까지 온 다음에는 b가 가리키는 값과 pivot을 바꿔준다. 그러면

[6, 1, 4, 7(b, p), 11, 16, 10, 13(i)]

이렇게 되고 첫 번째 pivot에 대한 파티션이 끝난다.

그럼 퀵 정렬은 어떻게 구현하면 될까?


퀵 정렬 구현


파티션을 구현했으면 그 다음은 간단하다.

재귀적으로 구현하기만 하면 된다. 재귀적 구현의 두 가지 필수 요소인 1. base case와 2. recursive case 만 염두하면 된다.

내 코드:

# 두 요소의 위치를 바꿔주는 helper function
def swap_elements(my_list, index1, index2):
    # 이전 과제에서 작성한 코드를 붙여 넣으세요!
    temp = my_list[index1]
    my_list[index1] = my_list[index2]
    my_list[index2] = temp
    
# 퀵 정렬에서 사용되는 partition 함수
def partition(my_list, start, end):
    # 이전 과제에서 작성한 코드를 붙여 넣으세요!
    pivot = my_list[end]
    i, b = start, start
    
    while i < end:
        if my_list[i] >= pivot:
            i += 1
        else:
            swap_elements(my_list, b, i)
            i += 1
            b += 1
    swap_elements(my_list, b, end)
    return b
    
# 퀵 정렬
def quicksort(my_list, start, end):
    # 코드를 작성하세요.
    if end - start < 1:
        return
    else:
        pindex = partition(my_list, start, end)
        quicksort(my_list, start, pindex - 1)
        quicksort(my_list, pindex + 1, end)

마지막 부분에 주목하면 재귀적으로 구현됐다는 것을 알 수 있다.


동적 프로그래밍 Dynamic Programming


다이나믹 프로그래밍의 조건으로는 두 가지가 있다.

  1. 최적 부분 구조(Optimal Substructure)

  2. 중복되는 부분문제(Overlapping Subproblems)

최적 부분 구조가 있다는 건 부분 문제들의 최적의 답을 이용해서 기존 문제의 최적의 답을 구할 수 있다는 것

중복되는 부분 문제는 피보나치 수 구하기 같은 문제에서 쉽게 찾아 볼 수 있는데, 예를 들어 7번째 피보나치 수를 구하기 위해 3번째 피보나치 수를 10번 이상 계산해야 한다고 하면, 그것은 굉장히 비효율적인 일이 될 것이다. 이런 것을 중복되는 부분 문제라고 한다.


또 다이나믹 프로그래밍을 구현하는 방법은 두 가지가 있다.

  1. Memoization : 중복되는 계산은 한 번만 계산 후 메모

  2. Tabulation : bottom-up 으로 table을 채워나가기



Memoization 으로 피보나치 함수 구현


내 코드:

def fib_memo(n, cache):
    # 코드를 작성하세요.
    if n in cache:
        return cache[n]
    elif n < 3:
        cache[n] = 1
        return cache[n]
    else:
        cache[n] = fib_memo(n-1, cache) + fib_memo(n-2, cache)
        return cache[n]


def fib(n):
    # n번째 피보나치 수를 담는 사전
    fib_cache = {}

    return fib_memo(n, fib_cache)



Tabulation 으로 피보나치 구현하기


내 코드:

def fib_tab(n):
    # 코드를 작성하세요.
    l = []
    l.append(1)
    l.append(1)
    i = 2
    while n > len(l):
        l.append(l[i-1] + l[i-2])
        i += 1
    return l[n-1]