我是一名程序员,但没有计算机科学背景,所以最近我一直在关注优秀的MIT OpenCourseWare计算机科学和编程入门。在这个过程中,问题是:“任何只使用函数定义和调用编写的程序,基本算术运算符,赋值和条件是否在恒定时间内运行?”
我认为答案是肯定的,因为所有这些操作看起来都很简单。但正如你聪明的人可能已经知道的那样,正确的答案是否定的,显然是因为无限期递归的可能性。
似乎我只是不理解“在恒定时间内”的含义。我想象的意思是,这听起来只是意味着一个简单的操作需要花费已知的时间来完成。我接受递归可以导致你的程序永远运行,但不是每个操作仍然需要有限和可测量的时间每个...即使现在有无限数量的它们?或者答案是否意味着无法有效地说两个无限递归程序需要花费相同的时间来运行?
如果有人能给我一个“在不变时间”的权威定义及其含义,我将非常感激!
答案 0 :(得分:33)
有效的恒定时间意味着你可以给程序运行多长时间的上限,这不受任何输入参数的影响。
将其与线性时间(对于某些输入n
- 通常实际上是输入数据的 size 而不是直接值)进行比较 - 这意味着对于mn + k
和m
的某些值,所用时间的上限可以表示为k
。
请注意,这并不意味着程序只会因为它在恒定时间内运行而将相同的时间用于任何输入数据。例如,请考虑以下方法:
int foo(int n)
{
if (n == 0)
{
return 0;
}
int j = n + 1;
int k = j * 2;
return k;
}
在n
非零的情况下比在零的情况下做更多的工作。但是,它仍然是恒定时间 - 最多,它将进行一次比较,一次加法和一次乘法。
现在将它与递归函数进行比较:
public int foo(int n)
{
if (n <= 1)
{
return 1;
}
return n * foo(n - 1);
}
这将递归n
次 - 因此它在n
中是线性的。但是,你可能比线性更糟糕。考虑这种计算Fibonacci数的方法:
public int fib(int n)
{
if (n == 0)
{
return 0;
}
if (n == 1)
{
return 1;
}
return fib(n - 2) + fib(n - 1);
}
看起来比以前的版本差得多 - 但现在这是指数(上限最容易表示为O(2 n )。它仍然只使用简单的比较,加法和函数调用。
答案 1 :(得分:14)
“恒定时间”意味着操作将在一段时间内(或内存空间 - 这是另一种经常测量的)独立的输入大小执行。通常你选择一个变量(让我们使用n
)来表示输入大小。
O(1)
- 恒定时间 - 运行时间不依赖于n
O(n)
- 线性时间 - 线性比例到n
O(n^2)
- 二次时间 - 运行时间与 n
的平方成正比
这些只是几个例子;可能性是无止境。请参阅complexity
上的wiki文章以下是一些仅由您提及的操作组成的程序可能需要花费不同时间的具体方法:
int n = // some value
doSomething
doSomething
doSomething
注意它的长度是三个 somethings ,与n
无关。 O(1)
int n = // some value
def f(n):
if n == 0 return
doSomething
f(n-1)
f(n)
现在我们为每个值0..n(线性时间,O(n)
)运行某事
我们可以有一点乐趣 -
int n = //some value
def f(n):
if n == 0 return
doSomething
f(n-1)
f(n-1)
这里的运行时间是多少? (即我们执行了多少 somethings ?):)
答案 2 :(得分:8)
“恒定时间”与“O(1)”具有相同的含义,这并不意味着算法在固定的时间内运行,只是意味着不是与输入的长度/大小/幅度成比例。即,对于任何输入,它可以在相同的时间内计算(即使该时间量非常长)。
答案 3 :(得分:4)
“常数时间”通常表示计算结果所需的时间与输入的大小无关。
例如。无论列表有多大,计算大多数托管语言中列表/向量的长度都是在恒定时间内完成的。大小存储为单独的字段,并随着列表的增长和缩小而更新。但计算计数就像读取字段一样简单。
计算双向链表的大小通常不是恒定的时间。该列表通常可以在两端进行变异,因此没有存储计数的中心位置。因此,确定列表的长度需要访问它并确定那里有多少元素。因此,随着列表的增长,计算答案所需的时间也会增加。
答案 4 :(得分:2)
此处的常量时间意味着不依赖于输入的数量(不是输入本身),如果不允许或转到,则使其依赖于输入数量的唯一方法是通过条件和递归。虽然你可以说一些有争议的解决方案不需要递归。 例如。 (在C中)
if(ReadInput()) DoSomeThing();
if(ReadInput()) DoSomeThing();
if(ReadInput()) DoSomeThing();
if(ReadInput()) DoSomeThing();
答案 5 :(得分:1)
“在恒定时间内”意味着无论输入什么,程序的运行时间都不会超过已知的时间。
将阶乘函数视为反例。例如,5的阶乘计算如下:
5! = 5 * 4 * 3 * 2 * 1 = 120
这显然比10的阶乘花费更少的时间计算,因为要做到后者,程序必须再进行五次乘法运算:
10! = 10 * 9 * 8 * 7 * 6 * 5 * 4 * 3 * 2 * 1
所以,“常数时间”并不意味着我们知道程序在每种情况下运行多长时间,但它永远不会超过已知时间(上限)。
答案 6 :(得分:0)
要考虑的真正重要的事情是时间如何随着元素数量的变化而变化。在恒定的时间内意味着无论涉及多少元素,时间都保持不变(外行的解释)。
答案 7 :(得分:0)
如果程序永远运行,那么它在一段已知的时间内没有完成,因为它没有完成。我们将“恒定时间”的概念应用于整个程序的运行,而不是每个步骤。
“常数时间”表示“时间不取决于输入量”。