时间复杂度详解2——时间复杂度的计算

时间复杂度基本计算规则:

  • 基本操作即只有常数项,认为其时间复杂度为O(1)
  • 顺序结构,时间复杂度按加法进行计算
  • 循环结构,时间复杂度按乘法进行计算
  • 分支结构,时间复杂度取最大值
  • 判断一个算法效率时,往往只需要关注操作数量的最高次项,其他次要项和常数项可以忽略
  • 在没有特殊说明时,我们所分析的时间复杂度都是指最坏时间复杂度

单层循环时间复杂度计算

例题分析

 例一:

i = n*n;
whlie(i != 1)
    i = i/2;

我们发现,循环执行的条件是i!=1,然后循环体中i=i/2;改变了i的值。

我们列出循环体执行次数t  和i的最终值(即执行完t次循环后的值)的关系

循环体执行次数t0123
i的改变量in^{2}\frac{n^{2}}{2}\frac{n^{2}}{4}\frac{n^{2}}{8}

           
第二步:找到t的最终值与i的关系:

i=\frac{n^{2}}{2^{t}}

第三步:确定循环停止条件:

i=1

第四步:联立第二步第三步两式解方程:

\frac{n^{2}}{2^{t}}=1     n^{2}=2^{t} 

两边对2取对数得    t=log_{2}n^{2}=2log_{2}n

所以得到时间复杂度为:T=O(log_{2}n)

例二:

x = 0;
while (n>=(x+1)*(x+1))
    x = x+1;

第一步:列出循环趟数t及x的最终值(即执行完t次循环后的值)

循环体执行次数t0123
x的最终值x0123


第二步:找到t与x的最终值关系:

t=x

第三步:确定循环停止条件:

n=(x+1)^{2}

第四步:联立第二步第三步两式解方程:

n=(t+1)^{2}

t=\sqrt{n}-1

所以得到时间复杂度为:

T=O(\sqrt{n})

 例三:

int i = 1;
while (i<=n)
    i = i *2

第一步:列出循环趟数t及i的最终值(即执行完t次循环后的值)

循环趟数t0123
i的最终值1248


第二步:找到t与i的关系:

i=2^{t}

第三步:确定循环停止条件:

i=n

第四步:联立第二步第三步两式解方程:

n=2^{t}

t=log_{2}n

所以得到时间复杂度为:

T=O(log_{2}n)

 例四:

int i = 0;
while (i*i*i<=n)
    i ++;


第一步:列出循环趟数t及i的最终值:

循环趟数t0123
i的最终值i0123


第二步:找到t与i的关系:

t=i

第三步:确定循环停止条件:

i^{3}=n

第四步:联立第二步第三步两式解方程:

t^{3}=n

t=\sqrt[3]{n}

所以得到时间复杂度为:

T=O(\sqrt[3]{n})

 例五:

y = 0;
while (y+1)*(y+1) <= n)
    y = y+1;


第一步:列出循环趟数t及y的最终值:

循环趟数t0123
y的最终值0123


第二步:找到t与y的关系:

t=y

第三步:确定循环停止条件:

(y+1)^{2}=n

第四步:联立第二步第三步两式解方程:

(t+1)^{2}=n

t=\sqrt{n}-1

所以得到时间复杂度为:

T=O(\sqrt{n})

两层循环时间复杂度计算

对于两层循环时间复杂度的计算,我们完全可以将这个循环视作一个单层循环,然后它的循环体是另一个循环。这样子计算起时间复杂度就很简单了

例题分析

例一:

int m=0,i,j;
for (i=1;i<=n;i++)
    for(j=1;j<=2*i;j++)
        m++;


第一步列出是第n次大循环

第二步列出在第n次大循环中内循环层语句能执行的次数:

第几次大循环123……n
第n次大循环中内循环语句的执行次数246……2n

注意上面这个都是每次大循环对应的内部循环语句执行次数,整个语句的时间复杂度还需要把每次大循环中内循环语句执行的次数相加起来

注意是将内循环语句次数相加

第三步 求和,写结果

2+4+...+2n=n(n+1)

T=O(n^{2})

 例二:

for (i=0;i<n;i++)
    for(j=0;j<m;j++)
        a[i][j] = 0;

第一步列出第n次大循环:

第二步列出第n次大循环中内层语句的执行次数:

第n次大循环123...n
第n次大循环中内层循环语句的执行次数mmmmm

注意上面这个都是每次大循环对应的内部循环语句执行次数,整个语句的时间复杂度还需要把每次大循环中内循环语句执行的次数相加起来


第三步 求和,写结果

注意是将内循环语句次数相加

T=O(m*n)

 例三:

count = 0;
for (k=1;k<=n;k*=2)
    for(j=1;j<=n;j++)
        count ++;

第一步列出第a次大循环:(注意这里k*=2)

