파워 프로그래밍 : O (1 ^ N), O (N ^ 1), O (2 ^ N), O (N ^ 2) 모두 하나로


65

실행 방법에 따라 4 가지 큰 O 시간 복잡성 을 나타내는 프로그램 (또는 기능)을 작성하십시오 . 어떤 형태로든 양의 정수 N을 취하는데, 이는 당신이 2 31 미만이라고 가정 할 수 있습니다 .

  1. 프로그램이 원래 형식으로 실행될 때 지속적으로 복잡 해야합니다 . 즉, 복잡도는 Θ (1) 또는 동등하게 Θ (1 ^ N) 이어야합니다 .

  2. 프로그램을 되돌리고 실행하면 선형 복잡성 이 있어야합니다 . 즉, 복잡도는 Θ (N) 또는 동등하게 Θ (N ^ 1) 이어야합니다 .
    ( 반전 되므로 이치 N^11^N맞습니다.)

  3. 프로그램이 두 배가 될 때 , 즉 그 자체로 연결되고 실행될 때, 프로그램은 지수 적으로 복잡 해야하며 , 특히 2 N 입니다. 즉, 복잡도는 Θ (2 ^ N) 이어야합니다 .
    ( 2in 2^N은 in의 두 배 이므로 의미 11^N있습니다.)

  4. 프로그램되면 반전 하고 있어야 실행 다항식 구체적 복잡성 N 2 . 즉, 복잡도는 Θ (N ^ 2) 이어야합니다 .
    ( 반전 되므로 이치 N^22^N맞습니다.)

이 네 가지 경우 만 처리해야합니다.

프로그램의 런타임이 필요한 복잡성에 따라 위와 아래에 모두 묶여 있어야하기 때문에 정확성을 위해 big O 대신 big theta (Θ) 표기법을 사용 하고 있습니다. 그렇지 않으면 O (1)로 함수를 작성하는 것만으로도 4 가지 점을 모두 충족시킬 수 있습니다. 뉘앙스를 이해하는 것은 너무 중요하지 않습니다. 주로 프로그램이 일부 상수 k에 대해 k * f (N) 연산을 수행하는 경우 Θ (f (N))에있을 수 있습니다.

원래 프로그램이

ABCDE

그런 다음 일정한 시간이 걸립니다. 즉, 입력 N이 1 또는 2147483647 (2 31 -1)이거나 그 사이의 값이든, 거의 같은 시간 내에 종료되어야합니다.

프로그램의 역 버전

EDCBA

즉, 종료하는 데 걸리는 시간은 N에 대략 비례해야합니다. 따라서 N = 1은 가장 짧은 시간이 걸리고 N = 2147483647이 가장 많이 걸립니다.

프로그램의 두 배 버전

ABCDEABCDE

N의 관점에서 2 대 N 시간이 필요합니다. 즉, 종료하는 데 걸리는 시간은 대략 2 N에 비례해야합니다 . 따라서 N = 1이 약 1 초 안에 종료되면 N = 60은 우주의 나이보다 오래 걸릴 것입니다. (아니, 테스트 할 필요는 없습니다.)

프로그램의 이중 및 역 버전

EDCBAEDCBA

즉, N의 관점에서 제곱 시간을 가져야합니다. 즉, 종료하는 데 걸리는 시간은 N * N에 대략 비례해야합니다. 따라서 N = 1이 약 1 초 안에 종료되면 N = 60은 종료하는 데 약 1 시간이 걸립니다.

