Algorithm “基本”的大O分析中的不一致性;算法";

Algorithm “基本”的大O分析中的不一致性;算法";,algorithm,big-o,analytics,Algorithm,Big O,Analytics,我最近学习了算法的形式化Big-O分析;然而,我不明白为什么这两种算法的运行时间会有很大的不同,这两种算法实际上做的是相同的事情。这两种算法都打印0到n的数字。我将用伪代码编写它们: Algorithm 1: def countUp(int n){ for(int i = 0; i <= n; i++){ print(n); } } Algorithm 2: def countUp2(int n){ for(int i = 0; i

我最近学习了算法的形式化Big-O分析;然而,我不明白为什么这两种算法的运行时间会有很大的不同,这两种算法实际上做的是相同的事情。这两种算法都打印0到n的数字。我将用伪代码编写它们:

Algorithm 1:

def countUp(int n){

    for(int i = 0; i <= n; i++){

        print(n);

    }

}

Algorithm 2:

def countUp2(int n){

    for(int i = 0; i < 10; i++){

        for(int j = 0; j < 10; j++){

            ... (continued so that this can print out all values 0 - Integer.MAX_VALUE)

            for(int z = 0; z < 10; z++){

                print("" + i + j + ... + k);

                if(("" + i + j + k).stringToInt() == n){

                    quit();

                }

            }

        }

    }

}
算法1:
def倒计时(整数n){

对于
countUp
中的(int i=0;i),循环一次命中[0,n]范围内的所有数字,从而导致运行时间为O(n)

countUp2
中,您做了一些完全相同的事情,多次。所有循环的边界都是10

假设有3个循环以10为界运行。因此,外部循环是
10
,内部循环是
10x10
,最内部循环是
10x10x10
。因此,最坏情况下,最内部循环将运行1000次,这基本上是恒定时间。因此,对于
n
有界的循环[0,10],您的运行时为10^n,这也可以称为常数时间O(1),因为它不依赖于最坏情况分析的
n


假设您可以编写足够多的循环,并且
n
的大小不是一个因素,那么您需要为n的每一个数字创建一个循环。
n
中的位数是
int(math.floor(math.log10(n))+1
;我们称之为
dig
。因此,迭代次数的更严格上限为10^dig(可以简化为O(n);证明留给读者作为练习)。

在分析算法的运行时时,需要寻找的一个关键因素是循环。在算法1中,代码执行n次,使运行时为O(n)。在算法2中,嵌套循环每次运行10次,因此运行时为O(10^3)。这是因为您的代码对中间循环的每次运行都会运行最内层循环10次,而中间循环对最外层循环的每次运行都会运行10次。因此,代码会运行10x10次。(但这纯粹是一个上限,因为您的if语句可能会在循环完成之前结束算法,具体取决于n的值).

若要在
countUp2
中最多计算
n
,则需要与
n
中的位数相同的循环数:所以
log(n)
循环。每个循环可以运行
10次,因此总迭代次数为
10^log(n)
,即
O(n)
第一次在O(n log n)中运行时间,因为
打印(n)
输出O(对数n)位

第二个程序假设n的上限,所以O(1)很小。当我们进行复杂度分析时,我们假设编程语言的一个更抽象的版本,其中(通常)整数是无界的,但算术运算仍然在O(1)中执行。在您的示例中,您混淆了实际的编程语言(有界整数)对于这个更抽象的模型(它没有)。如果你重写程序[*],使它有一个根据n动态调整的循环数(因此,如果你的数字n有k位,则有k+1个嵌套循环),那么它会对每个数字进行一次最内层代码的迭代,从0到n后的下一个10次方(logn)在构造字符串时工作[**],因此整个程序也是O(nlogn)

[*]您不能使用for循环和变量来实现这一点;您必须使用递归或类似的方法,使用数组而不是变量i,j,k,…,z


[**]这是假设您的编程语言优化了k长度-1字符串的添加,使其在O(k)时间内运行。明显的字符串连接实现是O(k^2)时间,这意味着您的第二个程序将在O(n(logn)^2)时间内运行。

第二个函数可能比第一个函数慢,但它仍应在O(n)时间内运行时间。你是如何得到O(n^10)的?@AtnNn第二个O(n)是怎样的?所有循环都是以10为界的如果你把数字11放入第二个算法中,你会得到输出,“0,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,8,2,3,4,5,6,7,8,9,10,11”这与第一个不同。@DebosmitRay循环也以条件
为界(“”+i+j+k).stringToInt()==n
@AtnNn如果我错了,请纠正我。但这不应该影响最坏情况下的运行时间,对吗?如果
n
是循环可以达到的最大值的一倍呢?对于
m
循环,循环在
10^m
处最大,这是一个常量。你的意思是什么?哪里使用
n
作为循环边界?循环边界独立于n@DebosmitRay为了与第一个示例保持一致,我使用了n来解释循环是如何影响分析的。我想我应该使用另一个字母,因为在本例中n用于单独的目的。循环边界是常量。因此使用
n
会引起不必要的混淆。@DebosmitRay Good点。希望编辑后更清楚。是的,先生,我想是。:)好的。我明白了。因为for循环是由一个常数限定的,所以它意味着它只是在O(n)中运行time@dsiegler19不,长官,Big-O分析表明,在最坏的情况下,第二个函数在O(1)中运行。