이것이 알고리즘의 "Big O"표기법을 식별하기위한 적절한 "규칙"입니까?


29

Big O Notation과 알고리즘 작성 방법에 따라 계산 방법에 대해 자세히 배웠습니다. 나는 알고리즘 Big O 표기법을 계산하기 위해 흥미로운 "규칙"을 발견했고, 내가 올바른 길을 가고 있는지 또는 벗어 났는지 확인하고 싶었다.

큰 O 표기법 : N

function(n) {
    For(var a = 0; i <= n; i++) { // It's N because it's just a single loop
        // Do stuff
    }
}

큰 O 표기법 : N 2

function(n, b) {
    For(var a = 0; a <= n; a++) {
        For(var c = 0; i <= b; c++) { // It's N squared because it's two nested loops
            // Do stuff
        }
    }
}

큰 O 표기법 : 2N

function(n, b) {
    For(var a = 0; a <= n; a++) {
        // Do stuff
    }
    For(var c = 0; i <= b; c++) { // It's 2N the loops are outside each other
        // Do stuff
    }
}

큰 O 표기법 : NLogN

function(n) {
    n.sort(); // The NLogN comes from the sort?
    For(var a = 0; i <= n; i++) {
        // Do stuff
    }
}

내 예와 후속 표기법이 정확합니까? 알아야 할 추가 표기법이 있습니까?


3
공식 대신 경험 법칙이라고 부르십시오. 아마도 올바른 길을 가고 있습니다. 물론, 그것은 정확히 "물건"이하는 일에 전적으로 달려 있습니다. Log (N)은 일반적으로 일종의 이진 / 트리 같은 분할을 수행하는 알고리즘에서 비롯됩니다. 여기 주제에 대한 훌륭한 블로그 게시물이 있습니다.
Daniel B

15
2Nbig-O 표기법 과 같은 것은 없습니다 .
바텍

15
@ JörgWMittag Big O의 정의에 의해 O (2n) = O (n)이므로
ratchet freak

3
@ JörgWMittag : 이것은 정말로 트롤링의 장소가 아닙니다.
vartec

3
@vartec-JörgWMittag가 의도적으로 트롤링이라고 생각하지 않습니다. 최근의 연구에서 나는 엄격한 Big-O 표기법과 Big-O, Theta 및 기타 파생 상품을 혼합 한 "일반적인 용어"사이에 많은 혼란이 있음을 발견했습니다. 나는 일반적인 사용법이 정확하다고 말하지는 않는다. 많은 일이 일어납니다.

답변:


26

공식적으로 big-O 표기법 은 복잡성 의 정도 를 나타냅니다.

big-O 표기법을 계산하려면

  1. 알고리즘 복잡성의 공식을 식별합니다. 예를 들어, 다른 하나의 내부에 중첩 된 두 개의 루프가 있고 다른 세 개의 루프가 중첩되지 않은 경우를 가정 해 보겠습니다.2N² + 3N
  2. 가장 높은 항을 제외한 모든 것을 제거하십시오. 2N²
  3. 모든 상수를 제거하십시오.

다시 말해서, 다른 하나의 내부에 중첩 된 두 개의 루프가 있고, 중첩되지 않은 다른 세 개의 루프는 O입니다. (N²)입니다.

이것은 물론 루프에있는 것이 간단한 지침이라고 가정합니다. 예를 들어 sort()루프 내부에있는 경우 sort()기본 언어 / 라이브러리가 사용 하는 구현 의 복잡성과 루프의 복잡성을 곱해야합니다 .


엄밀히 말하면 "모든 상수를 제거"될지는 2N³으로 N. "가산 상수와 곱셈 상수를 모두 제거하십시오"는 진실에 더 가깝습니다.
Joachim Sauer

@JoachimSauer : N² = N * N, 상수가 없습니다.
vartec

@vartec : 같은 주장에 따라 2N = N+N.
Joachim Sauer

2
@JoachimSauer, 당신의 "엄격히 말해서"는 비 전통적인 것입니다. en.wikipedia.org/wiki/Constant_(mathematics)를 참조하십시오 . 다항식에 대해 말할 때 "일정한"은 항상 지수가 아니라 계수 만 나타냅니다.
Ben Lee

1
@vartec, 위의 내 의견을 참조하십시오. 여기서 "일정한"을 사용하는 것은 절대적으로 정확하고 관습 적이었습니다.
Ben Lee

6

이러한 알고리즘을 분석하려면 결과를 실제로 변경할 수 있으므로 // dostuff를 정의해야합니다. dostuff가 일정한 O (1) 수의 연산을 요구한다고 가정 해 봅시다.

이 새로운 표기법을 사용한 몇 가지 예는 다음과 같습니다.

첫 번째 예에서 선형 순회 : 이것이 맞습니다!

에):

for (int i = 0; i < myArray.length; i++) {
    myArray[i] += 1;
}

왜 선형 (O (n))입니까? 입력 (배열)에 추가 요소를 추가 할 때 발생하는 작업 수는 추가하는 요소 수에 비례하여 증가합니다.

따라서 메모리 어딘가에서 정수를 증가시키기 위해 한 번의 작업이 필요한 경우 f (x) = 5x = 5 개의 추가 작업으로 루프가 수행하는 작업을 모델링 할 수 있습니다. 20 개의 추가 요소에 대해 20 개의 추가 작업을 수행합니다. 배열의 단일 패스는 선형 인 경향이 있습니다. 또한 버킷 정렬과 같은 알고리즘도 있습니다.이 알고리즘은 데이터 구조를 활용하여 한 번의 단일 패스 정렬로 정렬 할 수 있습니다.

두 번째 예도 정확하고 다음과 같습니다.

O (N ^ 2) :

