읽기 전..

  • 향후 상기한 제목으로 작성될 게시물은 모두 Geeks for Geeks Algorithms 페이지들을 번역 및 작성자의 해석대로 정리할 예정입니다.
  • 정확한 이론 베이스는 당연히 없고 학습한 내용을 정리할 목적으로 가볍게 작성하였기 때문에 오개념이 있을 수 있으니 혹시 페이지를 방문하신 분들께서는 참고바랍니다.
  • 개념을 바로잡기 위한 지적은 언제나 환영이니 부탁드립니다.

Algorithm Analysis? Performance?

흔히 알고리즘을 다룰 때 빠질 수 없는 개념은 '성능'이다. 성능이 보장되어 있어야 안정성, 보안 등 각종 이슈를 모두 해결할 수 있다. 알고리즘의 성능을 평가할 때는 단순히 '규모'에 대한 개념만 가지고 있으면 된다. 많은 양의 데이터를 정확하고 빠른 시간에 처리할 수 있는 알고리즘이 좋은 알고리즘이라고 생각하자.

단순하게 알고리즘 성능을 분석한다고 할 때 두 개의 알고리즘을 돌려서 적은 시간이 걸리는 알고리즘이 더 우수하다고 여겨진다. 그러나 적은 input부터 많은 input까지 고려하면 다음의 경우도 존재할 수 있다.

  1. 일부 input에서는 1번이 우수하고 다른 input에서는 2번이 우수한 경우
  2. 일부 input의 경우 어떤 기계에서는 1번이 우수하고, 다른 input의 경우 다른 기계에서는 2번이 우수한 경우

위 문제를 해결하기 위한 알고리즘 분석 개념 중 하나로 점근적 분석이 있다.

Asymptotic Analysis(점근적 분석)이란?

점근적 분석이란 알고리즘을 분석하기 위한 기법 중 하나로 input size대비 알고리즘이 처리하는 데 걸리는 시간(혹은 공간)에 대해 평가한다.

예를 들어, 빠른 머신에서 Linear Search를 수행할 때와 느린 머신에서 Binary Search를 수행할 때 일정 시점까지는 Linear Search가 빠르겠지만 input size가 점점 커질수록, 결국 Binary Search의 소요시간이 더 작아지는 시점이 온다.

  • 뒤에 나오는 개념이지만 Linear Search의 시간복잡도는 n인 반면, Binary Search의 시간복잡도는 $\log n$이기 때문

그렇다고 점근적 분석이 정확한 알고리즘 성능을 제시하지는 않는다.

$1000nlog n$과 $2nlog n$은 일정범위에선 분명한 차이가 있지만 점근적 분석에 따르면 "$nlog n$"이므로 서로 동일한 성능을 가진다고 판단하는 문제가 있다.

게시물 출처: Analysis of Algorithms | Set 1 (Asymptotic Analysis)

Linear Search 알고리즘에 대해 분석하기

Linear Search 알고리즘을 적용할 때 3가지 케이스에 대해 분석하기로 한다.

def search(arr, n, x):
    for i in range(n):
        if arr[i]==x:
            return i
    return -1
#arr는 입력 list, n은 arr의 길이, x는 찾는 값
  1. Worst Case

    서칭 알고리즘의 최악 케이스는 주어진 배열 내에 찾고자 하는 원소가 없어 모든 원소에 대해 탐색해야 하는 경우다. 특히, Linear Search의 경우 첫 번째 원소부터 하나씩 탐색하기 때문에 input size가 n이라면 최악의 경우의 시간복잡도는 Θ(n)이다.

  2. Average Case

    일반적인 경우는 평균으로 해석하자고 가정하면 균일하게 분포된 모든 케이스에 대해 걸리는 탐색시간을 케이스의 수로 나누면 된다. 즉, 찾고자 하는 원소가 첫 번째에 위치한 경우부터 존재하지 않는 경우까지 모든 시간을 더한 뒤 경우의 수로 나눈 결과는 아래와 같다.
    $$
    \begin{align}
    Average\;Case\;Time &= \frac{\sum_{i=1}^{n+1}(\Theta(i))}{n+1}
    \\&=\frac{\Theta((n+1)\cdot(n+2)/2)}{n+1}
    \\&=\Theta(n)
    \end{align}
    $$

  3. Best Case

    최상의 케이스는 찾고자 하는 원소가 첫 번째에 위치하여 한 번의 접근으로 탐색에 성공하는 경우로 시간 복잡도는 Θ(1)이다.

일반적으로 알고리즘을 분석할 땐 수행 시간의 상계(Upper bound)에 대한 정보를 제공할 수 있기에 가장 최악의 경우를 상정한다. 평균 케이스 분석은 모든 가능한 input에 대해 수학적으로 어떻게 분포되어 있는지 파악하고 있어야 하므로 잘 쓰이지 않는다.

다만 몇몇 알고리즘은 worst case와 best case 간의 소요시간이 동일한 경우가 있다. 예를 들어, Merge sort의 시간복잡도는 모든 케이스에 대해 Θ$(n\log n)$이다.

게시물 출처: Analysis of Algorithms | Set 2 (Worst, Average and Best Cases)

