[algorithm] 알고리즘의 시간 복잡성을 찾는 방법
질문
알고리즘의 시간 복잡성을 찾는 방법은 무엇입니까?
SO에 질문을 게시하기 전에 무엇을 했습니까?
그러나 시간 복잡성을 계산하는 방법에 대한 명확하고 직접적인 설명을 찾을 수있는 곳은 없습니다.
내가 뭘 알 겠어 ?
아래 코드와 같이 간단한 코드를 말하십시오.
char h = 'y'; // This will be executed 1 time
int abc = 0; // This will be executed 1 time
아래와 같은 루프를 말하십시오.
for (int i = 0; i < N; i++) {
Console.Write('Hello World !');
}
int i = 0; 이것은 한 번만 실행됩니다 . 시간은 실제로 i=0
선언이 아닌 계산됩니다 .
i <N; N + 1 번 실행됩니다
i ++; 이것은 N 번 실행 됩니다
따라서이 루프에 필요한 작업 수는
{1+ (N + 1) + N} = 2N + 2
참고 : 시간 복잡도 계산에 대한 이해가 확실하지 않기 때문에 여전히 잘못되었을 수 있습니다.
내가 알고 싶은 것?
좋아,이 작은 기본 계산은 내가 알고 있다고 생각하지만, 대부분의 경우 시간 복잡도를
O (N), O (N2), O (로그 N), O (N!) …. 많은 다른 ,
누구나 알고리즘의 시간 복잡성을 계산하는 방법을 이해하도록 도울 수 있습니까? 나는 이것을 알고 싶어하는 초보자가 많이 있다고 확신합니다.
답변
알고리즘의 시간 복잡성을 찾는 방법
입력 크기의 함수로 실행할 기계 명령어 수를 더한 다음 식을 가장 큰 (N이 매우 큰 경우) 용어로 단순화하고 단순화 상수를 포함 할 수 있습니다.
예를 들어 2N + 2
기계 명령어를 단순화하여이를 간단히 설명 하는 방법을 살펴 보겠습니다 O(N)
.
왜 두 개를 제거 2
합니까?
N이 커짐에 따라 알고리즘의 성능에 관심이 있습니다.
2N과 2라는 두 용어를 고려하십시오.
N이 커짐에 따라이 두 항의 상대적 영향은 무엇입니까? N이 백만이라고 가정합니다.
그런 다음 첫 번째 용어는 2 백만이고 두 번째 용어는 2입니다.
이러한 이유로 우리는 큰 N에 대해 가장 큰 항을 제외한 모든 항을 제거합니다.
이제 우리는에서 2N + 2
로 이동했습니다 2N
.
전통적으로 우리는 일정한 요소까지만 성능에 관심이 있습니다.
이것은 N이 클 때 성능에 일정한 배수 차이가 있는지 실제로 신경 쓰지 않는다는 것을 의미합니다. 어쨌든 2N의 단위는 처음부터 잘 정의되어 있지 않습니다. 따라서 가장 간단한 표현을 얻기 위해 상수 인자를 곱하거나 나눌 수 있습니다.
그래서 2N
그냥 N
됩니다.
답변
이것은 훌륭한 기사입니다 :
http://www.daniweb.com/software-development/computer-science/threads/13488/time-complexity-of-algorithm
아래 답변이 위에서 복사되었습니다 (우수한 링크가 파열 된 경우)
시간 복잡성을 계산하기위한 가장 일반적인 지표는 Big O 표기법입니다. 이렇게하면 N이 무한대에 가까워 질 때 N과 관련하여 실행 시간을 추정 할 수 있도록 모든 상수 요소가 제거됩니다. 일반적으로 다음과 같이 생각할 수 있습니다.
statement;
일정하다. 문의 실행 시간은 N과 관련하여 변경되지 않습니다.
for ( i = 0; i < N; i++ )
statement;
선형입니다. 루프의 작동 시간은 N에 정비례합니다. N이 두 배가되면 작동 시간도 증가합니다.
for ( i = 0; i < N; i++ ) {
for ( j = 0; j < N; j++ )
statement;
}
이차입니다. 두 루프의 실행 시간은 N의 제곱에 비례합니다. N이 두 배가되면 실행 시간이 N * N 증가합니다.
while ( low <= high ) {
mid = ( low + high ) / 2;
if ( target < list[mid] )
high = mid - 1;
else if ( target > list[mid] )
low = mid + 1;
else break;
}
로그입니다. 알고리즘의 실행 시간은 N을 2로 나눌 수있는 횟수에 비례합니다. 이는 알고리즘이 작업 영역을 각 반복마다 절반으로 나누기 때문입니다.
void quicksort ( int list[], int left, int right )
{
int pivot = partition ( list, left, right );
quicksort ( list, left, pivot - 1 );
quicksort ( list, pivot + 1, right );
}
N * log (N)입니다. 실행 시간은 로그인 N 루프 (반복 또는 재귀)로 구성되므로 알고리즘은 선형 및 로그의 조합입니다.
일반적으로 한 차원의 모든 항목으로 작업을 수행하는 것은 선형이고, 두 차원의 모든 항목으로 작업을 수행하는 것은 2 차적이며 작업 영역을 반으로 나누는 것이 로그입니다. 3 차, 지수 및 제곱근과 같은 다른 Big O 측정 값이 있지만 거의 공통적이지 않습니다. 큰 O 표기법은 측정 값이있는 O ( <type> )
위치 로 설명됩니다 <type>
. 퀵 정렬 알고리즘은로 설명됩니다 O ( N * log ( N ) )
.
이 중 어느 것도 최고, 평균 및 최악의 경우를 고려하지 않았습니다. 각각 고유 한 Big O 표기법이 있습니다. 또한 이것은 매우 간단한 설명입니다. Big O가 가장 일반적이지만 내가 보여준 것보다 더 복잡합니다. 큰 오메가, 작은 o 및 큰 세타와 같은 다른 표기법도 있습니다. 아마도 알고리즘 분석 과정 밖에서 그것들을 만나지 못할 것입니다. 😉
답변
여기에서 가져온 것- 알고리즘의 시간 복잡성 소개
1. 소개
컴퓨터 과학에서 알고리즘의 시간 복잡도는 알고리즘이 입력을 나타내는 문자열 길이의 함수로 실행하는 데 걸리는 시간을 수량화합니다.
2. 큰 O 표기법
알고리즘의 시간 복잡도는 일반적으로 계수 및 하위 차수를 제외하는 큰 O 표기법을 사용하여 표현됩니다. 이러한 방식으로 표현 될 때, 시간 복잡도는 무의식적으로, 즉 입력 크기가 무한대로 간다고한다.
예를 들어, 크기 n의 모든 입력에서 알고리즘에 필요한 시간이 최대 5n 3 + 3n 인 경우 점근 적 시간 복잡도는 O (n 3 )입니다. 나중에 더 자세히.
몇 가지 예 :
- 1 = O (n)
- n = O (n 2 )
- 로그 (n) = O (n)
- 2n + 1 = O (n)
3. O (1) 일정 시간 :
알고리즘은 입력 크기에 관계없이 동일한 시간이 필요한 경우 일정한 시간에 실행된다고합니다.
예 :
- 배열 : 모든 요소에 액세스
- 고정 크기 스택 : 푸시 및 팝 방법
- 고정 크기 대기열 : 대기열 및 대기열에서 제외 방법
4. O (n) 선형 시간
알고리즘은 시간 실행이 입력 크기에 직접 비례하는 경우 선형 시간으로 실행된다고합니다. 즉, 입력 크기가 증가함에 따라 시간이 선형으로 증가합니다.
다음 예제를 고려하십시오. 아래에서 요소를 선형으로 검색하는 경우 시간 복잡성이 O (n)입니다.
int find = 66;
var numbers = new int[] { 33, 435, 36, 37, 43, 45, 66, 656, 2232 };
for (int i = 0; i < numbers.Length - 1; i++)
{
if(find == numbers[i])
{
return;
}
}
더 많은 예 :
- 배열 : 선형 검색, 이송, 최소 찾기 등
- ArrayList : 메소드 포함
- 큐 : 메소드 포함
5. O (log n) 로그 시간 :
알고리즘의 시간 실행이 입력 크기의 로그에 비례하면 알고리즘은 로그 시간으로 실행됩니다.
예 : 이진 검색
“20 개의 질문”게임을 상기하십시오. 과제는 간격으로 숨겨진 숫자의 값을 추측하는 것입니다. 추측 할 때마다 추측이 너무 높거나 낮은 지 알려줍니다. 20 개의 질문 게임은 추측 횟수를 사용하여 구간 크기를 절반으로 줄이는 전략을 의미합니다. 이진 검색으로 알려진 일반적인 문제 해결 방법의 예입니다.
6. O (n 2 ) 2 차 시간
시간 실행이 입력 크기의 제곱에 비례하면 알고리즘은 2 차 시간으로 실행된다고합니다.
예 :
7. 유용한 링크들
답변
이 질문에 대한 좋은 답변이 있지만. 의 몇 가지 예와 함께 여기에 다른 답변을 드리겠습니다 loop
.
-
O (n) : 루프 변수가 일정량 증가 / 감소되면 루프의 시간 복잡도는 O (n) 으로 간주됩니다 . 예를 들어 다음 함수는 시간 복잡도 가 O (n) 입니다.
// Here c is a positive integer constant for (int i = 1; i <= n; i += c) { // some O(1) expressions } for (int i = n; i > 0; i -= c) { // some O(1) expressions }
-
O (n ^ c) : 중첩 루프의 시간 복잡도는 가장 안쪽 명령문이 실행 된 횟수와 같습니다. 예를 들어 다음 샘플 루프의 시간 복잡도 는 O (n ^ 2)입니다.
for (int i = 1; i <=n; i += c) { for (int j = 1; j <=n; j += c) { // some O(1) expressions } } for (int i = n; i > 0; i += c) { for (int j = i+1; j <=n; j += c) { // some O(1) expressions }
예를 들어 선택 정렬 및 삽입 정렬의 시간 복잡도 는 O (n ^ 2) 입니다.
-
루프 변수가 일정한 양으로 나누어 지거나 곱 하면 루프의 O (Logn) 시간 복잡도는 O (Logn) 로 간주됩니다 .
for (int i = 1; i <=n; i *= c) { // some O(1) expressions } for (int i = n; i > 0; i /= c) { // some O(1) expressions }
예를 들어 이진 검색에는 O (Logn) 시간 복잡성이 있습니다.
-
루프 변수가 일정한 양만큼 기하 급수적으로 감소 / 증가되는 경우 루프의 O (LogLogn) 시간 복잡도는 O (LogLogn) 로 간주됩니다 .
// Here c is a constant greater than 1 for (int i = 2; i <=n; i = pow(i, c)) { // some O(1) expressions } //Here fun is sqrt or cuberoot or any other constant root for (int i = n; i > 0; i = fun(i)) { // some O(1) expressions }
시간 복잡도 분석의 한 예
int fun(int n)
{
for (int i = 1; i <= n; i++)
{
for (int j = 1; j < n; j += i)
{
// Some O(1) task
}
}
}
분석 :
For i = 1, the inner loop is executed n times.
For i = 2, the inner loop is executed approximately n/2 times.
For i = 3, the inner loop is executed approximately n/3 times.
For i = 4, the inner loop is executed approximately n/4 times.
…………………………………………………….
For i = n, the inner loop is executed approximately n/n times.
따라서 위 알고리즘의 총 시간 복잡도 (n + n/2 + n/3 + … + n/n)
는n * (1/1 + 1/2 + 1/3 + … + 1/n)
시리즈에서 중요한 것은 O (Logn)(1/1 + 1/2 + 1/3 + … + 1/n)
과 같습니다 . 따라서 위 코드의 시간 복잡도는 O (nLogn) 입니다.
답변
예제의 시간 복잡성
1-기본 연산 (산술, 비교, 배열 요소 액세스, 할당) : 실행 시간은 항상 일정합니다 O (1)
예 :
read(x) // O(1)
a = 10; // O(1)
a = 1.000.000.000.000.000.000 // O(1)
2-then else statement : 둘 이상의 가능한 명령문에서 최대 실행 시간 만 가져옵니다.
예:
age = read(x) // (1+1) = 2
if age < 17 then begin // 1
status = "Not allowed!"; // 1
end else begin
status = "Welcome! Please come in"; // 1
visitors = visitors + 1; // 1+1 = 2
end;
따라서 위의 의사 코드의 복잡성은 T (n) = 2 + 1 + max (1, 1 + 2) = 6입니다. 따라서 큰 오는 여전히 상수 T (n) = O (1)입니다.
3-반복 (for, while, repeat) :이 명령문의 실행 시간은 반복 횟수에 해당 반복 내의 조작 수를 곱한 것입니다.
예:
total = 0; // 1
for i = 1 to n do begin // (1+1)*n = 2n
total = total + i; // (1+1)*n = 2n
end;
writeln(total); // 1
따라서 복잡도는 T (n) = 1 + 4n + 1 = 4n + 2입니다. 따라서 T (n) = O (n)입니다.
4-중첩 루프 (루핑 내부 루핑) : 기본 루핑 내부에 하나 이상의 루핑이 있으므로이 명령문의 실행 시간은 O (n ^ 2) 또는 O (n ^ 3)을 사용했습니다.
예:
for i = 1 to n do begin // (1+1)*n = 2n
for j = 1 to n do begin // (1+1)n*n = 2n^2
x = x + 1; // (1+1)n*n = 2n^2
print(x); // (n*n) = n^2
end;
end;
일반적인 러닝 타임
알고리즘을 분석 할 때 몇 가지 일반적인 실행 시간이 있습니다.
-
O (1) – 상수 시간 상수 시간은 실행 시간이 일정 함을 의미 하며 입력 크기의 영향을받지 않습니다 .
-
O (n) – 선형 시간 알고리즘이 n 개의 입력 크기를 허용하면 n 개의 연산도 수행합니다.
-
O (log n) – 실행 시간 O (log n)이있는 로그 시간 알고리즘이 O (n)보다 약간 빠릅니다. 일반적으로 알고리즘은 문제를 동일한 크기의 하위 문제로 나눕니다. 예 : 이진 검색 알고리즘, 이진 변환 알고리즘
-
O (n log n) – Linearithmic Time이 실행 시간은 종종 문제를 하위 문제로 재귀 적으로 나누고 n 번으로 병합하는 “분할 및 정복 알고리즘”에서 찾을 수 있습니다. 예 : 병합 정렬 알고리즘
-
O (n 2 ) – 2 차 시간 모양 버블 정렬 알고리즘!
-
O (n 3 ) – 입방 시간 O (n 2 ) 와 동일한 원리를 갖습니다 .
-
O (2 n ) – 지수 시간 n이 1000.000이면 입력이 커질수록 T (n)은 21000.000이됩니다. Brute Force 알고리즘에는이 실행 시간이 있습니다.
-
O (n!) – 팩토리얼 타임 가장 느린 !!! 예 : 출장 판매원 문제 (TSP)
답변
코드를 분석 할 때는 모든 작업 / 인식 시간 복잡성을 계산하여 한 줄씩 분석해야하며, 결국 전체 그림을 얻기 위해 코드를 합산해야합니다.
예를 들어 선형 복잡도를 가진 간단한 루프를 하나 만들 수 있지만 나중에 같은 프로그램에서 3 차 루프를 갖는 3 차 루프를 가질 수 있으므로 프로그램은 3 차 복잡성 을 갖습니다 . 성장의 기능 순서가 바로 여기에 있습니다.
알고리즘의 시간 복잡성에 대한 가능성을 살펴보면 위에서 언급 한 성장 순서를 볼 수 있습니다.
-
일정 시간이 성장의 순서가
1
예를 들어, :a = b + c
. -
대수 시간 은 성장 순서가
LogN
있습니다. 일반적으로 무언가를 반으로 나누거나 (이진 검색, 나무, 심지어 루프) 같은 방식으로 무언가를 곱할 때 발생합니다. -
선형 , 성장 순서는
N
예를 들어int p = 0; for (int i = 1; i < N; i++) p = p + 2;
-
Linearithmic , 성장 순서는
n*logN
일반적으로 나누기 및 정복 알고리즘에서 발생합니다. -
입방 , 성장 순서
N^3
, 고전적인 예는 모든 트리플렛을 확인하는 트리플 루프입니다.int x = 0; for (int i = 0; i < N; i++) for (int j = 0; j < N; j++) for (int k = 0; k < N; k++) x = x + 2
-
지수 , 성장 순서는
2^N
일반적으로 전체 세트를 수행 할 때 발생합니다 (예 : 일부 세트의 서브 세트 확인).
답변
느슨하게 말하면, 시간 복잡도는 입력 크기가 증가함에 따라 알고리즘의 작업 수 또는 런타임이 어떻게 증가 하는지를 요약하는 방법입니다.
인생의 대부분의 것들과 마찬가지로, 칵테일 파티는 우리가 이해하는 데 도움이 될 수 있습니다.
의 위에)
파티에 도착하면 모든 사람의 손을 흔들어야합니다 (모든 항목에 대해 작업 수행). 참석자의 수가 N
증가함에 따라 모든 사람의 손을 흔드는 데 걸리는 시간 / 작업이로 증가합니다 O(N)
.
왜 O(N)
그렇지 cN
않습니까?
사람들과 악수하는 데 걸리는 시간에는 차이가 있습니다. 이것을 평균화하여 상수로 캡처 할 수 c
있습니다. 그러나 여기서 모든 사람과 악수하는 기본적인 작업 은 O(N)
무엇이든간에 항상 비례합니다 c
. 우리가 칵테일 파티에 가야하는지에 대해 토론 할 때, 우리는 종종 그 모임이 어떻게 보이는지에 대한 세부적인 내용보다 모든 사람을 만나야한다는 사실에 더 관심이 있습니다.
O (N ^ 2)
칵테일 파티의 호스트는 모든 사람들이 다른 사람들을 만나는 어리석은 게임을 원합니다. 따라서 N-1
다른 사람을 만나야 합니다. 다음 사람이 이미 당신을 만났기 N-2
때문에 사람들을 만나야합니다 . 이 시리즈의 합은입니다 x^2/2+x/2
. 참석자의 수가 증가함에 따라이 x^2
용어는 빠르게 커지 므로 다른 모든 것을 삭제합니다.
O (N ^ 3)
다른 모든 사람을 만나야하며, 각 회의 중에는 회의실에있는 다른 모든 사람에 대해 이야기해야합니다.
O (1)
호스트는 무언가를 발표하려고합니다. 그들은 와인 글라스를 딩하고 큰 소리로 말합니다. 모두가들을 수 있습니다. 참석자가 몇 명인지는 중요하지 않습니다.이 작업은 항상 같은 시간이 걸립니다.
O (로그 N)
호스트는 모든 사람을 알파벳 순서로 테이블에 배치했습니다. 댄은 어디에 있습니까? 당신은 그가 아담과 맨디 사이에 있어야한다고 생각합니다 (분명히 맨디와 잭 사이는 아닙니다!). 그는 조지와 맨디 사이에 있습니까? 아담과 프레드 사이, 신디와 프레드 사이에 있어야합니다. 그리고 … 우리는 세트의 절반과 그 세트의 절반을 보면서 Dan을 효율적으로 찾을 수 있습니다. 궁극적으로 O (log_2 N) 개인을 봅니다.
O (N 로그 N)
위의 알고리즘을 사용하여 테이블에서 앉을 위치를 찾을 수 있습니다. 많은 사람들이 한 번에 하나씩 테이블에 와서 모두이 작업을 수행하면 O (N log N) 시간이 걸립니다. 항목을 비교해야 할 때 모음을 정렬하는 데 걸리는 시간입니다.
최고 / 최악의 사례
당신은 파티에 도착하고 이니 고를 찾아야합니다-얼마나 오래 걸립니까? 도착 시간에 따라 다릅니다. 모두가 밀링 작업을하는 경우 최악의 상황을 경험 한 경우 O(N)
시간 이 걸립니다 . 그러나 모든 사람이 테이블에 앉으면 O(log N)
시간 이 걸립니다 . 또는 호스트의 와인 글라스 소리를 활용할 수 있으며 O(1)
시간 이 걸립니다 .
호스트를 사용할 수 없다고 가정하면 Inigo 찾기 알고리즘 에 도착한 당사자의 상태에 따라 O(log N)
하한과 상한 이 있다고 말할 수 있습니다 O(N)
.
우주 및 커뮤니케이션
알고리즘이 공간 또는 통신을 사용하는 방법을 이해하는 데 동일한 아이디어를 적용 할 수 있습니다.
Knuth는 전자의 “곡의 복잡성” 에 관한 멋진 논문을 썼습니다 .
정리 2 : 복잡도 O (1)의 노래는 임의로 긴 곡이 있습니다.
증거 : (케이시와 선샤인 밴드로 인해). (15)에 의해 정의 된 노래 Sk를 고려하지만
V_k = 'That's the way,' U 'I like it, ' U
U = 'uh huh,' 'uh huh'
모든 k.