等差数列,for循环,递归和尾递归的对比
生活中,如果1+2+3+4.....+100,大家基本上都会用等差数列计算,如果有人从1开始加,不是傻就是白X,那么程序中呢,是不是也是这样。今天无意中看到了尾递归,以前也写过,但是不知道这个专业名词,今天写一下对比下性能问题。
今天主要是看到了尾递归,所以联想到了这些,写下这篇文章,其中也把Benchmark (Nuget上的BenchmarkDotNet)的基准测试用了下,感觉比较好用,赞。Benchmark 需要在release下运行。
原则上所有的递归操作,都可以写成循环操作。尾递归是指,在函数返回的时候,调用自身本身,并且return语句不能包含表达式。这样编译器或者解释器就可以把尾递归做优化,试运行速度更快。
测试类
public class TestClass
{
/// <summary>
/// for循环
/// </summary>
/// <param name="n"></param>
/// <param name="counter"></param>
/// <returns></returns>
public int TestFor(int n)
{
int s = ; for (int i = ; i < n + ; i++)
{
s = s + i;
} return s;
} /// <summary>
/// 等差数列
/// </summary>
/// <param name="n"></param>
/// <param name="counter"></param>
/// <returns></returns>
public int TestForG(int n)
{
int s = ( + n) * n / ; return s;
} /// <summary>
/// 递归
/// </summary>
/// <param name="n"></param>
/// <param name="counter"></param>
/// <returns></returns>
public int TestRec(int n)
{
if (n == ) return ;
return n + TestRec(n - );
} /// <summary>
/// 尾递归
/// </summary>
/// <param name="n"></param>
/// <param name="counter"></param>
/// <returns></returns>
public int TestTail(int n)
{
return TestTail(, n);
} public int TestTail(int sum, int n)
{
if (n == ) return sum;
sum = sum + n;
return TestTail(sum, n - );
}
}
基准测试
[SimpleJob(RuntimeMoniker.NetCoreApp30)]
[RPlotExporter]
public class TestClassForBenchmark
{
private readonly TestClass testClass = new TestClass(); [Params(,,,)]
public int N; [Benchmark]
public void TestFor()
{
testClass.TestFor(N);
} [Benchmark]
public void TestForG()
{
testClass.TestForG(N);
} [Benchmark]
public void TestRec()
{
testClass.TestRec(N);
} [Benchmark]
public void TestTail()
{
testClass.TestTail(N);
} }
Main程序调用
BenchmarkRunner.Run<TestClassForBenchmark>();
结果

用Benchmark的基准测试发现,运行时间:等差 < for < 尾递归(接近for) < 递归,for的运行速度比递归快,但是递归结构比较清晰,容易理解。
发现TestForG有点问题,接下来自己简单测试
实际用Stopwatch测试
TestClass testClass = new TestClass(); Stopwatch stopSwitch = new Stopwatch();
int n = ;
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}");
Stopwatch测试结果
. 10次
结果:,TestFor时间:
结果:,TestForG时间:
结果:,TestRec时间:
结果:,TestTail时间: .
结果:,TestFor时间:
结果:,TestForG时间:
结果:,TestRec时间:
结果:,TestTail时间:
.
结果:,TestFor时间:
结果:,TestForG时间:
结果:,TestRec时间:
结果:,TestTail时间:
.
结果:,TestFor时间:
结果:,TestForG时间:
结果:,TestRec时间:
结果:,TestTail时间:
.
结果:,TestFor时间:
结果:,TestForG时间:
结果:,TestRec时间:
结果:,TestTail时间:
结论
1. for的运行速度比递归快,但是递归结构比较清晰,容易理解。
2. 等差计算不一定比for循环快
斐波那契数列对比
/// <summary>
/// 循环实现 counter:运行次数
/// </summary>
public long Fib(int n, ref int counter)
{
if (n < ) return ;
long a = , b = ;
long temp;
for (int i = ; i <= n; i++)
{
counter++;
temp = a;
a = b;
b = temp + b;
} return b;
} /// <summary>
/// 递归实现
/// </summary>
public long FibRec(int n, ref int counter)
{
counter++;
if (n < ) return ;
if (n < ) return ;
return FibRec(n - , ref counter) + FibRec(n - , ref counter);
} /// <summary>
/// 尾递归实现
/// </summary>
public long FibTailRec(int n, ref int counter)
{
if (n < ) return ;
if (n < ) return ;
return FibRec(, , n, ref counter);
} public long FibRec(long last, long prev, int n, ref int counter)
{
counter++; long temp = last + prev;
if (n == ) return temp;
last = prev;
prev = temp; return FibRec(last, prev, n - , ref counter);
}
效果