세부

  • 프로그램이 복잡하다고 생각하는 것을 보여 주거나 논쟁해야합니다. 타이밍 데이터를 제공하는 것은 좋은 생각이지만 이론적으로 왜 복잡성이 올바른지 설명하려고 노력하십시오.

  • 실제로 프로그램에 걸리는 시간이 복잡성을 완벽하게 대표하지 않거나 결정 론적이라면 괜찮습니다. 예를 들어, 입력 N + 1은 때때로 N보다 빠르게 실행될 수 있습니다.

  • 프로그램을 실행하는 환경 중요합니다. 인기있는 언어가 알고리즘에서 의도적으로 시간을 낭비하지 않는 방법에 대한 기본 가정을 할 수 있지만, 예를 들어 특정 버전의 Java 가 빠른 정렬 알고리즘 대신 버블 정렬을 구현 하는 것을 알고 있다면 정렬을 수행하는 경우이를 고려해야합니다 .

  • 여기서 모든 복잡성에 대해 최선의 경우 나 평균적인 경우가 아니라 최악의 시나리오 에 대해 이야기하고 있다고 가정 합니다.

  • 프로그램의 공간 복잡성은 중요하지 않으며 시간 복잡성 만 중요합니다.

  • 프로그램은 무엇이든 출력 할 수 있습니다. 양의 정수 N을 취하고 정확한 시간 복잡성을 갖는 것만 중요합니다.

  • 의견과 여러 줄 프로그램이 허용됩니다. (당신은 가정 할 수 \r\n반전이다 \r\n윈도우의 호환성을 위해.)

큰 O 알림

가장 빠른 O(1), O(N), O(N^2), O(2^N)것부터 가장 느린 것까지 (위의 1, 2, 4, 3 순서).

느린 용어가 항상 지배적입니다 (예 :) O(2^N + N^2 + N) = O(2^N).

O(k*f(N)) = O(f(N))상수 k. 그래서 O(2) = O(30) = O(1)O(2*N) = O(0.1*N) = O(N).

기억 O(N^2) != O(N^3)하고 O(2^N) != O(3^N).

깔끔한 큰 O 치트 시트.

채점

이것은 정상적인 코드 골프입니다. 바이트 단위의 가장 짧은 원래 프로그램 (상수 시간 1)이 이깁니다.


