VB.net 2010 视频教程 VB.net 2010 视频教程 python基础视频教程
SQL Server 2008 视频教程 c#入门经典教程 Visual Basic从门到精通视频教程
当前位置:
首页 > temp > C#教程 >
  • c#把Fibonacci引出委托扩展和递推递归委托

制作者:剑锋冷月 单位:无忧统计网,www.51stat.net
 

  先回顾一个数列的概念:按一定次序排列的一列 数 称为数列...(请参见百度百科:数列)

  几个简单的数列:

      1, 1, 1, 1, 1, 1, 1...                //数列1
      0, 1, 2, 3, 4, 5, 6, 7...                //数列2
      0, 1, 4, 9, 16, 25, 36, 49...        //数列3

  通项公式的定义:数列的第n项与项的序数这间的关系,也就是数列生成算法

  上面几个数列可表示为

       An = F(n) = 1
       An = F(n) = n
       An = F(n) = n * n

  有了数列和通项公式的定义,我们的任务就好描述了:

  用最简洁的代码描述通项公式,用最简洁算法生成数列的前N个数。

  在此要求下,用常规代码是做不到简洁的,这里我们用lambda表达式描述通项公式:

        //数列1 通项公式
        public static Func<int, int> fun1 = n => 1;
        //数列2 通项公式
        public static Func<int, int> fun2 = n => n;
        //数列3 通项公式
        public static Func<int, int> fun3 = n => n * n;

  lambda表达式是不是与数学公式很像啊!

  再来看生成算法,这里用了一个不一般的扩展:

        /**//// <summary>
        /// 生成队列的前count项
        /// </summary>
        /// <param name="func">通项公式</param>
        /// <param name="count">生成的数量</param>
        /// <returns>队列前count项</returns>
        public static IEnumerable<int> GetSequence(this Func<int, int> func, int count)
        {
            for (int i = 0; i < count; i++) yield return func(i);
        }

编缉推荐阅读以下文章

  • c#扩展方法奇思妙用变态篇四:string 的翻身革命
  • c#扩展方法奇思妙用变态篇三:switch/case组扩展
  • c#扩展方法奇思妙用变态篇二:封装 if/else、swith/case及while
 

  相信大家见的扩展大多针对类(object, string)、接口(IEnumerable<T>)进行扩展,针对Func(委托)估计对大多数人来说都是第一次。

  这个扩展就是标题中说的“委托扩展”,感觉很怪吧,很别扭吧,很别管太多,看看怎么调用吧:

        public static void Test1()
        {
            int[] ints1 = fun1.GetSequence(10).ToArray();      //1, 1, 1, 1
            int[] ints2 = fun2.GetSequence(10).ToArray();      //0, 1, 2, 3
            int[] ints3 = fun3.GetSequence(10).ToArray(); ;    //0, 1, 4, 9
        }

  自我感觉比较简洁,而且将生成数列(GetSequence)与数列算法(通项公式)分开,也达到了生成数列(GetSequence)的复用。

  上面几个数列比较简单,现在来看Fibonacci,

  0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55...

  用图形表示如下:

c#扩展方法奇思妙用变态篇一:由Fibonacci数列引出“委托扩展”及“递推递归委托”

  这个序列在大家学习c语言递推递归时都接触过,这个序列很神奇,请参看维基百科:斐波那契数列

  它的通项公式是 An = F(n) = n                          n =0, 1

  F(n-1) + F(n-2)      n>1

  注意:关于这数列有的是从n从0开始,有的是从1开始,这里不计较。

  递推递归算法如下,容易理解效率确很低!!

        public static int GetFibonacci(int n)
        {
            if (n > 1) return GetFibonacci(n - 1) + GetFibonacci(n - 2);
            else return n;
        }

编缉推荐阅读以下文章

  • c#扩展方法奇思妙用变态篇四:string 的翻身革命
  • c#扩展方法奇思妙用变态篇三:switch/case组扩展
  • c#扩展方法奇思妙用变态篇二:封装 if/else、swith/case及while
 

  本文是为了引出递推递归委托,暂不是算法的效率

  下面就要大(改)变(形)态了。

  不考虑 <1 的情况

  public static Func<int, int> Fibonacci = n => Fibonacci(n - 1) + Fibonacci(n - 2);

  与数学通项式对比一下,何其相似!这就是我们的“递推递归委托”!

  考虑所有情况,完成Fibonacci,如下

  public static Func<int, int> Fibonacci = n => n > 1 ? Fibonacci(n - 1) + Fibonacci(n - 2) : n;

  实在感叹c#精简的语法,一句代码可以表示一个递推递归!

  调用测试下吧!

        public static void Test2()
        { 
            //委托扩展方法 + 递推递归委托
            int[] fibonacciSequence = Fibonacci.GetSequence(12).ToArray();
        }

  当然这个生成算法效率不是一般的低!

  最后给出一个数学推导出的精确算法

        public static Func<int, int> Fibonacci2 = n => (int)(5.0.Pow(-0.5) * ((0.5 * (1 + 5.0.Pow(0.5))).Pow(n + 1) - (0.5 * (1 - 5.0.Pow(0.5))).Pow(n + 1)));
        //Pow扩展,简化调用
        public static double Pow(this double x, double y)
        {
            return Math.Pow(x, y);
        }

  一点意见:像这样代码,最好是给封装起来,否则会很麻烦的。

  这篇文章是给极少数人看的(启发一下),看完后封装好给大多数人用。这是也“变态篇”系列文章的宗旨.

