FizzBuzz and Fibonacci优化

***************************************转载请注明出处:http://blog.csdn.net/lttree******************************************

一、 引言

今天早上,例行随便看看。

看到文章 ->  面试中如何剔除“鱼目混珠”的程序员

看到里面这段:

招聘程序设计人员,尤其是提到代码,最流行的将鱼目混珠的程序员剔除的问题是 “Fizz-Buzz” 测试。如果一个程序员无法在10-15分钟之间写出一个 Fizz-buzz,那他可能需要更多的锻炼,或许根本没有准备好。另外一个方法就是让他们写 Fibonacci series(斐波纳契数列),并请他们优化一下。大家都知道 Fibonacci 是非常常见的,但是你可能会很惊讶的看到这些程序员很难在之上写出这些数列,即使是在 IDE 上也写不出来。

恩,话说不懂什么事Fizz-buzz测试。。。

于是Wiki了一下 -> http://en.wikipedia.org/wiki/Fizz_buzz

好吧,就是个报数游戏,3或3的倍数 喊Fizz,5或5的倍数喊Buzz,如果既是3又是5的倍数喊FizzBuzz。

重点是后面的那个Fibonacci 的优化,

我只知道 递归和递推两种,上网搜了搜,果真优化很多

看到了 时间复杂度O(log(n)) 空间复杂度O(1)的方法。

就想学习一下

二、Fizz-Buzz

这个我觉得没有难度,

这是我写的:

<span style="font-family:Comic Sans MS;font-size:14px;">// Fizz Buzz
void FizzBuzz( int n )
{
    bool isFZ;
    for( int i = 1 ; i <= n ; ++i )
    {
        isFZ=false;
        if( i % 3 == 0 )    {cout<<"Fizz";isFZ=true;}
        if( i % 5 == 0 )    {cout<<"Buzz";isFZ=true;}
        if( !isFZ ) cout<<i;
        cout<<" ";
        if( i % 10 == 0 )   cout<<endl;
    }
}</span>

很简单,但我总觉得有点繁杂,

希望会更好的方法的,留下代码,学习一下~

三、Fibonacci的优化

简单说一下Fibonacci 数列

有一种理想型生物,都拿兔子来说= =。

刚开始有这么一对兔子,每月初可以生一对兔子,而刚出生的兔子到第三个月初开始,也可以生每月初生一对兔子。

这样下去,到第n个月,会有多少对兔子?

来一个表格:

月份: 0
1 2
3 4 5
6 ...
n

①兔: 0
0 1
1 2 3
5 ...
n-1_成年兔+n-1_②兔

推导: n-1_成年兔 = n-2_成年兔+n-2_②兔,n-1_②兔=n-2_①兔

n-1_成年兔+n-1_②兔= n-2_兔总

②兔: 0
0 0
1 1 2
3 ...
n-1_①兔

成年兔: 0
1 1
1 2 3
5 ...
n-1_成年兔+n-1_②兔

兔总: 0
1 2
3 5 8
13 ...
n-1_兔总+n-2兔总

PS:①兔表示一个月大的兔子,②兔即两个月大兔子。里面的数字是兔子的对数。

这是推倒出来

第n个月的 ①兔对数 等于 该月成年兔对数

第n个月的 ②兔对数 等于 第 n-1月的①兔对数

第n个月的 成年兔对数 等于 第n-1月的成年兔对数+②兔对数

然后再根据n-1 推 n-2 的发现

第n个月兔子对数 等于 第n-1月 与 第n-2月 兔子对数之和。

这是按我的理解思路,讲述的。。有点绕,不知道懂了木有。。。

基本的Fibonacci概念说完了,现在看它们的解法:

1.最简单暴力好读的——递归方法

时间复杂度:O(n^2)

空间复杂度:数字过大可能导致 栈溢出

<span style="font-family:Comic Sans MS;font-size:14px;">int digui( int n )
{
    if( n == 0 )
        return 0;
    else if( n == 1 )
        return 1;
    else
        return ( digui(n-1) + digui(n-2) );
}</span>

2.省空间也省了时间的,递归进阶——递推方法

时间复杂度:O(n)

空间复杂度:O(n)

<span style="font-family:Comic Sans MS;font-size:14px;">// 普通递推算法
int* ditui( int n )
{
    int* arr = new int[n+1];
    arr[0]=0,arr[1]=1;

    for( int i = 2 ; i <= n ; ++i )
        arr[i]=arr[i-1]+arr[i-2];

    return arr;
}</span>

3.继续优化——优化递推法

我们可以看到,如果求第n个Fibonacci数,

我们只需要知道第n-1和第n-2个的Fibonacci数即可,

前面的不需要存储。

所以,就有了更优化,

时间复杂度:O(n)

空间复杂度:O(1)

<span style="font-family:Comic Sans MS;font-size:14px;">// 递推算法优化
int ditui_opt( int n )
{
    if( n < 2 ) return n;
    int i = 1,pre1=0,pre2=1;
    while( i < n )
    {
        pre2 = pre2 + pre1;
        pre1 = pre2 - pre1;
        ++i;
    }
    return pre2;
}</span>

4.更优化的——矩阵法

时间复杂度:O(log(n) )

空间复杂度:数字过大可能导致 栈溢出

递归、递推已经无法优化时间复杂度了,

空间都到了O(1)了,也没法再精进了,

so,有没有别的方法来进行进一步优化呢?

Of course!

我们可以发现,其实f(n) 都是 f(0)和f(1) 有关的:

f(2) = f(1) + f(0);

f(3) = f(2) + f(1) = 2*f(1) + f(0);

f(4) = f(3) + f(2) = 3*f(1) + 2*f(0);

.......

所以未来的f(n)一定等于:

f(n) = a*f(1) + b*f(0);

可是,如何求a和b呢?

通过矩阵行列式,可以推演出:

这样,关键就是求矩阵的次方了,

如果直接计算,那么时间复杂度是O(n),根本就没有什么优化。

所以,此时,我们就要用二分法(分治法)来解决

M^a =  M^(a/2) * M^(a/2) = ....

这样,时间复杂度可以优化到O(log(n) )!

<span style="font-family:Comic Sans MS;font-size:14px;">// 矩阵优化 方法

// 构造个矩阵结构体
struct Matrix
{
    int m0,m1,m2,m3;
};
// 矩阵乘法
Matrix mat_mul( Matrix mtx1 , Matrix mtx2 )
{
    Matrix mat;
    mat.m0 = mtx1.m0 * mtx2.m0 + mtx1.m1 * mtx2.m2;
    mat.m1 = mtx1.m0 * mtx2.m1 + mtx1.m1 * mtx2.m3;
    mat.m2 = mtx1.m2 * mtx2.m0 + mtx1.m3 * mtx2.m2;
    mat.m3 = mtx1.m2 * mtx2.m1 + mtx1.m3 * mtx2.m3;
    return mat;
}
// 矩阵乘方
Matrix mat_pow( int k )
{
    Matrix mat;
    if( k == 1 )
    {
        mat.m0=1;
        mat.m1=1;
        mat.m2=1;
        mat.m3=0;
    }
    else if( k % 2 == 0)
    {
        mat = mat_pow( k/2 );
        mat = mat_mul( mat , mat );
    }
    else
    {
        mat = mat_pow( (k - 1) / 2 );
        mat = mat_mul( mat , mat );
        mat = mat_mul( mat , mat_pow(1) );
    }
    return mat;
}
// 最后求Fibonacci
int fib_matrix( int n )
{
    if( n < 2 )    return n;

    Matrix mat;
    mat = mat_pow(n-1);

    int ans;
    ans = mat.m0 + mat.m1;
    return ans;
}</span>

这种方法时间上压缩到了log(n),可是空间上,因为二分法,算是递归的过程,

有可能会导致 栈溢出。

于是乎,又有了优化方法。

5.优化中的优化

时间复杂度:O(log(n))

空间复杂度:O(1)

没错,就是O(1)。

压缩空间复杂度,而且是对于递归,

那就是用 递归 转 递推的方法,推导出的,

我对于这个方法,还是有些迷惑,没办法讲述太明白,

在转递推过程中,

会发现,并不是所有的数都需要存储,

于是就会用到 《编程之美》中的:

来考虑。

算法之路,博大精深啊。。

***************************************转载请注明出处:http://blog.csdn.net/lttree******************************************

时间: 2024-08-19 12:57:33

FizzBuzz and Fibonacci优化的相关文章

Fibonacci快速实现(优化)

斐波那契数列的通俗解法是利用递推公式进行递归求解,我们可以更优化的去解决它. 方法一:通项公式 斐波那契数列的递推公式是f(n)=f(n-1)+f(n-2),特征方程为:x2=x+1,解该方程得(1+sqrt(5))/2,(1-sqrt(5))/2.所以f(n)=Ax1n+Bx2n,带入f(0)=0,f(1)=1得A=sqrt(5)/5,B=-sqrt(5)/5.则f(n)求出. 方法二:分治策略 可以看出斐波那契数列有如下性质: (fn fn-1)=(fn-1 fn-2)*A,可以得出A=(1

Fibonacci数列--矩阵乘法优化

Fibonacci数列 题目描述 定义:f0=f1=1, fn=fn-1+fn-2(n>=2).{fi}称为Fibonacci数列. 输入n,求fn mod q.其中1<=q<=30000. 输入描述 第一行一个数T(1<=T<=10000). 以下T行,每行两个数,n,q(n<=109, 1<=q<=30000) 输出描述 文件包含T行,每行对应一个答案. 样例输入 3 6 2 7 3 7 11 样例输出 1 0 10 数据范围及提示 1<=T<

【转】Fibonacci 斐波纳契堆优化 Dijkstra 最短路径算法

话不多说,拿来主义,直接上代码! PS:打印最短路径我还不晓得怎么加,如有哪位大神知道,还请mark一下! 1 /*********************************************************************** 2 * File: FibonacciHeap.java 3 * Author: Keith Schwarz ([email protected]) 4 * 5 * An implementation of a priority queue

算法学习#02--斐波那契Fibonacci数列算法优化

算法列表 本文从时间效率和占用空间内存角度评估,找出最优算法. 经典递归算法Recursive algorithm(很慢) 动态存储算法Dynamic programming(慢) 矩阵幂算法Matrix exponentiation(快) 倍数公式算法Fast doubling(很快) 倍数公式算法+快速乘法Fast doubling with Karatsuba(最快) Fibonacci数列 1.数列介绍 斐波那契数列(Fibonacci sequence),又称黄金分割数列.因数学家列昂

javascript memoization递归优化

memoize优化递归 function createRec(callback, cache) { cache = cache || []; var rec = function(n) { (n in cache) || (cache[n] = callback(rec, n)); return cache[n]; } return rec; } 以Fibonacci数列为例子,如何创建一个优化的递归 var fib = createRec(function(cal, n) { return c

hihocoder#1239 Fibonacci

#1239 : Fibonacci 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 Given a sequence {an}, how many non-empty sub-sequence of it is a prefix of fibonacci sequence. A sub-sequence is a sequence that can be derived from another sequence by deleting some elements

c语言:写一个函数,输入n,求斐波拉契数列的第n项(5种方法,层层优化)

写一个函数,输入n,求斐波拉契数列的第n项. 斐波拉契数列:1,1,2,3,5,8...,当n大于等于3时,后一项为前面两项之和. 解:方法1:从斐波拉契数列的函数定义角度编程 #include<stdio.h> int fibonacci(int n) { int num1=1, num2=1, num3=0,i; if (n <= 2) { printf("斐波拉契数列的第%d项为:%d\n",n,num1); } else { for (i = 2; i <

Android UI性能优化详解

设计师,开发人员,需求研究和测试都会影响到一个app最后的UI展示,所有人都很乐于去建议app应该怎么去展示UI.UI也是app和用户打交道的部分,直接对用户形成品牌意识,需要仔细的设计.无论你的app UI是简单还是复杂,重要的是性能一定要好. UI性能测试 性能优化都需要有一个目标,UI的性能优化也是一样.你可能会觉得“我的app加载很快”很重要,但我们还需要了解终端用户的期望,是否可以去量化这些期望呢?我们可以从人机交互心理学的角度来考虑这个问题.研究表明,0-100毫秒以内的延迟对人来说

"二分查找(Binary Search)"与"斐波那契查找(Fibonacci Search)"

首先,我们来看一个笔者的拙作,一段二分查找代码 //返回值是key的下标,如果A中不存在key则返回-1 template <class T> int BinSearch(T* A, const T &key, int lo, int hi) {     int mid;     while(lo<hi)     {         mid = lo + (hi-lo)/2;         if(key < A[mid])             hi = mid-1;