Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
395 views
in Technique[技术] by (71.8m points)

algorithm - 大O,您如何计算/近似?(Big O, how do you calculate/approximate it?)

Most people with a degree in CS will certainly know what Big O stands for .

(大多数拥有CS学位的人肯定会知道Big O代表什么。)

It helps us to measure how (in)efficient an algorithm really is and if you know in what category the problem you are trying to solve lays in you can figure out if it is still possible to squeeze out that little extra performance.

(它可以帮助我们评估算法的效率(效率),如果您知道要解决的问题属于哪一类,则可以判断出是否仍有可能榨取少量的额外性能。)

1

(1个)

But I'm curious, how do you calculate or approximate the complexity of your algorithms?

(但是我很好奇, 如何计算或估算算法的复杂性?)

1 but as they say, don't overdo it, premature optimization is the root of all evil , and optimization without a justified cause should deserve that name as well.

(1, 但是正如他们所说,不要过度使用, 过早的优化是万恶之源 ,而没有正当理由的优化也应该得到这个名字。)

  ask by sven translate from so

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

I'll do my best to explain it here on simple terms, but be warned that this topic takes my students a couple of months to finally grasp.

(我会尽力在这里简单地解释它,但要注意,这个主题需要我的学生花几个月的时间才能最终掌握。)

You can find more information on the Chapter 2 of the Data Structures and Algorithms in Java book.

(您可以在《 Java中的数据结构和算法 》第二章中找到更多信息。)


There is no mechanical procedure that can be used to get the BigOh.

(没有可用于获取BigOh的机械程序 。)

As a "cookbook", to obtain the BigOh from a piece of code you first need to realize that you are creating a math formula to count how many steps of computations get executed given an input of some size.

(作为“食谱”,要从一段代码中获取BigOh ,您首先需要认识到您正在创建一个数学公式,以计算给定大小的输入后执行多少计算步骤。)

The purpose is simple: to compare algorithms from a theoretical point of view, without the need to execute the code.

(目的很简单:从理论的角度比较算法,而无需执行代码。)

The lesser the number of steps, the faster the algorithm.

(步骤数越少,算法越快。)

For example, let's say you have this piece of code:

(例如,假设您有这段代码:)

int sum(int* data, int N) {
    int result = 0;               // 1

    for (int i = 0; i < N; i++) { // 2
        result += data[i];        // 3
    }

    return result;                // 4
}

This function returns the sum of all the elements of the array, and we want to create a formula to count the computational complexity of that function:

(此函数返回数组所有元素的总和,我们想创建一个公式来计算该函数的计算复杂度 :)

Number_Of_Steps = f(N)

So we have f(N) , a function to count the number of computational steps.

(因此,我们有f(N) ,它是一个计算计算步数的函数。)

The input of the function is the size of the structure to process.

(函数的输入是要处理的结构的大小。)

It means that this function is called such as:

(这意味着将调用该函数,例如:)

Number_Of_Steps = f(data.length)

The parameter N takes the data.length value.

(参数Ndata.length值。)

Now we need the actual definition of the function f() .

(现在我们需要函数f()的实际定义。)

This is done from the source code, in which each interesting line is numbered from 1 to 4.

(这是从源代码完成的,其中每个有趣的行从1到4编号。)

There are many ways to calculate the BigOh.

(有许多方法可以计算BigOh。)

From this point forward we are going to assume that every sentence that doesn't depend on the size of the input data takes a constant C number computational steps.

(从这一点出发,我们将假定不依赖于输入数据大小的每个句子都采用恒定的C数计算步骤。)

We are going to add the individual number of steps of the function, and neither the local variable declaration nor the return statement depends on the size of the data array.

(我们将添加函数的各个步骤,并且局部变量声明和return语句都不依赖于data数组的大小。)

That means that lines 1 and 4 takes C amount of steps each, and the function is somewhat like this:

(这意味着第1行和第4行每个都执行C步,并且函数有点像这样:)

f(N) = C + ??? + C

The next part is to define the value of the for statement.

(下一部分是定义for语句的值。)

Remember that we are counting the number of computational steps, meaning that the body of the for statement gets executed N times.

(请记住,我们正在计算计算步骤的数量,这意味着for语句的主体被执行了N次。)

That's the same as adding C , N times:

(这与将C相加N次相同:)

f(N) = C + (C + C + ... + C) + C = C + N * C + C

There is no mechanical rule to count how many times the body of the for gets executed, you need to count it by looking at what does the code do.

(没有机械规则来计算for主体执行多少次,您需要通过查看代码的作用来计算它。)

