尝试通过计算步数来计算函数的大数。我认为这些是如何按照他们在示例中的方式来计算每一步,但不知道如何计算总数。
int function (int n){
int count = 0; // 1 step
for (int i = 0; i <= n; i++) // 1 + 1 + n * (2 steps)
for (int j = 0; j < n; j++) // 1 + 1 + n * (2 steps)
for (int k = 0; k < n; k++) // 1 + 1 + n * (2 steps)
for (int m = 0; m <= n; m++) // 1 + 1 + n * (2 steps)
count++; // 1 step
return count; // 1 step
}
我想说这个函数是O(n ^ 2),但我不明白它是如何计算的。
我一直在看的例子
int func1 (int n){
int sum = 0; // 1 step
for (int i = 0; i <= n; i++) // 1 + 1 + n * (2 steps)
sum += i; // 1 step
return sum; // 1 step
} //total steps: 4 + 3n
和
int func2 (int n){
int sum = 0; // 1 step
for (int i = 0; i <= n; i++) // 1 + 1 + n * (2 steps)
for (int j = 0; j <= n; j++) // 1 + 1 + n * (2 steps)
sum ++; // 1 step
for (int k = 0; k <= n; k++) // 1 + 1 + n * (2 steps)
sum--; // 1 step
return sum; // 1 step
}
//total steps: 3n^2 + 7n + 6
答案 0 :(得分:2)
你刚才提出的是非常简单的例子。 在我看来,你只需要理解循环中的复杂性如何工作,以便理解你的例子。
简而言之(非常简短)必须在asymptotic complexity中考虑一个周期,如下所示:
loop (condition) :
// loop body
end loop
condition
应该告诉您循环执行的次数与输入的大小 相比。原因非常直观:身体中的内容会被重复执行,直到条件得到验证,即循环(以及身体)执行的次数。
只是一些例子:
// Array linear assignment
std::vector<int> array(SIZE_ARRAY);
for (int i = 0; i < SIZE_ARRAY; ++i) {
array[i] = i;
}
让我们分析一下这个简单的循环:
首先,我们需要选择输入 relative 来计算我们的复杂度函数。这种情况非常简单:变量是数组的大小。那是因为我们想知道我们的程序如何尊重输入数组的大小增长。
循环将重复SIZE_ARRAY
次。所以正文执行的次数是SIZE_ARRAY
次(注意:这些值是可变的,不是常数值)。
现在考虑循环体。指令array[i] = i
不依赖于数组的大小。它需要未知数量的CPU周期,但该数字始终相同,即常量。
总结一下,我们重复SIZE_ARRAY
次指令,该指令占用恒定数量的CPU时钟(假设k
是该值,是常量)。
因此,从数学上来说,该简单程序将执行的CPU时钟数为SIZE_ARRAY * k
。
使用O Big notation,我们可以描述限制行为。这是当自变量变为无穷大时函数将采取的行为。
我们可以写:
O(SIZE_ARRAY * k) = O(SIZE_ARRAY)
那是因为k
是一个常数值,根据 Big O Notation 的定义,常数不会在无穷大处增长(永远是常数)。
如果我们将SIZE_ARRAY
称为N
(输入的大小),我们可以说我们的函数的时间复杂度为O(N)
。
最后一个(“更复杂”)的例子:
for (int i = 0; i < SIZE_ARRAY; ++i) {
for (int j = 0; j < SIZE_ARRAY; ++j) {
array[j] += j;
}
}
与之前一样,我们将问题规模与SIZE_ARRAY
进行比较。
不久:
SIZE_ARRAY
次,即O(SIZE_ARRAY)
。SIZE_ARRAY
次。k
。我们考虑第一个循环的执行次数,然后乘以它的主体复杂度。
O(SIZE_ARRAY) * [first_loop_body_complexity].
但第一个循环的主体是:
for (int j = 0; j < SIZE_ARRAY; ++j) {
array[j] += j;
}
这是前一个例子中的单个循环,我们刚刚计算出的是复杂性。这是一个O(SIZE_ARRAY)
。所以我们可以看到:
[first_loop_body_complexity] = O(SIZE_ARRAY)
最后,我们的整个复杂性是:
O(SIZE_ARRAY) * O(SIZE_ARRAY) = O(SIZE_ARRAY * SIZE_ARRAY)
那是
O(SIZE_ARRAY^2)
使用N
代替SIZE_ARRAY
。
O(N^2)
答案 1 :(得分:1)
免责声明:这不是数学解释。这是一个愚蠢的版本,我认为可以帮助那些被介绍到复杂世界的人,并且像我第一次遇到这个概念时一样无能为力。我也不给你答案。试着帮助你到达那里。
故事的道德:不计算步骤。复杂性不是关于执行多少指令(我将使用它而不是“步骤”)。这本身(几乎)完全无关紧要。通俗地说(时间)复杂性是指执行时间如何根据输入的增长而增长 - 这就是我最终理解复杂性的方式。
让我们一步一步地了解一些最常见的复杂性:
这表示一种算法,其执行时间不依赖于输入。当输入增长时,执行时间不会增长。
例如:
auto foo_o1(int n) {
instr 1;
instr 2;
instr 3;
if (n > 20) {
instr 4;
instr 5;
instr 6;
}
instr 7;
instr 8;
};
此功能的执行时间不依赖于n
的值。请注意,即使根据n
的值执行某些指令,我也可以说。数学上这是因为O(constant) == O(1)
。直观地说,这是因为指令数量的增长与n
不成比例。在同一个想法中,如果函数有10个instr或1k指令则无关紧要。它仍然是O(1)
- 不变的复杂性。
这表示一种算法,其执行时间与输入成比例。当给出小输入时,它需要一定量。增加输入时,执行时间按比例增长:
auto foo1_on(int n)
{
for (i = 0; i < n; ++i)
instr;
}
此功能为O(n)
。这意味着当输入加倍时,执行时间会增加一个因子。任何输入都是如此。例如,当您将输入从10加倍到20并且将输入从1000加倍到2000时,在算法执行时间的增长中或多或少地存在相同因素。
与忽略对“最快”增长没有太多贡献相对的想法一致,所有下一个功能仍然具有O(n)复杂性。数学O
复杂性是上限的。这会导致O(c1*n + c0) = O(n)
auto foo2_on(int n)
{
for (i = 0; i < n / 2; ++i)
instr;
}
此处:O(n / 2) = O(n)
auto foo3_on(int n)
{
for (i = 0; i < n; ++i)
instr 1;
for (i = 0; i < n; ++i)
instr 2;
}
此处O(n) + O(n) = O(2*n) = O(n)
这告诉你,当你增加输入时,执行时间会越来越大。例如,下一个是O(n^2)
算法的有效行为:
读取:当您将输入从...加倍到...时,可以增加执行时间...次
试一试!。编写O(n^2)
算法。并加倍输入。首先,您会看到计算时间略有增加。有一次它只是吹而你必须等待几分钟,而在之前的步骤中它只需要几秒钟。
查看n^2
图表后,您可以轻松了解这一点。
auto foo_on2(int n)
{
for (i = 0; i < n; ++i)
for (j = 0; j < n; ++j)
instr;
}
这个功能O(n)
怎么样?简单:第一个循环执行n
次。 (我不在乎它是n times plus 3
还是4*n
。然后,对于第一个循环的每一步,第二个循环执行n
次。有n
次迭代i循环。对于每次迭代,都有n
次j迭代。总共我们有n * n = n^2
次j迭代。因此O(n^2)
还有其他有趣的复杂性,如对数,指数等等。一旦你理解了数学背后的概念,就会变得非常有趣。例如,对数复杂度O(log(n))
的执行时间随着输入的增长而增长慢和更慢。当你查看日志图时,你可以清楚地看到它。
网上有很多关于复杂性的资源。搜索。读。不明白!再次搜索。读。拿纸和笔。了解!。重复。
答案 2 :(得分:0)
在这些简单的情况下,您可以通过查找最常执行的指令来确定时间复杂度,然后找出这个数字取决于count++
的方式。
在示例1中,sum += i;
执行n ^ 4次=&gt;为O(n ^ 4)
在示例2中,sum ++;
执行n次=&gt;为O(n)
在示例3中,function enqueue_foundation() {
wp_enqueue_style( 'foundation-css', get_template_directory_uri() . '/css/foundation.min.css');
wp_enqueue_script( 'foundation-js', get_template_directory_uri() . '/js/foundation.min.js');
wp_enqueue_script( 'foundation-jquery', get_template_directory_uri() . '/js/jquery.js');
}
add_action('wp_enqueue_scripts', 'enqueue_foundation');
执行n ^ 2次=&gt;为O(n ^ 2)
嗯,实际上这是不正确的,因为你的一些循环执行n + 1次,但这根本不重要。在示例1中,指令实际执行(n + 1)^ 2 * n ^ 2次,其与n ^ 4 + 2 n ^ 3 + n ^ 2相同。对于时间复杂度,只有最大的功率计数。
答案 3 :(得分:0)
为了简单起见:
O(N)表示小于或等于到N.因此,在一个代码段中,我们忽略所有并专注于采用最多步数的代码(最高功率)解决问题/完成执行。
按照你的例子:
int function (int n){
int count = 0; // ignore
for (int i = 0; i <= n; i++) // This looks interesting
for (int j = 0; j < n; j++) // This looks interesting
for (int k = 0; k < n; k++) // This looks interesting
for (int m = 0; m <= n; m++) // This looks interesting
count++; // This is what we are looking for.
return count; // ignore
}
要完成该陈述,我们需要“等待”或“覆盖”或“步骤”(n + 1)* n * n *(n + 1)=&gt; O(〜N R个4)。
第二个例子:
int func1 (int n){
int sum = 0; // ignore
for (int i = 0; i <= n; i++) // This looks interesting
sum += i; // This is what we are looking for.
return sum; // ignore
}
要完成它需要n + 1步= => O(〜N)。
第三个例子:
int func2 (int n){
int sum = 0; // ignore
for (int i = 0; i <= n; i++) // This looks interesting
for (int j = 0; j <= n; j++) // This looks interesting
sum ++; // This is what we are looking for.
for (int k = 0; k <= n; k++) // ignore
sum--; // ignore
return sum; // ignore
}
为此我们需要(n + 1)*(n + 1)步骤=&gt; O(〜N ^ 2)