パワープログラミング:O(1 ^ N)、O(N ^ 1)、O(2 ^ N)、O(N ^ 2)をすべて1つに


65

実行方法に応じて、4つの一般的な大きなO 時間の複雑さを示すプログラム(または関数)を作成します。どのような形式でも、正の整数Nを取りますが、これは2 31より小さいと仮定できます。

  1. プログラムを元の形式で実行する場合、一定の複雑さが必要です。つまり、複雑度はΘ(1)または同等にΘ(1 ^ N)でなければなりません。

  2. プログラムを反転して実行すると、線形の複雑さが生じるはずです。つまり、複雑さはΘ(N)または同等にΘ(N ^ 1)でなければなりません。
    (これN^11^N逆になっているので理にかなっています。)

  3. プログラムが2倍になった場合、つまり、プログラム自体に連結されて実行される場合、指数関数的な複雑さ、特に2 Nが必要です。つまり、複雑さはΘ(2 ^ N)でなければなりません。
    (ので、これは理にかなっている2では2^N二重のある11^N)。

  4. プログラムを2倍にして反転して実行すると、多項式の複雑さ、特にN 2が必要になります。つまり、複雑さはΘ(N ^ 2)でなければなりません。
    (これN^22^N逆になっているので理にかなっています。)

これらの4つのケースは、処理する必要がある唯一のケースです。

プログラムの実行時間は必要な複雑さによって上下に制限される必要があるため、正確さのために、大きなOではなく大きなシータ(Θ)表記を使用していることに注意してください。それ以外の場合、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 = 1が約1秒で終了する場合、N = 60は終了するのに約1時間かかります。

詳細

  • あなたは、あなたが言っている複雑さの中でプログラムが実行されていることを示すか、議論する必要があります。いくつかのタイミングデータを提供することは良い考えですが、理論的に複雑さが正しい理由を説明しようとします。

  • 実際にあなたのプログラムにかかる時間が複雑さを完全に表していない(あるいは決定論的でさえある)のであれば問題ありません。たとえば、入力N + 1はNよりも高速に実行される場合があります。

  • プログラムを実行している環境重要です。人気のある言語がアルゴリズムで意図的に時間を浪費することは決してないという基本的な仮定を立てることができますが、たとえば、特定のバージョンのJava がより高速なソートアルゴリズムの代わりにバブルソートを実装していることがわかっている場合、ソートを行う場合はそれを考慮する必要があります。

  • ここでのすべての複雑さについて、ベストケースや平均ケースではなく、最悪ケースのシナリオについて話していると仮定します。

  • プログラムのスペースの複雑さは重要ではなく、時間の複雑さだけが重要です。

  • プログラムは何でも出力できます。彼らが正の整数Nを取り、正しい時間の複雑さを持っていることが重要です。

  • コメントと複数行のプログラムが許可されています。(\r\n逆になっているの\r\nはWindowsとの互換性であると思われるかもしれません。)

ビッグ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チートシート。

得点

これは通常のコードゴルフです。バイト単位の最短オリジナルプログラム(一定時間のもの)が勝ちます。


素晴らしい質問です!軽微な点:ワーストケース/ベストケース/アベレージケースを指定する必要はありません。サイズNとしてカウントされる入力は数値Nだけであるため(BTWは入力サイズの通常の概念ではありません。入力Nをサイズlog(N)として扱います。したがって、Nごとに1つのケースのみが存在し、これは同時に最高、最悪、および平均のケースです。
-ShreevatsaR

5
複雑さの通常の定義から逸脱しているようです。私たちは常に入力のサイズの機能としてアルゴリズムの時間複雑さを定義します。入力が数値の場合、入力のサイズはその数値の2を底とする対数です。したがって、プログラムn = input(); for i in xrange(n): passは指数関数的に複雑になります。これは、入力サイズ2 ** kがどこにk = log_2(n)あるかによってステップがとられるためです。要件が劇的に変わるため、これが当てはまるかどうかを明確にする必要があります。
wchargin

回答:


36

Python 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は入力の2乗と同じ回数繰り返されます)
  • 印刷する

への複数の呼び出しがあるときに、ユーザーの操作を待ってハングアップしないのはなぜinput()ですか?
ジョナサンアラン

1
「送信終了」が送信終了時に送信されるのは抜け穴ですか?
リーキー修道女

1
説明できますか?
ブレインガイダー