To simplify the calculations, we are ignoring the variable initialization, condition and increment parts of the for statement.

(为了简化计算,我们忽略了for语句的变量初始化,条件和增量部分。)

To get the actual BigOh we need the Asymptotic analysis of the function.

(为了获得实际的BigOh,我们需要对该函数进行渐近分析 。)

This is roughly done like this:

(大致是这样完成的:)

  1. Take away all the constants C .

    (带走所有常数C)

  2. From f() get the polynomium in its standard form .

    (从f()获得standard form多项式 。)

  3. Divide the terms of the polynomium and sort them by the rate of growth.

    (除以多项式的项,然后按增长率对其进行排序。)

  4. Keep the one that grows bigger when N approaches infinity .

    (当N接近infinity大时,保持增长的那个。)

Our f() has two terms:

(我们的f()有两个术语:)

f(N) = 2 * C * N ^ 0 + 1 * C * N ^ 1

Taking away all the C constants and redundant parts:

(除去所有C常量和冗余部分:)

f(N) = 1 + N ^ 1

Since the last term is the one which grows bigger when f() approaches infinity (think on limits ) this is the BigOh argument, and the sum() function has a BigOh of:

(由于最后一项是当f()接近无穷大(考虑极限 )时会增大的项,因此这是BigOh参数,并且sum()函数的BigOh为:)

O(N)

There are a few tricks to solve some tricky ones: use summations whenever you can.

(有一些技巧可以解决一些棘手的问题:尽可能使用求和 。)

As an example, this code can be easily solved using summations:

(例如,可以使用求和轻松地解决此代码:)

for (i = 0; i < 2*n; i += 2) {  // 1
    for (j=n; j > i; j--) {     // 2
        foo();                  // 3
    }
}

The first thing you needed to be asked is the order of execution of foo() .

(您需要询问的第一件事是foo()的执行顺序。)

While the usual is to be O(1) , you need to ask your professors about it.

(虽然通常是O(1) ,但您需要向教授询问。)

O(1) means (almost, mostly) constant C , independent of the size N .

(O(1)表示(几乎,几乎是)常数C ,与大小N无关。)

The for statement on the sentence number one is tricky.

(关于第一句的for语句很棘手。)

While the index ends at 2 * N , the increment is done by two.

(当索引以2 * N结尾时,增量增加2。)

That means that the first for gets executed only N steps, and we need to divide the count by two.

(这意味着第一个for仅执行N步骤,我们需要将计数除以2。)

f(N) = Summation(i from 1 to 2 * N / 2)( ... ) = 
     = Summation(i from 1 to N)( ... )

The sentence number two is even trickier since it depends on the value of i .

(语句编号是更加棘手,因为它取决于价值i 。)

Take a look: the index i takes the values: 0, 2, 4, 6, 8, ..., 2 * N, and the second for get executed: N times the first one, N - 2 the second, N - 4 the third... up to the N / 2 stage, on which the second for never gets executed.

(看一下:索引i取值:0、2、4、6、8,...,2 * N,第二个for执行:N乘以第一个,N-2第二个,N- 4第三个……直到N / 2阶段,第二个for永远不会执行。)

On formula, that means:

(在公式上,这意味着:)

f(N) = Summation(i from 1 to N)( Summation(j = ???)(  ) )

Again, we are counting the number of steps .

(同样,我们正在计算步骤数 。)

And by definition, every summation should always start at one, and end at a number bigger-or-equal than one.

(并且根据定义,每次求和应始终始于一个,并以大于或等于1的数字结束。)

f(N) = Summation(i from 1 to N)( Summation(j = 1 to (N - (i - 1) * 2)( C ) )

(We are assuming that foo() is O(1) and takes C steps.)

((我们假设foo()O(1)并采取C步。))

We have a problem here: when i takes the value N / 2 + 1 upwards, the inner Summation ends at a negative number!

(我们这里有一个问题:当i向上取值N / 2 + 1时,内部求和运算将以负数结束!)

That's impossible and wrong.

(那是不可能的,也是错误的。)

We need to split the summation in two, being the pivotal point the moment i takes N / 2 + 1 .

(我们需要将总和一分为二,成为iN / 2 + 1的关键点。)

f(N) = Summation(i from 1 to N / 2)( Summation(j = 1 to (N - (i - 1) * 2)) * ( C ) ) + Summation(i from 1 to N / 2) * ( C )

Since the pivotal moment i > N / 2 , the inner for won't get executed, and we are assuming a constant C execution c


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...