私はプログラマーとして働いていますが、コンピューターサイエンスのバックグラウンドがないため、最近、コンピューターサイエンスとプログラミングの優れたMIT OpenCourseWare入門書をフォローしています。その過程で、質問があります。 「関数の定義と呼び出し、基本的な算術演算子、割り当て、条件のみを使用して記述されたプログラムは、一定の時間で実行されますか?」
これらの操作はすべて非常に単純に見えるので、答えは「はい」だと思いました。しかし、賢い人はおそらくすでに知っていたように、正解はノーでした。これは、明らかに無期限の再帰の可能性があるためです。
「一定時間」の意味がわからないようです。私がその意味を描いた方法では、単純な操作が完了するまでに既知の時間がかかることを意味しているように聞こえました。再帰によってプログラムが永久に実行される可能性があることは承知していますが、個々の操作には、それぞれ無限の数があるとしても、有限で測定可能な時間がかかるのではないでしょうか。それとも、答えは、2つの無限再帰プログラムが実行に同じ時間を要すると正当に言うことができないことを意味するのでしょうか?
誰かが私に「一定の時間で」の権威ある定義とその意味を教えてくれるなら、私は非常に感謝するでしょう!
一定の時間とは、事実上、プログラムの実行にかかる時間に一定の上限を与えることができることを意味します。これは、入力パラメーターの影響を受けません。
たとえば、線形時間(一部の入力n
-これは実際には直接値ではなく入力データのサイズになることが多い)-と比較してください。これは上限を意味します。所要時間の一部は、m
およびk
の一部の値に対してmn + k
として表すことができます。
プログラムが一定時間で実行されるという理由だけで、プログラムが入力データに対して同じの時間を要することを意味しないことに注意してください。たとえば、次の方法について考えてみます。
int foo(int n)
{
if (n == 0)
{
return 0;
}
int j = n + 1;
int k = j * 2;
return k;
}
n
がゼロ以外の場合は、ゼロの場合よりも多くの作業を実行します。ただし、それでも一定の時間です-最大で、1回の比較、1回の加算、1回の乗算を実行します。
次に、それを再帰関数と比較します。
public int foo(int n)
{
if (n <= 1)
{
return 1;
}
return n * foo(n - 1);
}
これはn
回繰り返されるので、n
では線形です。ただし、線形よりもはるかに悪化する可能性があります。フィボナッチ数を計算するためのこの方法を検討してください。
public int fib(int n)
{
if (n == 0)
{
return 0;
}
if (n == 1)
{
return 1;
}
return fib(n - 2) + fib(n - 1);
}
それはlook以前のバージョンよりもはるかに悪いわけではありません-しかし、これは現在exponential(上限はO(2)として最も簡単に表現されますn)。ただし、それでも単純な比較、加算、および関数呼び出しのみを使用しています。
「一定時間」とは、入力サイズの時間(またはメモリスペース-これはよく測定される別のこと)で操作が実行されることを意味します独立。通常、入力サイズを示すために変数を選択します(n
を使用しましょう)。
O(1)
-一定時間-実行時間はn
に依存しません
O(n)
-線形時間-実行時間はn
に線形比例です
O(n^2)
--2次時間-実行時間はn
の2乗に比例します
これらはほんの一例です。可能性は無限大。 複雑さ に関するwikiの記事を参照してください
あなたが言及した操作のみで構成されるプログラムがさまざまな時間を要する可能性があるいくつかの具体的な方法を次に示します。
_int n = // some value
doSomething
doSomething
doSomething
_
n
が何であるかに関係なく、長さが3何かであることに注意してください。 O(1)
_int n = // some value
def f(n):
if n == 0 return
doSomething
f(n-1)
f(n)
_
ここで、値0..n(線形時間、O(n)
)ごとにsomethingを実行します。
そして、私たちは少し楽しむことができます-
_int n = //some value
def f(n):
if n == 0 return
doSomething
f(n-1)
f(n-1)
_
ここでの実行時間は何ですか? (つまり、いくつの何かを実行しますか?):)
「一定時間」は「O(1)」と同じ意味です。これは、アルゴリズムが一定の時間で実行されることを意味するのではなく、ではないに比例することを意味します。入力の長さ/サイズ/大きさ。つまり、どの入力でも、同じ時間で計算できます(その時間が本当に長い場合でも)。
「定数時間」とは、一般に、結果の計算にかかる時間が入力のサイズに依存しないことを意味します。
例えば。ほとんどの管理対象言語でのリスト/ベクトルの長さの計算は、リストのサイズに関係なく一定時間で行われます。サイズは別のフィールドとして保存され、リストの拡大と縮小に応じて更新されます。ただし、カウントの計算は、フィールドを読み取るのと同じくらい簡単です。
二重にリンクされたリストのサイズの計算は、多くの場合、一定の時間ではありません。多くの場合、リストは両端で変更される可能性があるため、カウントを格納する中心的な場所はありません。したがって、リストの長さを決定するには、リストにアクセスして、そこに含まれる要素の数を決定する必要があります。したがって、リストが大きくなるにつれて、答えを計算するのにかかる時間も大きくなります。
ここでの一定時間とは、(入力自体ではなく)入力の数に依存しないことを意味します。許可されていない場合、またはgotoが許可されていない場合、入力の数に依存させる唯一の方法は、条件と再帰です。いくつかの議論の余地のある解決策では再帰は必要ないと主張することもできますが。例えば。 (Cで)
if(ReadInput()) DoSomeThing();
if(ReadInput()) DoSomeThing();
if(ReadInput()) DoSomeThing();
if(ReadInput()) DoSomeThing();
「一定時間内」とは、入力が何であっても、プログラムが既知の時間より長く実行されないことを意味します。
反例として階乗関数を考えてみましょう。たとえば、5の階乗は次のように計算されます。
5! = 5 * 4 * 3 * 2 * 1 = 120
後者を実行するには、プログラムがさらに5つの乗算を実行する必要があるため、これは明らかに10の階乗よりも計算にかかる時間が短くなります。
10! = 10 * 9 * 8 * 7 * 6 * 5 * 4 * 3 * 2 * 1
したがって、「一定時間」とは、それぞれの場合にプログラムが実行される時間を知っていることを意味するのではなく、既知の時間(上限)より長く実行されることは決してないことを意味します。
考慮すべき本当に重要なことは、要素の数の関数として時間がどのようにスケーリングするかです。一定時間とは、関係する要素の数に関係なく、時間が同じままであることを意味します(素人の説明)。
プログラムが永久に実行される場合、プログラムは完了しないため、既知の時間内に完了しません。 「一定時間」の概念は、個々のステップではなく、プログラム全体の実行に適用されます。
「一定時間」とは、「入力量に依存しない時間」を意味します。