1
再:「ファイルの終わり」引数、あなたは物事を後方に見ています。ターミナルを使用している場合、それに接続されているものがあるため、入力要求がハングします。ctrl-Dを送信すると、入力を明示的に送信できません。入力が空のファイルに接続されている場合(入力ボックスを空のままにした場合のTIOのように)、またはすべての入力が読み取られたファイルに接続されている場合は、入力を要求する必要はありません。入力がないことは既にわかっています。UNIX / Linuxでは、「ファイルの終わり」と「利用可能な入力なし」は通常のファイルでは区別できません。

3
前者の場合、k入力lは1であるため1**k、まだ計算していますよね?O(log(k))あなたと私と誰もがそれが1つであることを事前に知っているという事実にもかかわらず、どれが時間がかかりますか?
リチャードラスト

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/#//;

実際に指数時間がかかるビットは2番目のevalです。コマンドを評価し、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追加を行うため、これには2次時間がかかります。基本的に次と同等です:

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

2行目は入力の合計を見つけて加算Nし、4行目はsummation(N)加算を行いますO(N^2)


すばらしいです!主流の言語でそれを行うのは大変だっただろう!これは私の賛成です!
アルジュン

よくやった、これはかなりいいです。おそらく、$x.=q;##say...代わりに$x.=q;#say...#1の代わりに2つ)のつもりでした。(それが、75バイトではなく76バイトをカウントした理由を説明します)。また、-nバイトカウントでフラグをカウントする必要があります。これは、プログラムがそれなしではあまり実行しないためです。
ダダ

@Dada私は誤ってevals///コマンドを入れ替えました。eval最初に行う場合は、1つだけが必要#です。良いキャッチ!
クリス

@クリス右、それは確かに動作します。あなたは、コンテキストで何もしない、最後の#$x=~s/#//;reverseproduces を省略することができるかもしれ;//#/s~=x$ません#。(私はそれをテストしていません)。とにかく、+ 1を持っている!
ダダ

@Dada Niceもう一度キャッチ!
クリス

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)の複雑さを持つプログラムで、そのdoubleはO(2 n)の複雑さを持ちます。
  • defO(n 1)の複雑さを持つプログラムで、そのdoubleはO(n 2)の複雑さを持ちます。

それから、私の提出は"abc"ƒ"fed"、ここƒで評価されます。そのように、"fed"評価されません。

個別プログラムの生成

最初のコンポーネントの擬似コード;(1╖╜ⁿr

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

2番目のコンポーネントの擬似コード;(1╖╜ⁿ@r

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

これが可能になるとは思っていませんでした!お疲れ様でした!+1
アルジュン

@Arjun感謝します。
リーキー修道女

これは非常に優れており、コメントIMOを使用しないことで本当に課題に直面しています。驚くばかり!
シュリーバツァー

1
まあ、これは実際にコメントが...文字列は、未評価のNOPあるとしている...
漏れ修道女

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

説明

ここでのキーはinですĿ。これは、「インデックスnのリンクをモナドとして呼び出す」ことを意味します。リンクは、メインリンク(一番下のリンク)を除き、1から始まる上から下にインデックスが付けられます。Ŀ同様にモジュール式なので、5つのリンクのうちリンク番号7を呼び出そうとすると、実際にはリンク2を呼び出します。

このプログラムで呼び出されるリンクは、プログラムのバージョンに関係なく、常にインデックス10()のリンクです。ただし、インデックス10にあるリンクはバージョンによって異なります。

それぞれの後に表示されるがĿ逆転するとき、それが破損しないように、そこにあります。前に数字がない場合、プログラムは解析時にエラーになりますĿ。持って、それの後には、単に出力にまっすぐ行く場所niladのうち、あります。

元のバージョンはlinkを呼び出し、n + 1を計算します。

逆バージョンはlinkを呼び出し、R範囲1 .. nを生成します。

2倍のバージョンは、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をスタックにプッシュする通常のプログラムと同じパスに従いますが、2番目にラップする代わりに&、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)がスタックにプッシュされた後、1行下に移動して前述の印刷ループに入ります。最初のループのため、時間の複雑さはΘ(N + 2 ^ N)ですが、それはΘ(2 ^ N)に減少します。

オンラインでお試しください!

ダブルとリバース

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

関連するコマンド:

;&^

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

 @>:*[

  :

これは逆のプログラムとほぼ同じように機能しN^2ますが、プログラムの2番目のコピーの最初の行が最初の最後の行に追加されるため、スタックからポップされません。つまり、ドロップコマンド($)は実行されません。のでN^2、スタックの一番上で印刷ループに入ります。

オンラインでお試しください!

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.