算法效率与程序优化Word文件下载.docx

上传人:b****5 文档编号:19786600 上传时间:2023-01-10 格式:DOCX 页数:43 大小:86.19KB
下载 相关 举报
算法效率与程序优化Word文件下载.docx_第1页
第1页 / 共43页
算法效率与程序优化Word文件下载.docx_第2页
第2页 / 共43页
算法效率与程序优化Word文件下载.docx_第3页
第3页 / 共43页
算法效率与程序优化Word文件下载.docx_第4页
第4页 / 共43页
算法效率与程序优化Word文件下载.docx_第5页
第5页 / 共43页
点击查看更多>>
下载资源
资源描述

算法效率与程序优化Word文件下载.docx

《算法效率与程序优化Word文件下载.docx》由会员分享,可在线阅读,更多相关《算法效率与程序优化Word文件下载.docx(43页珍藏版)》请在冰豆网上搜索。

算法效率与程序优化Word文件下载.docx

便十分重要。

可利用计数足够一定次数后再统一MOD循环完后再MOD使用中间变量记录MOD结果等

方法减少次数。

在高精度计算中,许多人喜欢边运算边整理移位,从而在内层循环中有除、模运算各一次,效率

极低。

应该利用int的数据范围,先将计算结果保存至当前位,在各位的运算结束后再统一整理。

以下是用统一整理法编写的高精度乘法函数,规模为10000位。

inta[10000]={0},b[10000]={0},c[10000]={0};

voidmul()

{inti,j,t;

for(i=0;

iv10000;

i++)

for(j=0;

jv10000-i;

j++)

c[i+j]+=a[i]*b[j];

i<

9999;

{c[i+1]+=c[i]/10;

c[i]%=10;

}

以上函数运行后,平均用时。

以下是边运算边整理的程序。

10000;

j<

10000-i;

{c[i+j+1]+=(c[i+j]+a[i]*b[j])/10;

c[i+j]=(c[i+j]+a[i]*b[j])%10;

统一整理与边整理边移位相比,快了倍,有明显优势。

故尽量减少

除法、取模运算的次数,是从常数级别降低时间复杂度的方法。

(7)大小比较

if(x>

y)x=y;

净运行时间,与加法运算速度相当。

故比较运算也属于较快的基本运算。

二、位运算的速度

(1)左移、右移

x<

<

1;

x>

>

净运行时间无法测岀,证明位运算速度极快。

而使用自乘计算需要,自除运算需要,所以尽可能

使用位运算代替乘除。

(2)逻辑运算

t=x|y;

t=xAy;

t=x&

y;

净运行时间约30ms,比加法运算(约40ms)快较多,是因为全是按二进制位计算。

但加减与位运

算关系并不大,所以利用位运算主要是利用左右移位的高速度。

三、数组运算的速度

(1)直接应用数组

for(k=0;

k<

k++)

t=q[k];

净运行时间。

这里计算了内层循环的时间。

若改为

0;

t=q[0];

则净运行时间为,很快,与的循环时间相比,可以忽略。

故应用数组,速度很快,不必担心数组寻址耗时。

同时我们发现,循环耗时在各种运算中是很大的,仅次于乘除,故我们要尽量减少循环次数,能在一个循环中解决的问题不放在两个循环中,减少循环变量次数。

(2)二维数组

5000;

t=z[i][k];

实际运行时间为,若规模扩至10000则10s内无法岀解,由于频繁访问虚拟内存。

可以试想,若物

理内存足够大,则运行时间约为320ms,仅为的基准运行时间的3/2,差距似乎并不是很大;

由此推得

其净运行时间约为120ms。

但相较加、减等简单操作,速度仍为3倍,尤其与几乎不需时间的一维数组

相比差距巨大。

尤其是在计算中,二维数组元素经常调用,时间效率不可忽视。

所以,对于已知数目不多的同样大小的数组,可用几个变量名不同的一维数组表示,如x、y方向,两种不同参数,而不要

滥用二维数组。

在滚动数组中,可用两个数组交替计算的方式,用二维数组同样较慢。

四、实数运算的速度

测试方法与“基本运算”类似。

运算符

=

+

-

*

/

%

longint

int64

double

--

由上表可见,涉及乘除、取模时int64很慢,要慎用;

int显然最快,但对大数据要小心越界。

一组变量中既有超岀int的,又有不超过int的,则要分类处理,不要直接都定义成int64,尤其在乘

除模较多的高精度过程中。

以上讨论了主要基本运算的速度问题。

概括起来说,除、模最慢,二维数组较慢,加减乘、逻辑位运算、比较大小较快,左右移位、一维数组、赋值几乎不需要时间。

而循环for或while语句十分特

殊,它的运算速度大于判断大小、自加控制变量所用时间之和,无论采用内部if判断退岀,还是在入

口处判断,都回用去约200ms的时间。

所以尽量减少循环次数,是优化算法的关键。

对于双层或多层的

循环,应把循环次数少的放在最外层,最大的循环位居最内部,以减少内层循环的执行次数。

第二章各种算法的速度

一、排序算法的速度

1.冒泡排序

20000;

a[i]=rand();

s=clock();

i<

n;

i;

if(a[i]>

a[j])

b=clock();

运行时间:

1407ms

2.选择排序

n;

{max=0;

if(a[j]>

a[max])

max=j;

b[i]=a[max];

a[max]=-1000000;

t=clock();

1220ms

3.插入排序

{t=a[i];

for(j=i-1;

j>

=0;

j--)

t)

break;

for(l=i;

l>

j+1;

l--)

a[l]=a[l-1];

a[j+1]=t;

984ms

以上三种都是0(n^2)的排序,其中插入排序最快,且可以用指针加以优化。

从编程复杂度上,冒泡排序最简单。

从算法的稳定性上,插入排序是稳定的,即排序后关键字相同的记录顺序不改变,特别适用于表达式处理等问题。

一般的选择排序是不稳定的,但这里给出的程序由于使用了人类最原始的方法,即依次选择最大的并排除,故是稳定的。

冒泡排序是不稳定的,涉及必须保持数据原顺序的题目时不能选择冒泡排序,而必须选择稳定的排序方式。

以下试验所采用的环境是:

CPUIntelCore*2,内存512M,操作系统Windows7UltimateBeta,

程序语言C。

编译环境Dev-c++,以下称为2号机。

由于CPU速度较慢,且操作系统占用资源较多,程序运行速度明显减慢,第一章的“基本运行测试”需要时间约为前者的2倍,即为406ms。

故第一章的

程序运行时间此处应乘2。

4.快速排序的标准版

#defineMAX

inta[MAX];

intp(intl,intr)

{intx=a[l],i=l-1,j=r+1,t;

while⑴

{do{--j;

}while(a[j]<

x);

do{++i;

}while(a[i]>

if(i<

j)

a[i]=a[j];

a[j]=t;

elsereturnj;

voidq(intl,intr)

{intn;

if(l<

r)

{n=p(l,r);

q(l,n);

q(n+1,r);

2948ms。

注意:

不要以为三种平方级排序方法的速度与快速排序可比拟,因为平方级的数据范围是10000,而快速排序的范围是。

对于10000的数据,快速排序只需。

另外,快速排序不是

稳定的排序,需要保持原顺序的不能用此法。

voidp(intl,intr)

r){

while

(1)

elsebreak;

P(l,j);

P(j+1,r);

若程序改为以上形式,则运行时间为2917ms,稍快了一些,是因为减少了函数调用次数。

对于函

数调用,我们进行这样的测试。

由此可见,调用函数本身并不浪费时间,仅相当于循环本身时间400ms的1/40,相当于加法80ms

的1/8,是很快的运算。

但由于在函数内部需要进行现场保护,调用系统堆栈,所以用时大幅增加,定义变量后只是一个自增运算就用去120ms,相当于主程序中加法运算时间的3/2倍。

故函数中的运算比

主程序中要慢,尤其是反复调用函数,会增加不必要的时间开销。

所以,一些简单的功能尽量在一个函数或主程序内完成,不要使用过多的函数;

涉及全局的变量不要在函数调用时由接口给岀,再返回值,尽量使用全局变量。

这些方法可能使程序的可读性降低,不利于调试,但有利于提高时效,正如汇编语言程序比高级语言快一样。

5.优化的快速排序

(1)用插入排序优化

由于递归调用浪费大量时间,本算法的思想是,当首尾间距小于min时,改用效率较高的插入排

序,减少反复递归。

这个想法是好的,但运行效果并不如人意。

当min=4时,程序运行时间降为

2901ms,优化幅度不大,且增加了编程复杂度,故不宜采用。

其原因是递归调用、插入排序内部循环所用时间过长。

(2)用小数据判断优化

if(l+1<

while

(1){}

并排序

归并排序是一种稳定的排序方法,且时间效率与快速排序相同,都是0(nlogn)。

但归并排序比快

速排序的常数因子大,故快速排序还是最快的排序方法。

归并排序则适用于有特殊要求的题目,如不满足交换律的表达式处理。

inta[MAX],b[MAX];

voidcombine(intfrom,intto)

{inti,t,mid=(from+to)/2,f,r;

if(from==to||from+1==to)

return;

if(from+2==to)

{if(a[from]<

a[from+1])

{t=a[from];

a[from]=a[from+1];

a[from+1]=t;

combine(from,mid);

combine(mid,to);

f=from;

r=mid;

i=from;

while(f!

=mid||r!

=to)|

if(f!

=mid&

&

a[f]>

a[r])

b[i++]=a[f++];

elseb[i++]=a[r++];

for(i=from;

to;

a[i]=b[i];

调用:

combine(0,MAX);

归并排序算法还可用于统计逆序对数。

所谓逆序对,即为在一个数组a中,满足i<

j且a[i]>

a[j]

(或a[i]<

a[j],依排序方向而定)的点(i,j)的个数。

voidsort(intfrom,intto)

tot++;

sort(from,mid);

sort(mid,to);

=to)

else

{b[i++]=a[r++];

=mid)tot+=(mid-from);

主函数:

sort(0,n);

printf("

%d"

n*(n-1)/2-tot);

7.多关键字排序

多关键字排序,经常出现于要求按某要素排序姓名,该要素相同的按字典序排列的题目。

这样,此要素是第一关键字,姓名是第二关键字。

qist(O,n-1);

j=0;

for(i=1;

if(ist[lo[i]]!

=ist[lo[i-1]])

{qnameist(j,i-1);

j=i;

qnameist(j,n-1);

O(n)的

此算法的意思是,先按第一关键字快速排序,在从左至右扫描,找到每段相同的元素,再按第二关键字快速排序。

此算法复杂度仍为0(nlogn),且比两次快速排序要快,因为第二次排序已用

时间将其分为若干小块,排序效率大大提高。

8.字符串排序

voidqname(intl,intr)

{inti=l-1,j=叶1,t;

}while(strcmp(name[lo[j]],name[lo[i]])>

0);

}while(strcmp(name[lo[j]],name[lo[i]])<

{t=lo[i];

lo[i]=lo[j];

lo[j]=t;

qname(l,j);

qname(j+1,r);

上述排序方法,实质上是将普通快速排序中的数大小比较换成字符串大小比较。

为了避免字符串交换浪费时间,采用了类似指针的定位数组,只需交换定位数组的元素即可。

当然,用指针直接实现效率更高。

100;

{a[i]=rand();

b[i]=rand();

if(strcmp(a,b)==0)

1046ms,其中净运行时间约640ms。

这是对长度100的字符串进行10A8次比较的时

间。

故strcmp的效率即为约8次整数比较所需时间,比自己编写的函数快。

intstr(chara[],charb[])

{inti=0;

while(a[i]!

=0||b[i]!

=0)

{if(a[i]>

b[i])

return1;

if(a[i]<

return-1;

return0;

1131ms,净运行时间约725ms,相当于9次整数比较时间,比标准库函数稍慢。

显然,标准库函数是经过精心优化的,我们在有库函数可用时尽量用库函数,不仅降低编程复杂度,降低错误率,还能提高时间效率。

排序

#defineMOD999997源最短路径——dijkstra算法

voiddijkstra()

{inti,j,min=0;

i++)dis[i]=d[0][i];

{min=n;

if(!

use[j]&

d[i][j]<

d[i][min])

min=j;

use[min]=1;

if(dis[min]+d[min][j]<

dis[j])dis[j]=dis[min]+d[min][j];

随机数产生器:

MAX;

if(i!

=j)

d[i][j]=rand();

当MAX=2000时(即点数为2000个),运行时间:

当MAX更大时,内存会使用过多,故在1s时

限内至多运行规模为2000的单源最短路径35次。

2.单源最短路径一一Ford算法

intford()

{inti,j;

d[i]=MAXINT;

j<

num;

if(d[f[j]]+w[j]vd[e[j]])d[e[j]]=d[f[j]]+w[j];

if(d[f[j]]+w[j]<

d[e[j]])

①数据构造:

10*n条随机边。

当n=1000时,运行时间:

当n=10000时,运行时间:

6766ms。

②数据构造:

n*n条边的完全图。

当n=1000时,运行时间6469ms。

比Dijkstra和SPFA都慢很多,因为

其算法的理论时间复杂度就达到了O(VE),属于介于0(nA2)与0(nF)的算法。

但我们仍然需要这种算

法,因为当图中存在负权时,必须使用Ford算法。

同时,该算法还能判断图中是否存在负权回路(无

解)。

3.单源最短路径一一SPFA算法

structmising{

intnum;

intpoo;

structmising*p;

};

intwor[500000];

intdis[60001];

intyes[60001]={0};

intbeg=0,end=1;

main()

{

intn,m;

inta,b,c,i;

structmisingq[60001],*qq[60001],*x;

doubles,t;

FILE*fp;

fp=fopen("

"

"

r"

);

fscanf(fp,"

%d%d"

,&

n,&

m);

qq[i]=&

q[i];

m;

%d%d%d"

&

a,&

b,&

c);

a__;

b--;

qq[a]->

p=(structmising*)malloc(sizeof(structmising));

qq[b]->

p=(structmising*)malloc(sizeof(structmising));

qq[a]=qq[a]->

p;

qq[b]=qq[b]->

poo=b;

qq[b]->

poo=a;

num=qq[b]->

num=c;

qq[i]->

p=NULL;

dis[i]=-1;

yes[0]=1;

wor[0]=0;

dis[0]=0;

while(beg!

=end)

x=q[wor[beg]].p;

while(x匸NULL)

f(dis[x->

poo]==-1||(dis[x->

poo]>

x->

num+dis[wor[beg]]&

dis[x>

poo]!

=1)){

dis[x->

poo]=dis[wor[beg]]+x->

wor[end]=x->

poo;

if(yes[x>

poo]==O)

yes[x->

poo]=1;

end++;

x=x->

yes[wor[beg]]=0;

beg++;

dis[n-1]);

t=clock();

随机数生成模块:

fprintf(fp,"

%d%d\n"

n,10*n);

10*n;

{j=rand()%n+1;

k=rand()%n+1;

while(j==k){j=rand()%n+1;

%d%d%d\n"

j,k,rand());

运行时间(不含文件操作):

当n=100000时,运行时间1937ms;

当n=10000时,运行

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 外语学习 > 英语学习

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1