算法时间复杂度计算_算法的时间复杂度

算法时间复杂度计算

If you are a web developer or a programmer in general, you have most likely written algorithms for various tasks. Examples are: searching through a table, sorting an array of numbers by descending value, calculating the shortest path to get to a location… But what qualifies an algorithm to be a good algorithm?

如果您是一般的Web开发人员或程序员,那么您很可能为各种任务编写了算法。 例如:搜索表,按降序对数字数组进行排序,计算到达位置的最短路径……但是,什么使一种算法成为一种好的算法呢?

One specification of an algorithm is its correctness. You will probably assume that your algorithm works after testing it out a few times. However, if you can mathematically prove that your algorithm will work as expected for every input value possible, this is a huge bonus. I will not go further in to that subject in this writing.

一种算法规范是其正确性。 您可能会假设您的算法经过几次测试后仍然有效。 但是,如果您可以从数学上证明您的算法对于每个可能的输入值都能按预期工作,那么这将是一笔巨大的收获。 在本文中,我不会进一步探讨该主题。

Another specification is its efficiency: how does the computing time relate to the amount of input? Is it a linear relation? Does computing time rise exponentially for the doubling of input? That’s what this article will be about.

另一个规范是它的效率:计算时间与输入量如何相关? 它是线性关系吗? 输入时间增加一倍,计算时间是否会呈指数增长? 这就是本文的主题。

时间复杂度 (Time Complexity)

Time complexity is, as mentioned above, the relation of computing time and the amount of input. This is usually about the size of an array or an object. Time complexity also isn’t useful for simple functions like fetching usernames from a database, concatenating strings or encrypting passwords. It is used more for sorting functions, recursive calculations and things which generally take more computing time.

如上所述,时间复杂度是计算时间与输入量之间的关系。 这通常大约是数组或对象的大小。 时间复杂度对于简单功能(例如从数据库中获取用户名,连接字符串或加密密码)也没有用。 它更多地用于排序功能,递归计算以及通常需要更多计算时间的事情。

This is not because we don’t care about that function’s execution time, but because the difference is negligible. We don’t care if it takes 10ms instead of 3ms to fetch a username. However, if we have a recursive sorting algorithm which takes 400ms and we can reduce that to 50ms, that would be an interesting thing to do.

这不是因为我们不在乎该函数的执行时间,而是因为差异可以忽略不计。 我们不在乎获取用户名是否需要10毫秒而不是3毫秒。 但是,如果我们有一个递归排序算法,该算法需要400毫秒,并且可以将其减少到50毫秒,那将是一件很有趣的事情。

As you might guess, the lower the computing time, the more efficient the algorithm is. The question that follows is: ‘how can we define time complexity in an universal way?’. That’s where we’ll use the ‘Big O notation’.

您可能会猜到,计算时间越短,算法的效率就越高。 接下来的问题是:“我们如何以通用的方式定义时间复杂度?”。 那就是我们将使用“大O符号”的地方。

大O符号 (Big O notation)

The Big O notation is a notation for the time complexity of an algorithm. It is a mathematical representation of the upper bound of the limit of the scaling factor of the algorithm. For example, if we double the size of an input array, by how much does the computing time increase? This might become clear with two examples:

Big O表示法是一种算法的时间复杂度。 它是算法比例因子上限的数学表示。 例如,如果我们将输入数组的大小加倍,那么计算时间将增加多少? 这可以通过两个示例来弄清楚:

$numbers = array(14,82,4,0,24,28);
foreach($numbers as $number)
{
    echo $number;
}

Imagine that the $numbers array is the argument of the function. We have a foreach loop running through its items. If we calculate the time that the code takes to run, what happens if we double the size of the array? We can easily see in this example that it will double the time to run. We see that there is a linear relationship between the size of the array and the computing time. So if we write the size of the array as n, we can write the time complexity as O(n). Another example:

假设$numbers数组是该函数的参数。 我们有一个foreach循环贯穿其中。 如果我们计算代码运行所需的时间,如果将数组的大小加倍,会发生什么? 在此示例中,我们可以很容易地看到它将使运行时间加倍。 我们看到数组的大小和计算时间之间存在线性关系。 因此,如果将数组的大小写为n,则可以将时间复杂度写为O(n)。 另一个例子:

$numbers = array(14,82,4,0,24,28);
foreach($numbers as $number1)
{
    foreach($numbers as $number2)
    {
        if($number1 >= $number2)
        {
            echo $number1 . " is greater than or equal to " . $number2;
        }
        else
        {
            echo $number1 . " is smaller than " . $number2;
        }
    }
}

