数据加载中……
Linq与斐波那契数列共舞

从学习算法开始就免不了递归实现一个有趣的题目——斐波那契数列。生于公元1170年的意大利数学家列昂纳多·斐波那契通过兔子的繁殖来引入这样一个数列:1,1,2,3,5,8,13,21……这个数列从第三项开始,每一项都等于前两项之和。它的通向公式:(1/√5)*{[(1+√5)/2]^n - [(1-√5)/2]^n}。如果用C语言等通过递归方式实现它非常的简单如下面所示:

1long fibonacci(long n)
2{
3    if(n == 0 || n == 1)
4        return n;
5    else
6        return fibonacci(n-1+ fibonacci(n-2);
7}
然而如果我们结合Linq的Lambda语句实现它又如何呢?这里必须澄清的是Lambda语句与Lambda表达式类似,只是语句在大括号中:
(parameters)=>{statement;}

和匿名方法一样,Lambda语句无法创建表达式目录树。由此引发了我们通过委托来帮助Lambda语句实现该数列的假想。先回顾一下之前委托调用的方法:

第一:通过明确定义匹配的函数原型
这也是最早的委托,先定义一个函数实体,然后相应的定义委托类型,最后通过声明一个变量来调用它,如下所示:

static double Square(double source)
{
   
return Math.Pow(source, 2);
}


delegate double CallMethod(double input);

CallMethod callMethod1 
= Square;
Console.WriteLine(callMethod1(
3));

第二:实例化泛型委托Func<T,TResult>(这种类型的委托还有好几个)
同样的要定义具体的函数,不同的是不需要定义具体的委托类型,而是通过实例化Func泛型委托来实现,如:

Func<doubledouble> callMethod2 = Square;
Console.WriteLine(callMethod2(
3));

第三:通过Func<T,TResult>与匿名方法一起使用
通过这种方式减轻了复杂度,并且提高了代码的可读性。如下:

Func<doubledouble> callMethod3 = delegate(double input) return Math.Pow(input, 2); };

第四:通过Lambda表达式或Lambda语句
这也是相对来说比较简单的方式,它通过泛型委托定义函数和Lambda表达式实现功能的方式来实现。

Func<doubledouble> callMethod4 = d => Math.Pow(d, 2);
Console.WriteLine(callMethod4(
3));

由此可知,泛型委托在Lambda实现上有着链接和协调的作用。因此在这个意义上讲我们通过两种泛型委托的实现第一种是Action<T1,T2,T3>,该委托用于封装一个方法,该方法采用三个参数并且没有返回值。第二种是Func<T1,TResult>,该委托封装一个具有一个参数并且返回TResult参数指定类型值的方法。必须应用System.Core.dll命名空间System;

根据命题,我们设定实现两种目标的函数,分别命名为Fibonacci_A和Fibonacci_B,前者实现给出前两个数打印出在n之前的斐波那契数,后者则实现打印出前n个斐波那契数。对于递归的方法两个函数的具体实现会有所不同。

Action<intintint> Fibonacci_A = (firstNum, secondNum, boundNum) => 
{
               
int exchangeNum = firstNum + secondNum;
               firstNum 
= secondNum;
               secondNum 
= exchangeNum ;

               
if (secondNum > boundNum)
               
{
                   
return;
               }

               
else
           
{
                   Console.WriteLine(exchangeNum );
                   MethodBase.GetCurrentMethod().Invoke(
nullnew object[] { firstNum, secondNum ,boundNum});
               }

           }
;

   Fibonacci_A(0,1,200);

           Func<int,int> Fibonacci_B = n =>
       