훌륭한 질문입니다! 사소한 점 : 크기 N으로 계산되는 유일한 입력은 숫자 N 자체이므로 (BTW는 일반적인 입력 크기 개념이 아니기 때문에 최악의 경우 / 최상의 경우 / 평균 경우를 지정할 필요가 없습니다. 입력 N을 크기 로그 N의 크기로 취급하십시오. 따라서 각 N에 대해 하나의 사례 만 있으며 이는 동시에 최고, 최악 및 평균 사례입니다.
ShreevatsaR

5
일반적인 복잡성 정의에서 벗어난 것으로 보입니다. 우리는 항상 알고리즘의 시간 복잡성을 입력 크기의 함수로 정의합니다 . 입력 값이 숫자 인 경우 입력 크기는 해당 숫자의 밑이 2 인 로그입니다. 따라서 프로그램 n = input(); for i in xrange(n): pass은 입력 크기가 2 ** k어디 k = log_2(n)인지 단계를 거치기 때문에 기하 급수적으로 복잡 합니다. 요구 사항이 급격히 변하기 때문에 이것이 사실인지 명확히해야합니다.
wchargin

답변:


36

파이썬 3 , 102 바이트

try:l=eval(input());k=1#)]0[*k**l(tnirp
except:k=2#2=k:tpecxe
print(k**l*[0])#1=k;))(tupni(lave=l:yrt

온라인으로 사용해보십시오!

이것은 프로그램이 수행하는 것이므로 O (1 ^ n)입니다.

  • 입력을 평가하다
  • 배열을 생성한다 [0]
  • 그것을 인쇄

역전 :


try:l=eval(input());k=1#)]0[*l**k(tnirp
except:k=2#2=k:tpecxe
print(l**k*[0])#1=k;))(tupni(lave=l:yrt

온라인으로 사용해보십시오!

이것은 프로그램이 수행하는 것이므로 O (n ^ 1)입니다.

  • 입력을 평가하다
  • 배열 [0] * input을 작성하십시오 (0은 입력 횟수만큼 반복됨)
  • 그것을 인쇄

배가 :

try:l=eval(input());k=1#)]0[*k**l(tnirp
except:k=2#2=k:tpecxe
print(k**l*[0])#1=k;))(tupni(lave=l:yrt
try:l=eval(input());k=1#)]0[*k**l(tnirp
except:k=2#2=k:tpecxe
print(k**l*[0])#1=k;))(tupni(lave=l:yrt

온라인으로 사용해보십시오!

이것은 프로그램이 수행하는 것이므로 O (2 ^ n)입니다.

  • 입력을 평가하다
  • 배열을 생성한다 [0]
  • 그것을 인쇄
  • 입력을 평가하려고
  • 불합격
  • [0] * (2 ^ input) 배열을 만듭니다 (0은 2 ^ input만큼 여러 번 반복됨)
  • 그것을 인쇄

배가되고 반전 :


try:l=eval(input());k=1#)]0[*l**k(tnirp
except:k=2#2=k:tpecxe
print(l**k*[0])#1=k;))(tupni(lave=l:yrt
try:l=eval(input());k=1#)]0[*l**k(tnirp
except:k=2#2=k:tpecxe
print(l**k*[0])#1=k;))(tupni(lave=l:yrt

온라인으로 사용해보십시오!

이것은 프로그램이 수행하는 것이므로 O (n ^ 2)입니다.

  • 입력을 평가하다
  • 배열 [0] * input을 작성하십시오 (0은 입력 횟수만큼 반복됨)
  • 그것을 인쇄
  • 입력을 평가하려고
  • 불합격
  • 배열을 만듭니다 [0] * (input ^ 2) (0은 입력의 제곱만큼 반복됩니다)
  • 그것을 인쇄

호출이 여러 번있을 때 사용자 상호 작용을 기다리는 것이 왜 멈추지 input()않습니까?
Jonathan Allan

1
전송 끝에서 "전송 끝"이 전송되는 허점이 있습니까?
Leaky Nun

1
설명 할 수 있습니까?
Brain Guider

1
다시 : "파일 끝"인수, 당신은 일을 거꾸로보고 있습니다. 터미널을 사용하는 경우 연결된 무언가가 있기 때문에 입력 중단을 요청합니다. ctrl-D는 명시 적으로 입력을 보내지 않도록 보낼 수 있습니다. 입력이 빈 파일에 연결되어 있거나 (입력 상자를 비워두면 TIO처럼) 모든 입력을 읽은 파일에 연결되어있는 경우 입력을 요청하지 않아도됩니다. 입력이 없다는 것을 이미 알고 있습니다. UNIX / Linux에서 일반 파일에서는 "파일 끝"과 "입력 가능하지 않음"을 구분할 수 없습니다.

3
첫 번째 경우, k입력은 입력 l중 하나이므로 여전히 컴퓨팅 1**k하고 있습니까? O(log(k))당신과 나와 모두가 그것이 하나라는 것을 미리 알고 있다는 사실에도 불구하고 어느 것이 시간 이 걸리 나요?
Richard Rast

18

Perl 5, 82 73 71 + 1 (-n 플래그 용) = 72 바이트

나는 이것이 (많은) 더 골프를 칠 수 있다고 확신하지만, 취침 시간이며 그대로 디버깅하는 데 충분한 시간을 보냈으며 지금까지 내가 가진 것을 자랑스럽게 생각합니다.

#x$=_$;
$x.=q;#say for(1..2**$_)#)_$..1(rof _$=+x$;;
eval $x;$x=~s/#//;

프로그램 자체는 입력을 사용하지 않고 주석으로 시작하는 문자열을 평가 한 다음 단일 문자열 대체를 수행하므로 분명히 일정한 시간에 있습니다. 기본적으로 다음과 같습니다.

$x="#";
eval $x;
$x=~s/#//;

배가 :

#x$=_$;
$x.=q;#say for(1..2**$_)#)_$..1(rof _$=+x$;;
eval $x;$x=~s/#//;
#x$=_$;
$x.=q;#say for(1..2**$_)#)_$..1(rof _$=+x$;;
eval $x;$x=~s/#//;

실제로 지수 시간이 걸리는 비트는 두 번째 평가입니다.이 명령 say for(1..2**$_)은 1에서 2 ^ N까지의 모든 숫자를 나열 하는 명령을 평가하여 지수 시간이 분명히 걸립니다.

역전 :

;//#/s~=x$;x$ lave
;;$x+=$_ for(1..$_)#)_$**2..1(rof yas#;q=.x$
;$_=$x#

이것은 (순진하게) 입력의 합산을 계산하며, 선형 시간이 명확하게 걸립니다 (각 추가가 일정한 시간에 있기 때문에). 실제로 실행되는 코드는 다음과 같습니다.

$x+=$_ for(1..$_);
$_=$x;

마지막 줄은 이러한 명령이 반복 될 때 2 차 시간이 걸리는 것입니다.

반전 및 배가 :

;//#/s~=x$;x$ lave
;;$x+=$_ for(1..$_)#)_$**2..1(rof yas#;q=.x$
;$_=$x#
;//#/s~=x$;x$ lave
;;$x+=$_ for(1..$_)#)_$**2..1(rof yas#;q=.x$
;$_=$x#

이것은 이제 입력의 합의 합을 취합니다 (그리고 입력의 합에 합산합니다). N^2덧셈을 하기 때문에 이차 시간이 걸린다. 기본적으로 다음과 같습니다.

$x=0;
$x+=$_ for(1..$_);
$_=$x;
$x+=$_ for(1..$_);
$_=$x;

두번째 라인은 고 입력의 가산 발견 N네번째는 않지만, 추가 사항 summation(N)인 추가 사항 O(N^2).


큰! 주류 언어로하는 것은 힘들었을 것입니다! 이것은 나의 공감대가 있습니다!
Arjun

잘 했어, 이것은 꽤 좋다. 당신은 아마 의미 $x.=q;##say...대신에 $x.=q;#say...(두 사람과 함께하지만 #대신에 1). (왜 75 대신 76 바이트를 세는지 설명 할 것입니다). 또한 -n프로그램이 없으면 많은 것을하지 않기 때문에 바이트 수로 플래그를 계산해야 합니다.
Dada

@Dada 실수로 evals///명령을 바꿨습니다 . 당신이 eval첫 번째 를 수행 하면 하나만 필요합니다 #. 잘 잡아!
Chris

@Chris 맞아, 그것은 실제로 작동합니다. 당신은 마지막 생략 할 수 있습니다 #: $x=~s/#//;생산 반전 ;//#/s~=x$은 선두와 마찬가지로 귀하의 상황에서, 아무것도 안하는 #. (하지만 테스트하지는 않았습니다). 어쨌든 +1하십시오!
Dada

@Dada 니스 다시 한번 잡아라!
Chris

17

실제로 20 바이트

";(1╖╜ⁿr"ƒ"rⁿ@╜╖1(;"

온라인으로 사용해보십시오!

입력: 5

산출:

rⁿ@╜╖1(;
[0]
5

역전 :

";(1╖╜@ⁿr"ƒ"rⁿ╜╖1(;"

온라인으로 사용해보십시오!

산출:

rⁿ╜╖1(;
[0, 1, 2, 3, 4]
5

배가 :

";(1╖╜ⁿr"ƒ"rⁿ@╜╖1(;"";(1╖╜ⁿr"ƒ"rⁿ@╜╖1(;"

온라인으로 사용해보십시오!

산출:

rⁿ@╜╖1(;
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31]
rⁿ@╜╖1(;
rⁿ@╜╖1(;
[0]

배가되고 반전 :

";(1╖╜@ⁿr"ƒ"rⁿ╜╖1(;"";(1╖╜@ⁿr"ƒ"rⁿ╜╖1(;"

온라인으로 사용해보십시오!

산출:

rⁿ╜╖1(;
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24]
rⁿ╜╖1(;
rⁿ╜╖1(;
[0, 1, 2, 3, 4]

주요 아이디어

실제로 스택 기반 언어입니다.

  • abcO (1 n ) 복잡도 를 갖는 프로그램 이며, 이중은 O (2 n ) 복잡도를 갖습니다 .
  • defO (n 1 ) 복잡도 를 갖는 프로그램 이고, 이중은 O (n 2 ) 복잡도 를 갖는 프로그램입니다 .

그런 다음 내 제출 "abc"ƒ"fed", 어디 ƒ평가입니다. 그렇게하면 "fed"평가되지 않습니다.

개별 프로그램 생성

첫 번째 구성 요소의 의사 코드 ;(1╖╜ⁿr:

register += 1 # register is default 0
print(range(register**input))

두 번째 구성 요소의 의사 코드 ;(1╖╜ⁿ@r:

register += 1 # register is default 0
print(range(input**register))

나는 이것이 가능할 것이라고 생각하지 않았다! 대단한 일입니다! +1
Arjun

@Arjun 감사합니다.
Leaky Nun

이것은 우수하고 논평 IMO를 사용하지 않음으로써 진정으로 도전을 제기합니다. 대박!
ShreevatsaR

1
글쎄, 이것은 실제로 의견이 있습니다 ... 문자열이 평가되지 않고 NOP입니다 ...
Leaky Nun

4

젤리 , 20 바이트

Leaky Nun의 Actually 솔루션에서 부분적으로 영감을 얻었습니다 .

선행 및 후행 줄 바꿈이 중요합니다.

표준:


⁵Ŀ⁵
R
R²
2*R
‘
⁵Ŀ⁵

온라인으로 사용해보십시오!

입력: 5

산출:

610

역전 :


⁵Ŀ⁵
‘
R*2
²R
R
⁵Ŀ⁵

온라인으로 사용해보십시오!

입력: 5

산출:

[1, 2, 3, 4, 5]10

배가


⁵Ŀ⁵
R
R²
2*R
‘
⁵Ŀ⁵

⁵Ŀ⁵
R
R²
2*R
‘
⁵Ŀ⁵

온라인으로 사용해보십시오!

입력: 5

산출:

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32]10

배가 및 역전


⁵Ŀ⁵
‘
R*2
²R
R
⁵Ŀ⁵

⁵Ŀ⁵
‘
R*2
²R
R
⁵Ŀ⁵

온라인으로 사용해보십시오!

입력: 5

산출:

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25]10

설명

여기에있는 키 Ŀ는 "인덱스 n의 링크를 모나드로 호출합니다"를 의미합니다. 링크는 메인 링크 (가장 아래 링크)를 제외하고 1부터 시작하여 위에서 아래로 색인됩니다. Ŀ또한 모듈 식이므로 5 개의 링크 중 7 번 링크를 호출하려고하면 실제로 링크 2를 호출하게됩니다.

이 프로그램에서 호출되는 링크는 프로그램의 버전에 관계없이 항상 인덱스 10 ( )에 있는 링크 입니다. 그러나 인덱스 10에있는 링크는 버전에 따라 다릅니다.

각 후 나타납니다이 Ŀ반전 할 때이 중단되지 않도록있다. 이전에 숫자가 없으면 프로그램이 구문 분석시 오류가 발생합니다 Ŀ. 갖는 그 후 그냥 바로 출력으로 이동 장소 nilad의 부족이다.

원래 버전 은 n + 1을 계산 하는 링크를 호출합니다 .

역전 된 버전 은 링크를 호출 R하여 범위 1을 생성합니다. n.

배가 된 버전 은 링크를 호출하여 2*R2 n 을 계산 하고 1 .. 2 n 범위를 생성합니다 .

이중 및 역방향 버전 은 링크를 호출하여 ²Rn 2 를 계산 하고 1 .. n 2 범위를 생성합니다 .


4

Befunge-98 , 50 바이트

표준

\+]#:\-1vk  !:;#
@k!:-1 .: ;#]#$ <;
[*:>@ 
&$< ^&;

이것은 지금까지 4 중에서 가장 간단한 프로그램입니다. 실행되는 유일한 명령은 다음과 같습니다.

\+]
  !
  : @
&$< ^&;

이 프로그램은 "오른쪽으로 돌리기"명령 ( ])과 화살표를 누르기 전에 관련이없는 작업을 수행합니다 . 그런 다음 2 개의 "입력 입력"명령을 수행합니다. 입력에 숫자가 1 개 뿐이고 TIO가 &s를 처리하는 방식으로 인해 프로그램은 60 초 후에 종료됩니다. 2 개의 입력이 있고 (바이트를 추가 할 수 없기 때문에) IP는 "프로그램 종료"기능으로 이동합니다.

온라인으로 사용해보십시오!

반전

;&^ <$&
 @>:*[
;< $#]#; :. 1-:!k@
#;:!  kv1-\:#]+\

이것은 조금 더 복잡합니다. 관련 명령은 다음과 같습니다.

;&^  $
  >:*[
;< $#]#; :. 1-:!k@
  :

어느 것이

;&^                   Takes input and sends the IP up. the ; is a no-op
  :                   Duplicates the input.
  >:*[                Duplicates and multiplies, so that the stack is [N, N^2
     $                Drops the top of the stack, so that the top is N
     ]#;              Turns right, into the loop
         :.           Prints, because we have space and it's nice to do
            1-        Subtracts 1 from N
              :!k@    If (N=0), end the program. This is repeated until N=0
;< $#]#;              This bit is skipped on a loop because of the ;s, which comment out things

여기서 중요한 부분은 :. 1-:!k@조금입니다. 더 낮은 시간의 복잡성에서 실행하기 전에 올바른 복잡성을 스택으로 푸시하는 한 원하는 것을 얻을 수 있기 때문에 유용합니다. 이 방법으로 나머지 2 개의 프로그램에서 사용됩니다.

온라인으로 사용해보십시오!

배가

\+]#:\-1vk  !:;#
@k!:-1 .: ;#]#$ <;
[*:>@ 
&$< ^&;\+]#:\-1vk  !:;#
@k!:-1 .: ;#]#$ <;
[*:>@ 
&$< ^&;

관련 명령은 다음과 같습니다.

\+]
  !
  :
&$< ^&;\+]#:\-1vk  !:;#
@k!:-1 .: ;#]#$ <;

이 프로그램은 2 개의 루프로 진행됩니다. 첫째, 일반 프로그램과 동일한 경로를 따라 가며 1과 N을 스택으로 푸시하지만 두 번째로 줄 바꿈하는 대신 &IP는 주석을 통해 루프로 넘어갑니다 2^N.

        vk!:    If N is 0, go to the next loop.
      -1        Subtract 1 from N
 +  :\          Pulls the 1 up to the top of the stack and doubles it
  ]#            A no-op
\               Pulls N-1 to the top again

라인 4의 다른 비트는 ;s를 사용하여 건너 뜁니다.

(2 ^ N)을 스택에 밀어 넣은 후에는 위에서 언급 한 인쇄 루프로 줄을 이동합니다. 첫 번째 루프 때문에 시간 복잡도는 Θ (N + 2 ^ N)이지만 Θ (2 ^ N)로 줄어 듭니다.

온라인으로 사용해보십시오!

배가 및 역전

;&^ <$&
 @>:*[
;< $#]#; :. 1-:!k@
#;:!  kv1-\:#]+\;&^ <$&
 @>:*[
;< $#]#; :. 1-:!k@
#;:!  kv1-\:#]+\

관련 명령 :

;&^

;< $#]#; :. 1-:!k@

 @>:*[

  :

이 기능은 반전 된 프로그램과 거의 동일하게 작동하지만 프로그램 N^2의 두 번째 복사본의 첫 번째 줄이 첫 번째 줄의 마지막 줄에 추가되므로 drop 명령 ( $)이 절대 실행되지 않기 때문에 스택에서 튀어 나오지 않습니다. N^2스택의 맨 위에 있는 인쇄 루프로 이동 합니다.

온라인으로 사용해보십시오!

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