In this piece of code, there is a foreach loop located inside another foreach loop. Let’s say ‘n’ is the size of $numbers. Then we loop ‘n’ times through ‘n’. That makes the total amount of loops n². As you might guess, we write the time complexity as O(n²).

在这段代码中,另一个foreach循环中有一个foreach循环。 假设'n'是$numbers的大小。 然后,我们通过“ n”循环“ n”次。 这使循环总数为n²。 您可能会猜到,我们将时间复杂度写为O(n²)。

The big O notation expresses the scaling of computing time and uses some sort of mixture between the upper bound and the limit of that scaling. For example:

大号O表示计算时间的标度,并在该标度的上限和上限之间使用某种混合形式。 例如:

$numbers = array(14,82,4,0,24,28);
foreach($numbers as $number1)
{
    foreach($numbers as $number2)
    {
        if($number1 >= $number2)
        {
            echo $number1 . " is greater than or equal to " . $number2;
        }
        else
        {
            echo $number1 . " is smaller than " . $number2;
        }
    }
}

foreach($numbers as $number)
{
    echo $number;
}

You might feel the urge to write that time complexity as O(n²+n). While, technically, it is not wrong, it is rather meaningless: you always define time complexity as the mathematical limit to infinity. If you take the limit to infinity of a polynomial, it is always the variable with the highest exponent that matters. Since time complexity applies to the rate of change of time, factors are never written before the variables. This means that, for example, you can replace O(5n) by O(n).

您可能会想将时间复杂度写成O(n²+ n)的冲动。 从技术上讲,这没有错,但是却毫无意义:您总是将时间复杂度定义为无穷大的数学极限。 如果对多项式取无穷大,则始终是指数最高的变量。 由于时间复杂度适用于时间的变化率,因此永远不会在变量之前写入因子。 这意味着,例如,您可以将O(5n)替换为O(n)。

高效算法 (Efficient algorithms)

Now that we know how to express time complexity, we can take a look at some examples of efficient algorithms.

现在我们知道了如何表达时间复杂度,下面我们来看一些高效算法的例子。

For the first one, I want to introduce another special notation: O(log(n)), which shows a logarithmic relationship. An example of an algorithm that uses this is the binary search algorithm. For those too lazy to read the full article: you want to find a name in an alphabetically ordered list and so you go to the center. If the name you search comes before that, you go to the center between the center page and the beginning (so the 1/4th). You continue that until you find the right name. The time complexity of that algorithm is O(log(n)).

对于第一个,我想引入另一种特殊的表示法:O(log(n)),它显示了对数关系。 使用此算法的一个示例是二进制搜索算法 。 对于那些懒于阅读全文的人:您想在按字母顺序排列的列表中找到一个名称,因此您去了中心。 如果搜索的名称在此之前,则您将转到中心页面和开头之间的中间位置(即1/4)。 您继续操作直到找到正确的名称。 该算法的时间复杂度为O(log(n))。

If you were to find the name by looping through the list entry after entry, the time complexity would be O(n). While that isn’t bad, O(log(n)) is many times better. It can be qualified as an efficient algorithm.

如果要通过在输入后循环遍历列表项来查找名称,则时间复杂度为O(n)。 虽然这还不错,但是O(log(n))好很多倍。 它可以被视为一种有效的算法。

低效的算法 (Inefficient algorithms)

Just as there are efficient algorithms, we have inefficient algorithms as well. One of them is the bogosort algorithm. While (fortunately) nobody actually uses it, it’s used as a demonstration of how you should not do it. When using it for sorting a list of numbers in descending order, it will, at random, choose an order for the list. It will then check if the list is in the correct order, if it is not, it will randomize it again. As you see, that algorithm isn’t very efficient and it has a time complexity of O(n x n!) (with n! as factorial of n). If you want to sort arrays in a time efficient manner, look for another algorithm, Heapsort for example.

就像有高效的算法一样,我们也有效率不高的算法。 其中之一是bogosort算法 。 尽管(幸运的是)没有人实际使用它,但是它只是用来说明您不应该使用它。 当使用它对数字列表进行降序排序时,它将随机选择列表的顺序。 然后它将检查列表的顺序是否正确,如果顺序不正确,则会再次将其随机化。 如您所见,该算法不是很有效,它的时间复杂度为O(nxn!)(其中n!为n的阶乘 )。 如果要以节省时间的方式对数组进行排序,请寻找另一种算法,例如Heapsort

编写算法并对其进行优化 (Writing an algorithm and optimizing it)

I will now demonstrate how we can apply time complexity by first writing an algorithm, and then writing a better one. You will see why the latter is better by looking at its complexity. I want to write a function with an array as argument. The array will consist of a number of positive integers. The function then will return a new array, containing these integers, sorted by increasing size.