第二步列出第a次大循环中内层语句的执行次数:

由于这里大循环能执行的次数不是一眼就能看出来的,所以还得先计算能执行多少次大循环

假设能执行t次大循环,则

k=2^{t-1}

k=n

n=2^{t-1}

t=log_{2}n+1

第a次大循环123……log_{2}n+1
k的值124……n
第a次大循环中内层语句的执行次数nnnnn

内层每个都是n,求和则可以得到:

注意是将内循环语句次数相加

内循环语句总的执行次数是

(log_{2}n+1)n

T=O(nlog_{2}n)

 例四:

for (i=n-1;i>=1;i--)
    for(j=1;j<=i;j++)
        if (A[j] > A [j+1])
            {//时间复杂度为O(1)的语句
}

第一步列出第t次大循环:

第二步列出第t次大循环中内层语句的执行次数:

第t次大循环123...n-1
第t次大循环中内层循环语句的执行次数n-1n-2n-3...1

第三步 求和,写结果

注意是将内循环语句次数相加

在本例中它就是个等差数列求和

(n-1) + (n-2) + \ldots + 2 + 1 = \frac{(n-1) \times n}{2}

所以时间复杂度就是

T=O(n^{2})

多层循环时间复杂度计算

实际上无论多少层循环,我们只需一层一层分开来计算就行了,最后只需将最内层的语句的执行次数全加起来就好了

例一:

for(i=0;i<=n;i++)
    for(j=0;j<=i;j++)
        for(k=0;k<j;k++)

要计算这个嵌套循环的时间复杂度,我们首先要分析每个循环的执行次数。

外层循环 for(i=0;i<=n;i++) 从 0 到 n,因此会执行 n+1 次。

对于每一个 i 的值,内层循环 for(j=0;j<=i;j++) 的执行次数从 0 到 i。具体来说:

  • 当 i = 0 时,内层循环执行 1 次(j 从 0 到 0)。
  • 当 i = 1 时,内层循环执行 2 次(j 从 0 到 1)。
  • 当 i = 2 时,内层循环执行 3 次(j 从 0 到 2)。
  • ...
  • 当 i = n 时,内层循环执行 n+1 次(j 从 0 到 n)。

因此,内层循环的总执行次数是 1 + 2 + 3 + ... + (n+1),这是一个等差数列的和,其和为 (\frac{(n+1)(n+2)}{2})。

对于每一个 j 的值,最内层的循环 for(k=0;k<j;k++) 的执行次数是从 0 到 j-1。这实际上是一个等差数列的前 j 项和,但我们要注意,这个内层循环对于每一个 j 都会执行,所以我们需要将它与 j 的所有可能值相乘。

具体来说,当 j 分别取 0, 1, 2, ..., n 时,最内层循环的执行次数分别是 0, 1, 3, ..., n(n-1)/2。因此,最内层循环的总执行次数是这些值的和。

为了计算这个总和,我们可以观察到一个事实:每一个 k 值在 j 从 k+1 到 n 的过程中都会被计算一次。因此,k=0 会被计算 n 次,k=1 会被计算 n-1 次,以此类推,直到 k=n-1 只被计算 1 次。

这个总和是另一个等差数列的和,其和为 \frac{n(n-1)}{2}

因此,整个嵌套循环的时间复杂度是外层循环次数 n+1 乘以最内层循环的总执行次数\frac{n(n-1)}{2},即(n+1) \times \frac{n(n-1)}{2}

简化后得到时间复杂度为 (O(n^3))。这是因为尽管有系数和较低阶的项,但在大 n 的情况下,(n^3) 项将主导整个表达式,因此时间复杂度是立方级的。

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

对于复杂的算法,我们可以将其分为几个容易计算的部分,然后再利用大O加法法则和乘法法则,计算算法复杂度

加法法则

