生活中,如果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循环,递归和尾递归的对比的更多相关文章

  1. 递归与尾递归(C语言)

    原文:递归与尾递归(C语言)[转] 作者:archimedes 出处:http://www.cnblogs.com/archimedes/ 本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留 ...

  2. ArrayList和LinkedList的几种循环遍历方式及性能对比分析(转)

    主要介绍ArrayList和LinkedList这两种list的五种循环遍历方式,各种方式的性能测试对比,根据ArrayList和LinkedList的源码实现分析性能结果,总结结论. 通过本文你可以 ...

  3. ArrayList和LinkedList的几种循环遍历方式及性能对比分析

    最新最准确内容建议直接访问原文:ArrayList和LinkedList的几种循环遍历方式及性能对比分析 主要介绍ArrayList和LinkedList这两种list的五种循环遍历方式,各种方式的性 ...

  4. ArrayList和LinkedList的几种循环遍历方式及性能对比分析(转载)

    原文地址: http://www.trinea.cn/android/arraylist-linkedlist-loop-performance/ 原文地址: http://www.trinea.cn ...

  5. HashMap循环遍历方式及其性能对比(zhuan)

    http://www.trinea.cn/android/hashmap-loop-performance/ ********************************************* ...

  6. HashMap循环遍历方式及其性能对比

    主要介绍HashMap的四种循环遍历方式,各种方式的性能测试对比,根据HashMap的源码实现分析性能结果,总结结论.   1. Map的四种遍历方式 下面只是简单介绍各种遍历示例(以HashMap为 ...

  7. 【转】ArrayList和LinkedList的几种循环遍历方式及性能对比分析

    原文网址:http://www.trinea.cn/android/arraylist-linkedlist-loop-performance/ 主要介绍ArrayList和LinkedList这两种 ...

  8. linux循环递归设置权限

    这里给出一个循环递归得到对文件夹和文件分别有效的设置方法: find /path -type f -exec chmod 644 {} \; #对目录和子目录里的文件 find /path -type ...

  9. Java 集合 ArrayList和LinkedList的几种循环遍历方式及性能对比分析 [ 转载 ]

    Java 集合 ArrayList和LinkedList的几种循环遍历方式及性能对比分析 @author Trinea 原文链接:http://www.trinea.cn/android/arrayl ...

随机推荐

  1. ArrayAccess 接口(源码)

    The ArrayAccess interface (PHP 5 >= 5.0.0, PHP 7) Introduction Interface to provide accessing obj ...

  2. web自动化环境配置

    1.下载chrome浏览器对应版本的驱动 下载地址:https://npm.taobao.org/mirrors/chromedriver 2.下载后将chromedriver放到python安装路径 ...

  3. springboot之整合基本的jdbc并操作Mysql数据库

    对于数据访问层,无论是SQL还是NOSQL,springboot默认采用整合spring data方式进行统一处理,添加大量自动配置,屏蔽了许多设置,引入各种xxxTemplate,xxxReposi ...

  4. 漏洞利用:验证绕过,XSS利用,Cookic盗用,文件上传

    1.      文件上传 低级别 写好上传的内容 选择好上传的文件 上传成功. 测试:访问文件,执行代码 中级别 修改文件后缀为png 上传该文件 抓包修改文件后缀为php,然后允许数据包通过. 上传 ...

  5. Unreal Engine 4 蓝图完全学习教程(四)—— 变量与计算

    Ⅰ.值的基础类型 ①文本.字符串(Text.String):文本类型的值. ②整型.浮点型(Int.Float):数字类型的值. ③布尔型(Bool):表示“真或假”二者选其一的状态. Ⅱ.加法运算 ...

  6. 当你的程序在朋友的机器上显示丢失msvcr100d.dll的时候

    0. 给朋友发了个DEMO,收到提示:丢失 msvcr100d.dll 1. 一看是运行库文件,赶紧让朋友下载并安装vc++ 2010 redistribution,朋友反馈还是提示丢失这个dll文件 ...

  7. 转:RBAC如何设计一个权限系统

    前言 权限管理是所有后台系统的都会涉及的一个重要组成部分,主要目的是对不同的人访问资源进行权限的控制,避免因权限控制缺失或操作不当引发的风险问题,如操作错误,隐私数据泄露等问题.目前在公司负责权限这块 ...

  8. Oracle11G DG 搭建及管理

    一.准备工作 环境准备 主数据库Oracle Database安装 备服务器Oracle Database software 安装 二.正式配置 三.基本管理 -------------------- ...

  9. VFP执行 SQL Server 储存过程示例

    PUBLIC errvalPUBLIC errmsgPUBLIC handleerrval=0errmsg=' ' *Sql Server 连接参数sourcename= 'test'user= 's ...

  10. FastDFS 配置文件 client.conf storage_ids.conf

    client.conf : # connect timeout in seconds # default value is 30s connect_timeout=30              连接 ...