counter是运行的次数,斐波那契数列直接用递归,n=100都直接堆栈溢出。递归用的好了,思路清晰,用的不好的话,数据稍微大点就是深深的坑。用递归尽量优化为尾递归,也就是返回的时候调用自身,不要有表达式。
等差数列,for循环,递归和尾递归的对比的更多相关文章
- 递归与尾递归(C语言)
原文:递归与尾递归(C语言)[转] 作者:archimedes 出处:http://www.cnblogs.com/archimedes/ 本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留 ...
- ArrayList和LinkedList的几种循环遍历方式及性能对比分析(转)
主要介绍ArrayList和LinkedList这两种list的五种循环遍历方式,各种方式的性能测试对比,根据ArrayList和LinkedList的源码实现分析性能结果,总结结论. 通过本文你可以 ...
- ArrayList和LinkedList的几种循环遍历方式及性能对比分析
最新最准确内容建议直接访问原文:ArrayList和LinkedList的几种循环遍历方式及性能对比分析 主要介绍ArrayList和LinkedList这两种list的五种循环遍历方式,各种方式的性 ...
- ArrayList和LinkedList的几种循环遍历方式及性能对比分析(转载)
原文地址: http://www.trinea.cn/android/arraylist-linkedlist-loop-performance/ 原文地址: http://www.trinea.cn ...
- HashMap循环遍历方式及其性能对比(zhuan)
http://www.trinea.cn/android/hashmap-loop-performance/ ********************************************* ...
- HashMap循环遍历方式及其性能对比
主要介绍HashMap的四种循环遍历方式,各种方式的性能测试对比,根据HashMap的源码实现分析性能结果,总结结论. 1. Map的四种遍历方式 下面只是简单介绍各种遍历示例(以HashMap为 ...
- 【转】ArrayList和LinkedList的几种循环遍历方式及性能对比分析
原文网址:http://www.trinea.cn/android/arraylist-linkedlist-loop-performance/ 主要介绍ArrayList和LinkedList这两种 ...
- linux循环递归设置权限
这里给出一个循环递归得到对文件夹和文件分别有效的设置方法: find /path -type f -exec chmod 644 {} \; #对目录和子目录里的文件 find /path -type ...
- Java 集合 ArrayList和LinkedList的几种循环遍历方式及性能对比分析 [ 转载 ]
Java 集合 ArrayList和LinkedList的几种循环遍历方式及性能对比分析 @author Trinea 原文链接:http://www.trinea.cn/android/arrayl ...
随机推荐
- DWZ框架-- Dialog点击保存后不能自动关闭
案例 今天在用DWZ框架做添加功能时,发现在对话框保存成功后,后端返回正确的json格式,但对话框不能自动关闭窗口,而且保存后自动跳回主页,没有停留在当前用户列表页面,具体错误过程重现如下: 1.打开 ...
- 时序数据库 Apache-IoTDB 源码解析之前言(一)
IoTDB 是一款时序数据库,相关竞品有 Kairosdb,InfluxDB,TimescaleDB等,主要使用场景是在物联网相关行业,如:车联网.风力发电.地铁.飞机监控等等,具体应用案例及公司详情 ...
- Java中的代码点与代码单元
在Java中,什么是代码点与代码单元? 代码点(Code Point):在 Unicode 代码空间中的一个值,取值 U+0000 至 U+10FFFF,代表一个字符. 其中U+0000到U+FFFF ...
- StackExchange.Redis 之 hash 类型示例
StackExchange.Redis 的组件封装示例网上有很多,自行百度搜索即可. 这里只演示如何使用Hash类型操作数据: // 在 hash 中存入或修改一个值 并设置order_hashkey ...
- HDU 6602 Longest Subarray (线段树)
题意: 1e5的数组,c(1e5)种数字求最长的子串,使得其中每个出现的数字出现的次数为0次或者大于k次 思路: 枚举右端点i,维护当前右端点时,每个左端点的可行元素数量,当且仅当可行元素为c时更新答 ...
- SPH液面重构过程中的问题
使用粒子方法进行流体特效模拟需要进行液面重构,构造出流体的自由表面,液面重构方法也是一个独立的研究方向,针对其的研究已经有了很多成果,包括液面的平滑度.精度和并行效率等. 在这里,主要是记录一下我在液 ...
- Spring学习笔记:自动创建Proxy
为什么需要自动创建Proxy 手动为所有需要代理的类用ProxyFactoryBean创建代理Proxy需要大量的配置. 这样如果需要代理的类很多,配置就很繁琐,而且也不便于xml配置的维护. 因此S ...
- 《自拍教程13》Windows的常用命令
这些是Windows系统自带的常用DOS命令集合, 先大概了解下,当然如果能熟练掌握那最好了. 后续思维篇,思维篇还会结合不通的测试场景, 届时将列出这些命令更详细的使用描述. table.dataf ...
- Apache httpd.conf配置文件 3(虚拟主机)
### Section 3: Virtual Hosts 第三部分 虚拟主机 注意:在使用虚拟主机前,请先检查 http.conf 的 辅助配置文件httpd-vhosts.conf 是否注释 # ...
- 前缀和&差分
一:差分数组概念 一.差分数组的定义及用途 1.定义:对于已知有n个元素的数列d,建立记录它每项与前一项差值的差分数组f:显然,f[1]=d[1]-0=d[1];对于整数i∈[2,n],我们让f[i ...