编缉推荐阅读以下文章

  • c#扩展方法奇思妙用变态篇四:string 的翻身革命
  • c#扩展方法奇思妙用变态篇三:switch/case组扩展
  • c#扩展方法奇思妙用变态篇二:封装 if/else、swith/case及while
 

  希望大家对 “委托扩展” 和 “递推递归委托”提些看法,名字定义不太好,请指正!

  ----------------------------------------------------------------------------------------------------------

  以下为追加内容

  ----------------------------------------------------------------------------------------------------------

  看了大家的回复,对我鼓励很大,于是我也忍不住把 装配脑袋 的算法改进了一下:

public static Func<Point, Point> f = p => { p.X += p.Y; p.Y = p.X - p.Y; return p;  };
public static Func<Point, Func<Point, Point>, int, Point> g1 = (p, f, n) =>  n > 0 ? g1(f(p), f, n - 1) : p;
public static Func<Point, Func<Point, Point>, int, Point> g2 = (p, f, n) =>  n > 0 ? f(g2(p, f, n - 1)) : new Point(0, 1);

public static void Test8()
{
    //测试 generate1
    Point p1 = g1(new Point(0, 1), f, 3);
    for (int i = 0; i < 12; i++)
        Console.WriteLine(g1(new Point(0, 1), f, i));
    //测试 generate2
    Point p2 = g2(default(Point), f, 3);
    for (int i = 0; i < 12; i++)
        Console.WriteLine(g2(default(Point), f, i));
}

  这里用到Point (System.Drawing中的),因为它能包含两个整数,Fibonacci又是前两项之和,所以...

  以下是调试运行的结果:

  输出

{X=0,Y=1}
{X=1,Y=0}
{X=1,Y=1}
{X=2,Y=1}
{X=3,Y=2}
{X=5,Y=3}
{X=8,Y=5}
{X=13,Y=8}
{X=21,Y=13}
{X=34,Y=21}
{X=55,Y=34}
{X=89,Y=55}

{X=0,Y=1}
{X=1,Y=0}
{X=1,Y=1}
{X=2,Y=1}
{X=3,Y=2}
{X=5,Y=3}
{X=8,Y=5}
{X=13,Y=8}
{X=21,Y=13}
{X=34,Y=21}
{X=55,Y=34}
{X=89,Y=55}

 

  两列Fibonacci,不过第二列刚开始不对。

  g1和g2是两种算法,看上去很相似,有什么不同呢,设个断点单步调试(F11)下吧!

  上面的代码还不够简洁,最后将f与g1合在一起,如下:

  public static Func<Point, int, Point> g3 = (p, n) => n > 0 ? g3(new Point(p.X + p.Y, p.X), n - 1) : p;

  测试调用代码如下:

Test9()
        public static void Test9()
        {
            //测试 generate3
            Point p3 = g3(new Point(0, 1), 3);
            for (int i = 0; i < 12; i++)
                Console.WriteLine(g3(new Point(1,0), i));
        }

  几种算法的优点和缺点大家来评判吧!

 

编缉推荐阅读以下文章

  • c#扩展方法奇思妙用变态篇四:string 的翻身革命
  • c#扩展方法奇思妙用变态篇三:switch/case组扩展
  • c#扩展方法奇思妙用变态篇二:封装 if/else、swith/case及while
 

  相信大家见的扩展大多针对类(object, string)、接口(IEnumerable<T>)进行扩展,针对Func(委托)估计对大多数人来说都是第一次。

  这个扩展就是标题中说的“委托扩展”,感觉很怪吧,很别扭吧,很别管太多,看看怎么调用吧:

        public static void Test1()
        {
            int[] ints1 = fun1.GetSequence(10).ToArray();      //1, 1, 1, 1
            int[] ints2 = fun2.GetSequence(10).ToArray();      //0, 1, 2, 3
            int[] ints3 = fun3.GetSequence(10).ToArray(); ;    //0, 1, 4, 9
        }

  自我感觉比较简洁,而且将生成数列(GetSequence)与数列算法(通项公式)分开,也达到了生成数列(GetSequence)的复用。

  上面几个数列比较简单,现在来看Fibonacci,

  0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55...

  用图形表示如下:

c#扩展方法奇思妙用变态篇一:由Fibonacci数列引出“委托扩展”及“递推递归委托”

  这个序列在大家学习c语言递推递归时都接触过,这个序列很神奇,请参看维基百科:斐波那契数列

  它的通项公式是 An = F(n) = n                          n =0, 1

  F(n-1) + F(n-2)      n>1

  注意:关于这数列有的是从n从0开始,有的是从1开始,这里不计较。

  递推递归算法如下,容易理解效率确很低!!

        public static int GetFibonacci(int n)
        {
            if (n > 1) return GetFibonacci(n - 1) + GetFibonacci(n - 2);
            else return n;
        }

编缉推荐阅读以下文章

  • c#扩展方法奇思妙用变态篇四:string 的翻身革命
  • c#扩展方法奇思妙用变态篇三:switch/case组扩展
  • c#扩展方法奇思妙用变态篇二:封装 if/else、swith/case及while


相关教程