for (int i = 0; i < myArray.length; i++) {
    for (int j = 0; j < myArray.length; j++) {
        myArray[i][j] += 1;
    }
}

이 경우 첫 번째 배열의 모든 추가 요소 i에 대해 j를 모두 처리해야합니다. i에 1을 더하면 실제로 j에 (길이 j)가 추가됩니다. 따라서 당신은 맞습니다! 이 패턴은 O (n ^ 2)이거나,이 예에서는 실제로 O (i * j) (또는 i == j 인 경우 n ^ 2이며, 종종 행렬 연산 또는 제곱 데이터 구조의 경우입니다.

세 번째 예는 물건에 따라 물건이 바뀌는 곳입니다. 코드가 작성되고 작업이 상수 인 경우 크기가 n 인 배열의 2 패스가 있고 2n이 n으로 줄어들 기 때문에 실제로는 O (n)입니다. 서로 외부에있는 루프는 2 ^ n 코드를 생성 할 수있는 핵심 요소가 아닙니다. 다음은 2 ^ n 인 함수의 예입니다.

var fibonacci = function (n) {
    if (n == 1 || n == 2) {
        return 1;
    }

    else {
        return (fibonacci(n-2) + fibonacci(n-1));
    }
}

함수를 호출 할 때마다 함수에 대한 두 번의 추가 호출 (피보나치)이 생성되므로이 함수는 2 ^ n입니다. 함수를 호출 할 때마다해야하는 작업량이 두 배가됩니다! 이것은 히드라의 머리를 자르고 매번 두 개의 새로운 것이 생겨나는 것처럼 빠르게 자랍니다!

마지막 예제의 경우 merge-sort와 같은 nlgn 정렬을 사용하는 경우이 코드가 O (nlgn)임을 알 수 있습니다. 그러나 데이터 구조를 활용하여 특정 상황에서 더 빠른 정렬을 개발할 수 있습니다 (예 : 1-100과 같이 알려진 제한된 범위의 값). 그러나 최상위 코드가 우세하다고 생각하는 것은 맞습니다. 따라서 O (nlgn) 정렬이 O (nlgn)보다 적은 작업 옆에있는 경우 총 시간 복잡도는 O (nlgn)입니다.

JavaScript에서 (Firefox에서) Array.prototype.sort ()의 기본 정렬은 실제로 MergeSort이므로 최종 시나리오에서 O (nlgn)을보고 있습니다.


피보나치의 예가 실제로 피보나치입니까? 나는 이것이 당신이 만들려고 한 주장과 반대되는 것이 아니라는 것을 알고 있지만, 이름이 다른 사람들에게 오도를 일으킬 수 있으므로 실제로 피보나치가 아닌 경우 산만해질 수 있습니다.
Paul Nikonowicz

1

두 번째 예 (0에서 n 까지의 외부 루프 , 0에서 b 까지의 내부 루프 )는 O ( n 2 )가 아니라 O ( nb )입니다. 규칙은 당신이 n 번 무언가를 계산하고 각각의 다른 것에 대해 b 번 무언가를 계산하는 것이므로이 기능의 성장은 전적으로 n * b 의 성장에 달려 있습니다 있습니다.

세 번째 예는 O ( n )입니다. n으로 커지지 않는 모든 상수를 제거 할 수 있습니다 과 있으며 성장은 Big-O 표기법의 핵심입니다.

마지막 예에서, 예를 들어 Big-O 표기법은 정렬 방법에서 나올 것입니다. 정렬 방식이라면 (일반적으로 경우와 같이) 가장 효율적인 형식으로 O ( n * logn )입니다. .


0

이것은 대략적인 런타임 표현입니다. "거울의 규칙"은 정확하지 않지만 평가 목적으로 1 차 근사치가 양호하기 때문에 근사치입니다.

실제 런타임은 힙 공간의 양, 프로세서의 속도, 명령어 세트, 접두사 또는 접미사 증가 연산자의 사용 등 yadda에 따라 다릅니다. 적절한 런타임 분석을 통해 수락 여부를 결정할 수 있지만 기본 지식을 갖추면 처음부터 바로 프로그래밍 할 수 있습니다.

나는 교과서에서 실용적인 응용 프로그램으로 Big-O가 합리화되는 방법을 이해하는 데 올바른 길을 가고 있음에 동의합니다. 그것은 극복하기 어려운 장애물 일 수 있습니다.

점근 적 성장률은 큰 데이터 세트와 큰 프로그램에서 중요하므로 일반적인 예에서는 적절한 구문과 논리에 중요하지 않다는 것을 보여줍니다.


-1

큰 오, 정의에 따르면 : 함수 f (t)에는 함수 c * g (t)가 있습니다. 여기서 c는 t> n에 대해 f (t) <= c * g (t)> n 여기서 n과 같은 임의의 상수입니다. f (t)는 O (g (t))에 존재하며 컴퓨터 과학에서 알고리즘을 분석하는 데 사용되는 수학적 표기법입니다. 혼란 스러울 경우 클로저 관계를 살펴 보는 것이 좋습니다.이 방법을 사용하면이 알고리즘이 어떻게 큰 값을 얻는 지 더 자세히 볼 수 있습니다.

이 정의의 일부 결과 : O (n)은 실제로 O (2n)과 일치합니다.

또한 정렬 알고리즘에는 여러 가지 유형이 있습니다. 비교 정렬에 대한 최소 Big-Oh 값은 O (nlogn)이지만 더 큰 big-oh와 함께 많은 정렬이 있습니다. 예를 들어 선택 정렬에는 O (n ^ 2)가 있습니다. 일부 비 비교 정렬은 더 큰 값을 가질 수 있습니다. 예를 들어 버킷 정렬에는 O (n)이 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.