等差数列,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 ...
随机推荐
- javase第一章(了解java)
------------恢复内容开始------------ java介绍 java这门语言,如果你是一名IT从业者,那么就一定是会有所耳闻的,毕竟,这是编程史上其商业化最成功的一门语言,当然, 编程 ...
- VS下解决_CRT_SECURE_NO_WARNINGS 警告
1.带有警告的文件加 #define _CRT_SECURE_NO_WARNINGS 2.右击工程 - 属性 - 配置属性 - C/C++ - 命令行 命令行增加 /D _CRT_SECURE_NO ...
- java架构之路-(netty专题)netty的基本使用和netty聊天室
上次回顾: 上次博客,我们主要说了我们的IO模型,BIO同步阻塞,NIO同步非阻塞,AIO基于NIO二次封装的异步非阻塞,最重要的就是我们的NIO,脑海中应该有NIO的模型图. Netty概念: Ne ...
- ARTS Week 8
Dec 16, 2019 ~ Dec 22, 2019 Algorithm Problem 53 Maximum Subarray 最大子数组 题目链接 题目描述:给定一个数组,在所有连续的子数组中, ...
- LeetCode 127. Word Ladder 单词接龙(C++/Java)
题目: Given two words (beginWord and endWord), and a dictionary's word list, find the length of shorte ...
- 【题解】P1559 运动员最佳匹配问题
[题目](https://www.luogu.com.cn/problem/P1559) 题目描述 羽毛球队有男女运动员各n人.给定2 个n×n矩阵P和Q.P[i][j]是男运动员i和女运动员j配对组 ...
- centos7 上为php-fpm安装gd扩展库
转自:https://blog.csdn.net/liyyzz33/article/details/89166110 首先查看自己当前php的版本 php -v PHP 5.6.40 查看yum中是否 ...
- 动态规划------背包问题(c语言)
/*背包问题: 背包所能容纳重量为10:共五件商品,商品重量用数组m存储m[5]={2,2,6,5,4}, 每件商品的价值用数组n存储,n[5]={6,3,5,4,6};求背包所能装物品的最大价值. ...
- pymongo(看后转载,在原基础上添加了类连接和简单调用)
一.MongoDB 数据库操作 1. 连接数据库 import pymongo conn = pymongo.Connection() # 连接本机数据库 # conn = pymongo.Conne ...
- Mac解决:xcode-select: error: command line tools are already installed, use "Software Update" to install updates
1.因为node项目终端报错: No receipt for 'com.apple.pkg.CLTools_Executables' found at '/'. No receipt for 'com ...