T(n)=T1(n)+T2(n)=O(f(n))+O(g(n))=O(max(f(n),g(n))

乘法法则

T(n)=O(f(n)*g(n))

常见时间复杂度计算举例

实例1:

// 计算Func2的时间复杂度?
 void Func2(int N) 
{ int count = 0; 
for (int k = 0; k < 2 * N ; ++ k)
 { ++count; } 

int M = 10; 
while (M--) 
{ ++count; } 

printf("%d\n", count); 
}

 实例1基本操作执行了2N+10次,通过推导大O阶方法知道,时间复杂度为 O(N) 

实例2

// 计算Func3的时间复杂度?
 void Func3(int N, int M)
 { int count = 0; 
for (int k = 0; k < M; ++ k) 
{ ++count; } 

for (int k = 0; k < N ; ++ k) 
{ ++count; } 

printf("%d\n", count);
 }

实例2基本操作执行了M+N次,有两个未知数M和N,时间复杂度为 O(N+M) 。

假如说有条件:M远大于N,那么时间复杂度就是O(M),如果有条件:N远大于M,则时间复杂度就是O(N)。这里没有所以就是O(M+N)。

实例3

// 计算Func4的时间复杂度? 
void Func4(int N)
 { int count = 0; 
for (int k = 0; k < 100; ++ k) 
{ ++count; } 
printf("%d\n", count);
 }

 实例3基本操作执行了10次,通过推导大O阶方法,时间复杂度为 O(1)

实例4

// 计算strchr的时间复杂度?
const char * strchr ( const char * str, int character )
{
if(*str==character)
return str;

++str;
}

实例4基本操作执行最好1次,最坏N次,时间复杂度一般看最坏,时间复杂度为 O(N)

实例5 

// 计算BubbleSort的时间复杂度? 
void BubbleSort(int* a, int n) 
{ assert(a);

 for (size_t end = n; end > 0; --end)
 { int exchange = 0; 

for (size_t i = 1; i < end; ++i) 
{ 
if (a[i-1] > a[i]) 

{ Swap(&a[i-1], &a[i]); 
exchange = 1; 
} 

} 
if (exchange == 0)
 break;
} 
} 

实例5基本操作执行最好N次,最坏执行了(N*(N+1)/2次,通过推导大O阶方法+时间复杂度一般看最坏,时间复杂度为 O(N^2)

实例6 

// 计算BinarySearch的时间复杂度?
 int BinarySearch(int* a, int n, int x) 
{ 
assert(a);
 
int begin = 0; 
int end = n-1; 
while (begin < end)
 { int mid = begin + ((end-begin)>>1); 

if (a[mid] < x) 

begin = mid+1;

 else if (a[mid] > x) 

end = mid; 

else return mid;

 }
 return -1;
 }

实例6基本操作执行最好1次,最坏O(logN)次,时间复杂度为 O(logN).。

ps:logN在算法分析中表示是底数为2,对数为N。有些地方会写成lgN,但是不建议。(建议通过折纸查找的方式讲解logN是怎么计算出来的)

实例7

// 计算阶乘递归Fac的时间复杂度?

long long Fac(size_t N) 
{ if(0 == N) 

return 1;

 return Fac(N-1)*N; }

实例7通过计算分析发现基本操作递归了N次,时间复杂度为O(N) 

实例8

// 计算斐波那契递归Fib的时间复杂度?

 long long Fib(size_t N)

 { 
if(N < 3) 
return 1; 

return Fib(N-1) + Fib(N-2); }

实例8通过计算分析发现基本操作递归了2^N次,时间复杂度为O(2^N)。(建议画图递归栈帧的二叉树 讲解)

  • 42
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
无迹卡尔曼滤波(Unscented Kalman Filter,UKF)是一种可用于非线性系统的卡尔曼滤波方法。与传统的卡尔曼滤波方法相比,UKF通过一种称为无迹变换的技术,解决了传统卡尔曼滤波中求解雅可比矩阵的计算量大的问题。 无迹变换是UKF的核心概念之一。它通过选择一组特定的采样点(称为sigma点)来近似非线性系统的状态转移和观测函数。这些采样点通过对系统的均值和协方差矩阵进行变换得到。在UKF中,这些采样点会根据一定的规则被选取,通常是通过对状态变量进行扩展。每个采样点都会在状态转移和观测函数中进行计算,从而得到对状态和观测的估计值。 无迹卡尔曼滤波的步骤如下: 1. 根据系统的状态转移方程和观测方程,计算系统的均值和协方差矩阵。 2. 使用无迹变换方法,根据均值和协方差矩阵生成一组sigma点。 3. 对每个sigma点进行状态转移预测,得到预测状态的均值和协方差矩阵。 4. 对每个预测状态进行观测更新,得到修正后的状态估计值和协方差矩阵。 5. 根据观测值对状态进行更新,得到最终的状态估计值和协方差矩阵。 通过无迹卡尔曼滤波,我们可以在非线性系统中实现对状态的有效估计。相比于传统的卡尔曼滤波方法,UKF能够更好地适应非线性系统,并且无需计算雅可比矩阵,从而减少了计算的复杂度。<span class="em">1</span><span class="em">2</span><span class="em">3</span><span class="em">4</span> #### 引用[.reference_title] - *1* [卡尔曼滤波PPT 经典、无迹、扩展、多模型卡尔曼滤波](https://download.csdn.net/download/yyyh66024/12459288)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [【自动驾驶】学习卡尔曼滤波(三)——无迹卡尔曼滤波](https://blog.csdn.net/weixin_42301220/article/details/124708187)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* *4* [一文详解自适应无迹卡尔曼滤波(AUKF)](https://blog.csdn.net/weixin_45766278/article/details/131028011)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值