달고나 bof문서나 리버싱을 공부할 때 레지스터의 역할을 외우야 조금 빠르게 읽을 수 있을 것 같아서 정리하였다.


1. eax - 피연산자와 연산 결과의 저장소


범용 레지스터인 eax 레지스터는 산술(사칙연산 등), 논리 연산을 수행하며 함수의 반환값을 해당 레지스터에 저장한다.


연산과 관련된 명령이 eax 레지스터를 사용하기 때문에 eax 레지스터에 저장된 함수의 반환 값을 참고하여 호출 함수의 성공 여부, 실패 여부를 쉽게 파악할 수 있다.


32bit 체계로 전환됨에 따라 ax -> eax로 바뀌었으며 32bit로 구성되어 있다.


2. ebx - DB segment안의 데이터를 가리키는 포인터


범용 레지스터인 ebx 레지스터는 메모리 주소를 저장하기 위한 용도로 사용된다.

특정한 목적으로 설계된 레지스터는 아니며 레지스터의 일반 목적인 저장소로 사용된다.


ESI 레지스터나 EDI 레지스터와 결합될 수 있다.


3. ecx - 문자열 처리나 루프를 위한 카운터


범용 레지스터인 ecx 레지스터는 반복 명령어 사용 시 반복 카운터로 사용된다.

ecx레지스터에 반복할 횟수를 지정하고 반복 작업을 수행한다. 값을 감소시키면서 카운트를 센다는 특징을 가지고 있다.


코드 상에서 반복문은 카운터 값이 증가하는 방향이나 레지스터는 감소하는 방향이므로 혼동하지 말 것


4. edx - I/O(Input/Outut) 포인터


범용 레지스터인 edx 레지스터는 데이터 레지스터라고 부르며 기본적으로 eax 레지스터의 확장 개념으로 사용된다. 부호 확장 명령 등에 쓰인다.

큰 수의 곱셈 혹은 나눗셈 등의 연산이 이루어질 때 사용되며 eax 레지스터와 함께 사용된다.


5. esi - DS 레지스터가 가리키는 data segment 내의 어느 데이터를 가리키고 있는 포인터, 문자열 처리에서 source를 가리킴


DS 레지스터 : 세그먼트 레지스터의 일종으로 data segment의 시작 주소를 포함한다. 

데이터 연산을 위한 Source Index를 나타내거나 입력 데이터 스트림의 위치를 나타내기 위해 사용된다.


인덱스 레지스터의 일종으로 데이터 조작이나 복사 시에 소스 데이터의 주소가 저장된다.


6. edi - ES 레지스터가 가리키는 datasegment 내의 어느 데이터를 가리키고 있는 포인터, 문자열 처리에서 destination을 가리킴


ES 레지스터 : 세그먼트 레지스터의 일종으로 메모리 주소지정을 다루는 스트링(문자 데이터) 연산에서 사용된다.

스트링 연산에서 ES 레지스터는 edi 레지스터와 연관되며, 프로그램이 ES레지스터를 사용할 경우 적절한 세그먼트 주소로 초기화해야 한다.

데이터 연산을 위한 Source Index를 나타내거나 입력 데이터 스트림의 위치를 나타내기 위해 사용된다.


인덱스 레지스터의 일종으로 데이터 조작이나 복사 시에 소스 데이터의 주소가 저장된다.


7. esp - SS 레지스터가 가리키는 stack segment의 맨 꼭대기를 가리키는 포인터


SS 레지스터 :  세그먼트 레지스터의 일종으로 메모리 상에 스택의 구현을 가능케 한다. 주로 스택 세그먼트의 시작 주소가 저장된다.


SS 레지스터에 저장된 스택 세그먼트의 시작주소에 스택 포인터(stack pointer, sp or esp) 레지스터의 오프셋 값을 더하면 참조되고 이쓴ㄴ 스택의 현재 워드를 가리킨다.


함수 호출 시 함수에 전달되는 파라미터가 먼저 스택에 push되고 이후에 리턴 주소가 스택에 push된다.

esp 레지스터는 호출 스택의 가장 높은 위치를 가리키기 때문에 함수 호출 시 esp 레지스터는 리턴 주소를 가리킨다.


8. ebp - SS 레지스터가 가리키는 스택 상의 한 데이터를 가리키는 포인터


ebp 레지스터는 호출 스택의 가장 낮은 위치를 가리키기 때문에 사용되고 있는 스택 프레임이 소멸되지 않는 이상 ebp 레지스터의 값은 변하지 않는다.


9. eip - 현재 실행 중인 명령의 주소를 가리키는 포인터


CPU가 바이너리 코드를 실행시킴에 따라 EIP 레지스터는 CPU가 현재 어느 코드를 실행시키고 있는 중인지 나타내기 위해 계속적으로 실행되는 코드의 주소를 갱신한다.


보통 프로그램에서 ip(명령어 포인터) 레지스터를 참조하지 않으나 디버깅 프로그램을 사용하여 프로그램을 테스트하는 것처럼 ip 레지스터 값을 변경할 수 있다.


+ Recent posts