现在,我将演示如何通过首先编写一种算法,然后编写一种更好的算法来应用时间复杂度。 通过查看其复杂性,您将了解为什么后者更好。 我想写一个以数组为参数的函数。 该数组将由多个正整数组成。 然后,该函数将返回一个新数组,其中包含这些整数,并按大小递增排序。

The first algorithm I will use is called insertion sort. In short: it will loop through the array, and if an integer is smaller than the next one, it will switch them. A more detailed description can be read here.

我将使用的第一个算法称为插入排序。 简而言之:它将遍历数组,如果一个整数小于下一个整数,它将对其进行切换。 可以在这里阅读更详细的描述。

I implemented the algorithm this way:

我以这种方式实现了算法:

function insertionSort($array)
{
    $currentNumber;

    for($i = 1; $i < count($array); $i++)
    {
        $currentNumber = $array[$i];

        while (($i-1 >= 0) && ($currentNumber < $array[$i-1])) //While there is a smaller number to the left
        {
            $array[$i] = $array[$i-1]; //replace the current number by the number to its left
            $i--;
        }
        //there are no smaller numbers to the left anymore
        $array[$i] = $currentNumber; //set the current number to the number that originally had index i
    }

    return $array;
}

$array = array(4,29,9,2,9);
print_r(insertionSort($array));

You see that there is a while loop inside a for loop. The worst case scenario is a time complexity of O(n²). While the algorithm does a good job at what it’s designed for, O(n²) is not good if you’re dealing with bigger arrays. I will now demonstrate a better algorithm for the job: this algorithm will first find the maximum of the array that is passed as argument. It will then create an associative array named $counting, which counts the number of times that each index appears in the original array. Finally it loops through the counting array and it adds every index ‘n’ times to a new array, where ‘n’ is the value of the index. For example, if the value of $counting[23] is ‘3’, it will add 23 3 times to the new array.

您会看到for循环中有一个while循环。 最坏的情况是时间复杂度为O(n²)。 尽管该算法在设计目标上做得不错,但是如果您要处理更大的数组,O(n²)就不好了。 现在,我将展示一个更好的算法来完成这项工作:该算法将首先找到作为参数传递的数组的最大值。 然后,它将创建一个名为$counting的关联数组,该数组将$counting每个索引在原始数组中出现的次数。 最后,它循环遍历计数数组,并将每个索引“ n”次添加到新数组中,其中“ n”是索引的值。 例如,如果$ counting [23]的值为'3',它将向新数组中添加23 3次。

function findMax($array)
{
    $maximum = $array[0];
    for($i = 1; $i < count($array); $i++)
    {
        $maximum = ($array[$i] > $maximum ? $array[$i] : $maximum);  
    }

    return $maximum;
}

function increasingSort($array)
{
    $size = findMax($array);

    $counting = array();

    for($i = 0; $i <= $size; $i++)
    {
        $counting[$i] = 0;
    }

    for($i = 0; $i < count($array); $i++)
    {
        $counting[$array[$i]]++;
    }

    $ordered = array();

    for($i = 0; $i < count($counting); $i++)
    {
        for($j = 0; $j < $counting[$i];$j++)
        {
            $ordered[] = $i;
        }
    }

    return $ordered;
}

$array = array(29,1,2,2,2,28,98);
print_r(increasingSort($array));

The time complexity of this algorithm is O(n), a lot better than the Insertion Sort algorithm. However, note that this algorithm might not be suitable for higher numbers which vary a lot, as the $array will have a huge size. Always make sure that the algorithm fits the situation.

该算法的时间复杂度为O(n),比插入排序算法好很多。 但是,请注意,此算法可能不适用于变化较大的较大数字,因为$array的大小很大。 始终确保算法适合情况。

时间复杂度不是全部 (Time complexity is not everything)

Now that I showed you what time complexity is, note that computing time should never be your only focus. While you should always try to find out if your algorithm is time efficient enough, there are other aspects to consider, too. The computing time doesn’t matter if you need to sort ten items, so don’t waste time on that. Also, for most tasks like sorting, searching entries, etc. there already are various efficient and tested algorithms available, waiting for you to Google them.

现在,我向您展示了时间的复杂性,请注意,计算时间永远不应是您唯一的重点。 尽管您应该始终尝试找出算法是否足够省时,但也需要考虑其他方面。 如果需要对十个项目进行排序,则计算时间无关紧要,因此不要在上面浪费时间。 此外,对于大多数任务(如排序,搜索条目等),已经有各种有效且经过测试的算法可用,等待您使用Google。

翻译自: https://www.sitepoint.com/time-complexity-algorithms/

算法时间复杂度计算

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值