{
                   
if ((n == 0|| (n == 1))
                   
{
                       
return 1;
                   }

                   
return (Convert.ToInt32(MethodBase.GetCurrentMethod().Invoke(nullnew object[] { n - 2 })) 
                       
+Convert.ToInt32(MethodBase.GetCurrentMethod().Invoke(nullnew object[] { n - 1 })));
               }
;

           
for (int i = 0; i < 10; i++)
           
{
               Console.WriteLine(Fibonacci_B(i));
           }
为了实现委托自身的递归调用两个函数都通过反射来实现。

通过这两个简单的递归函数,让我们更加了解了Lambda语句的高级功能。以上的函数不一定是最简练的,希望有更好的方法的朋友把它贴出来,以之共勉。

posted on 2008-06-11 21:04 greater 阅读(1641) 评论(16)  编辑 收藏 所属分类: Linq

评论

#1楼  2008-06-11 21:14 James Dai [未注册用户]

int foo(int i)
{
if(i < 3)
return foo(1);
else
return foo(i) + foo(i-1);
}
    回复  引用    

#2楼  2008-06-11 21:50 zz22558 [未注册用户]

C#真的很麻烦
    回复  引用    

#3楼  2008-06-11 22:03 U2U      

感觉这样光玩形式,但是不实用的应用来传授技术,不利于程序员的成长。
    回复  引用  查看    

#4楼  2008-06-12 08:27 小强。假的 [未注册用户]

int foo(int i)
{
if(i < 3)
return foo(1);
else
return foo(i) + foo(i-1);
}
这个算法算一个 100 的出来。。你。。
    回复  引用    

#5楼  2008-06-12 10:02 信110 [未注册用户]

Action<int, int, int> Fibonacci_A = (second, first, count) =>
{
if (count == 0){
Console.Write(first);
return;
}
else
MethodBase.GetCurrentMethod().Invoke(null, new object[] { (second + first), second, (count - 1) });};

Fibonacci_A(1,0,200);
    回复  引用    

#6楼  2008-06-12 10:50 风海迷沙      

没看明白。递归?
    回复  引用  查看    

#7楼  2008-06-12 11:19 装配脑袋      

Lambda的递归为啥不用Y组合子,而要用反射?如果是这样Why Lambda?为什么斐波那契还要递归,而且是最差的O(2^N)型递归?一堆Why……
    回复  引用  查看    

#8楼  2008-06-12 11:28 fuadam [未注册用户]

linq在哪了
    回复  引用    

#9楼  2008-06-12 11:35 fuadam [未注册用户]

引用--------------------------------------------------
zz22558: C#真的很麻烦
------------------------------------------------------

Func<int, int> fib = null;
fib = n => n > 1 ? fib(n - 1) + fib(n - 2) : n;
fib(20);
    回复  引用    

#10楼  2008-06-12 15:50 560889223      

我怎么越看越有模板元编程的感觉....
    回复  引用  查看    

#11楼  2008-06-12 17:45 装配脑袋      

这是使用Y组合子来递归,用迭代型递归算法的例子。它是一个O(N)的算法,N稍微大一点就比LZ的快无数倍。同时满足跟Linq沾边的特点。LZ你参考一下吧。
class Program
{
    
static void Main(string[] args)
    {
        var fib 
= Y<intintintint>(self => (n, x, y) => n <= 1 ? y : self(n - 1, y, x + y));

        
for (int i = 0; i < 20; i++)
        {
            Console.WriteLine(fib(i, 
11));
        }

    }

    
static Func<T1, T2, T3, T> Y<T1, T2, T3, T>(Func<Func<T1, T2, T3, T>, Func<T1, T2, T3, T>> f)
    {
        
return (x, y, z) => f(Y(f))(x, y, z);
    }
}
    回复  引用  查看    

#12楼  2008-06-13 00:22 怪怪      

@装配脑袋
嘿嘿, 在外头看个标题就准备进来头晕了,因为估摸着又要看一次Y组合子的实现...

@560889223
两码事...
    回复  引用  查看    

#13楼  2008-06-13 00:40 fuadam [未注册用户]

还是用Memoize自然
    回复  引用    

#15楼 [楼主] 2008-07-20 21:03 greater      

@装配脑袋
谢谢!Y组合子
    回复  引用  查看    

#16楼  2008-09-04 12:57 Janja      

看着真别扭,不管是
class Program
{
static void Main(string[] args)
{
var fib = Y<int, int, int, int>(self => (n, x, y) => n <= 1 ? y : self(n - 1, y, x + y));

for (int i = 0; i < 20; i++)
{
Console.WriteLine(fib(i, 1, 1));
}

}

static Func<T1, T2, T3, T> Y<T1, T2, T3, T>(Func<Func<T1, T2, T3, T>, Func<T1, T2, T3, T>> f)
{
return (x, y, z) => f(Y(f))(x, y, z);
}
}还是
Action<int, int, int> Fibonacci_A = (second, first, count) =>
{
if (count == 0){
Console.Write(first);
return;
}
else
MethodBase.GetCurrentMethod().Invoke(null, new object[] { (second + first), second, (count - 1) });};

一大堆的标记符号,看着都头晕别说理解
感觉 Lambda表达式为了人的阅读更适合简单点的方法实现
    回复  引用  查看    

标题  
姓名  
主页
Email (博主才能看到) 
验证码 *  看不清,换一张 [登录][注册]
内容(请不要发表任何与政治相关的内容)  
博客园首页

新闻频道

社区

小组

博问

网摘

闪存

  登录  使用高级评论  新用户注册  返回页首  恢复上次提交      
该文被作者在 2008-06-11 21:12 编辑过
成果网帮您增加网站收入


相关链接: