java循环while之等差数列均值_等差数列,for循环,递归和尾递归的对比

生活中,如果1+2+3+4.....+100,大家基本上都会用等差数列计算,如果有人从1开始加,不是傻就是白X,那么程序中呢,是不是也是这样。今天无意中看到了尾递归,以前也写过,但是不知道这个专业名词,今天写一下对比下性能问题。

今天主要是看到了尾递归,所以联想到了这些,写下这篇文章,其中也把Benchmark (Nuget上的BenchmarkDotNet)的基准测试用了下,感觉比较好用,赞。Benchmark 需要在release下运行。

原则上所有的递归操作,都可以写成循环操作。尾递归是指,在函数返回的时候,调用自身本身,并且return语句不能包含表达式。这样编译器或者解释器就可以把尾递归做优化,试运行速度更快。

测试类

a4ff2653a09270c2c8fbee4b9666e50fba4.jpg

df2b33d8a229a46c23dc871c791763fb7c8.jpg

public classTestClass

{///

///for循环///

///

///

///

public int TestFor(intn)

{int s = 1;for (int i = 2; i < n + 1; i++)

{

s= s +i;

}returns;

}///

///等差数列///

///

///

///

public int TestForG(intn)

{int s = (1 + n) * n / 2;returns;

}///

///递归///

///

///

///

public int TestRec(intn)

{if (n == 1) return 1;return n + TestRec(n - 1);

}///

///尾递归///

///

///

///

public int TestTail(intn)

{return TestTail(1, n);

}public int TestTail(int sum, intn)

{if (n == 1) returnsum;

sum= sum +n;return TestTail(sum, n - 1);

}

}

View Code

基准测试

81fb84e229113c8b5d8450b93eddab069f6.jpg

044c41735a9a3b3513e2de077671effeb22.jpg

[SimpleJob(RuntimeMoniker.NetCoreApp30)]

[RPlotExporter]public classTestClassForBenchmark

{private readonly TestClass testClass = newTestClass();

[Params(100,500,1000,5000)]public intN;

[Benchmark]public voidTestFor()

{

testClass.TestFor(N);

}

[Benchmark]public voidTestForG()

{

testClass.TestForG(N);

}

[Benchmark]public voidTestRec()

{

testClass.TestRec(N);

}

[Benchmark]public voidTestTail()

{

testClass.TestTail(N);

}

}

View Code

Main程序调用

BenchmarkRunner.Run();

结果

805c1fa660d8d4d0744424b56c2911f0.png

用Benchmark的基准测试发现,运行时间:等差 < for < 尾递归(接近for) < 递归,for的运行速度比递归快,但是递归结构比较清晰,容易理解。

发现TestForG有点问题,接下来自己简单测试

实际用Stopwatch测试

e37446edac048123a2243c779b3af6d02f3.jpg

a5b0854ccf151dd22c595d2c50895c19246.jpg

TestClass testClass = newTestClass();

Stopwatch stopSwitch= newStopwatch();int n = 5000;

stopSwitch.Start();int sum =testClass.TestFor(n);

stopSwitch.Stop();

Console.WriteLine($"结果:{sum},TestFor时间:{stopSwitch.ElapsedTicks}");

stopSwitch.Start();

sum=testClass.TestForG(n);

stopSwitch.Stop();

Console.WriteLine($"结果:{sum},TestForG时间:{stopSwitch.ElapsedTicks}");

stopSwitch.Restart();

sum=testClass.TestRec(n);

stopSwitch.Stop();

Console.WriteLine($"结果:{sum},TestRec时间:{stopSwitch.ElapsedTicks}");

stopSwitch.Restart();

sum=testClass.TestTail(n);

stopSwitch.Stop();

Console.WriteLine($"结果:{sum},TestTail时间:{stopSwitch.ElapsedTicks}");

View Code

Stopwatch测试结果

1. 10次

结果:55,TestFor时间:2024结果:55,TestForG时间:3799结果:55,TestRec时间:1603结果:55,TestTail时间:2371

2. 100结果:5050,TestFor时间:1704结果:5050,TestForG时间:2708结果:5050,TestRec时间:1069结果:5050,TestTail时间:1401

3. 500结果:125250,TestFor时间:1794结果:125250,TestForG时间:3096结果:125250,TestRec时间:9398结果:125250,TestTail时间:2332

4. 1000结果:500500,TestFor时间:2080结果:500500,TestForG时间:4147结果:500500,TestRec时间:2003结果:500500,TestTail时间:2540

5. 5000结果:12502500,TestFor时间:1428结果:12502500,TestForG时间:3982结果:12502500,TestRec时间:6815结果:12502500,TestTail时间:2799

结论

1. for的运行速度比递归快,但是递归结构比较清晰,容易理解。

2. 等差计算不一定比for循环快

斐波那契数列对比

///

///循环实现 counter:运行次数///

public long Fib(int n, ref intcounter)

{if (n < 1) return 0;long a = 1, b = 1;longtemp;for (int i = 3; i <= n; i++)

{

counter++;

temp=a;

a=b;

b= temp +b;

}returnb;

}///

///递归实现///

public long FibRec(int n, ref intcounter)

{

counter++;if (n < 1) return 0;if (n < 3) return 1;return FibRec(n - 1, ref counter) + FibRec(n - 2, refcounter);

}///

///尾递归实现///

public long FibTailRec(int n, ref intcounter)

{if (n < 1) return 0;if (n < 3) return 1;return FibRec(1, 1, n, refcounter);

}public long FibRec(long last, long prev, int n, ref intcounter)

{

counter++;long temp = last +prev;if (n == 3) returntemp;

last=prev;

prev=temp;return FibRec(last, prev, n - 1, refcounter);

}

效果

1e9ac175ff20356a056e68c4d19131cd.png

counter是运行的次数,斐波那契数列直接用递归,n=100都直接堆栈溢出。递归用的好了,思路清晰,用的不好的话,数据稍微大点就是深深的坑。用递归尽量优化为尾递归,也就是返回的时候调用自身,不要有表达式。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值