k-Fibonacci的算法

时间:2010-11-08 08:37:02

标签: algorithm fibonacci

当k = 2时,我们都知道斐波纳契系列。

即:1,1,2,3,5,8,13

但这是2-fibonacci。像这样,我可以计算第三个斐波那契:

1,1,2,4,7,13,24

4-fibonacci:

1,1,2,4,8,15,29

......等等

我要问的是计算k-fibonacci系列中'n'元素的算法。

像这样:如果我要求fibonacci(n=5,k=4),结果应该是:8,即4-fibonacci系列中的第五个元素。

我没有在任何网站上找到它。帮助的资源可以是mathworld

任何?如果你知道python,我更喜欢。但如果没有,任何语言或算法都可以提供帮助。

提示我认为这有助于:     让我们分析k-fibonacci系列,其中k将从1到5

k    fibonacci series

1    1, 1, 1, 1, 1, 1, 1, 1,1, 1, 1, ...
2    1, 1, 2, 3, 5, 8, 13, 21, 34, 55, ...
3    1, 1, 2, 4, 7, 13, 24, 44, 81, ...
4    1, 1, 2, 4, 8, 15, 29, 56, 108, ...
5    1, 1, 2, 4, 8, 16, 31, 61, 120, ...

分析这个,我们可以看到k-fibonacci系列上的数组[0:k]等于 以前的斐波那契系列,它一直持续到k = 1

即。 (我会试着表明,但我找不到正确的说法):

k    fibonacci series

1    1, 
2    1, 1, 
3    1, 1, 2, 
4    1, 1, 2, 4, 
5    1, 1, 2, 4, 8, 

希望我能以某种方式解决这个问题。

[python中的解决方案(如果有人需要)]

class Fibonacci:

    def __init__(self, k):
        self.cache = []
        self.k = k

        #Bootstrap the cache
        self.cache.append(1)
        for i in range(1,k+1):
            self.cache.append(1 << (i-1))

    def fib(self, n):
        #Extend cache until it includes value for n.
        #(If we've already computed a value for n, we won't loop at all.)
        for i in range(len(self.cache), n+1):
            self.cache.append(2 * self.cache[i-1] - self.cache[i-self.k-1])

        return self.cache[n]


#example for k = 5
if __name__ == '__main__':
    k = 5
    f = Fibonacci(k)
    for i in range(10):
        print f.fib(i),

10 个答案:

答案 0 :(得分:9)

与2-fibonacci一样,动态编程是最佳选择。在k时间内记住早期O(n)的值以快速计算后面的值。

您可以用来提高k大值的速度的另一个优化是将f(n-k)添加到f(n-1)以获取f(n),而只是使用(2*f(n-1)) - f(n-k-1) }。由于这仅使用2次查找,2次添加和乘法,因此k查找和kk变大时会增加(但它仍然是O(n),一个较小的常数乘数)。

答案 1 :(得分:7)

这是一个基于Ambers answer的迭代解决方案:

class Fibonacci {

    List<Integer> cache = new ArrayList<Integer>();
    final int K;

    public Fibonacci(int k) {
        this.K = k;

        // Bootstrap the cache
        cache.add(1);
        for (int i = 1; i <= k; i++)
            cache.add(1 << (i-1));
    }

    public long fib(int n) {

        // Extend cache until it includes value for n.
        // (If we've already computed a value for n, we won't loop at all.)
        for (int i = cache.size(); i <= n; i++)
            cache.add(2 * cache.get(i-1) - cache.get(i-K-1));

        // Return cached value.
        return cache.get(n);
    }
}

测试如下:

public class Main {
    public static void main(String[] args) {
        System.out.println("k     fibonacci series");

        for (int k = 1; k <= 5; k++) {
            System.out.print(k + "     ");

            Fibonacci f = new Fibonacci(k);
            for (int i = 0; i < 10; i++)
                System.out.print(f.fib(i) + ", ");
            System.out.println("...");

        }
    }
}

并打印

k     fibonacci series
1     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, ...
2     1, 1, 2, 3, 5, 8, 13, 21, 34, 55, ...
3     1, 1, 2, 4, 7, 13, 24, 44, 81, 149, ...
4     1, 1, 2, 4, 8, 15, 29, 56, 108, 208, ...
5     1, 1, 2, 4, 8, 16, 31, 61, 120, 236, ...

答案 2 :(得分:7)

如果您只想解决一个值(即fibonnaci(n,k)),那么更有效的方法是使用线性递归,它将是O(k^3 log(n))k^3因子可以用更好的矩阵乘法算法进行改进。)

基本上,这种方法的工作方式是将向量F(n), F(n-1) ... F(n-k)表示为向量F(n-1), F(n-2) ... F(n-k-1)的矩阵时间。然后,由于矩阵乘法是关联的,您可以将矩阵提升为幂,并将其乘以初始向量F(k), F(k-1) ... F(0)

可以在O(log(n))中使用指数通过平方来进行指数化。

例如,对于k = 3的情况,我们将:

[F(n+2)]   [1 1 1] [F(n+1)]
[F(n+1)] = [1 0 0] [F(n)  ]
[F(n)  ]   [0 1 0] [F(n-1)]

所以要解决F(n),你只需找到

[F(n+2)]   [1 1 1]^n [F(2)]
[F(n+1)] = [1 0 0]   [F(1)]
[F(n)  ]   [0 1 0]   [F(0)]

答案 3 :(得分:3)

直接的方法是简单地将最后的k个术语相加以每次获得当前术语。这给了我们一个O(n * k)运行时。

另一种方法是使用矩阵求幂。对于k = 2,您可以使用矩阵对情况进行建模。从(Fn-1,Fn-2)我们可以通过计算(Fn-1 + Fn-2,Fn-1)导出(Fn,Fn-1)。

因此,乘以coloumn矩阵

[
Fn-1
Fn-2
]

使用方阵

[
1 1
1 0
]

产量

[
Fn-1 + Fn-2
Fn-1
]

从而为我们提供了Fn的价值。

当然,这还不比O(n * k)好。我们仍然会运行O(n)循环/递归来获得第n个项。

观察 (为方便起见,我现在正在横向编写coloumn矢量,但它们仍然是coloumns)

[[Fn],[Fn-1]] = [[Fn-1],[Fn-2]]*[[1,1] [1,0]]
              = [[Fn-2],[Fn-3]]*[[1,1] [1,0]]*[[1,1] [1,0]]
              = [[Fn-3],[Fn-4]]*[[1,1] [1,0]]*[[1,1] [1,0]]*[[1,1] [1,0]]
              = [[Fn-3],[Fn-4]]*([[1,1] [1,0]])^3
              = [[Fn-k],[Fn-k-1]]*([[1,1] [1,0]])^k
              = [[F1],[F0]]*([[1,1] [1,0]])^n-1

现在,([[1,1] [1,0]])^n-1可以使用exponentiation by squaring在O(log(n))时间内计算。因此,您可以使用最多log(n)矩阵乘法计算k-fibonacci的第n项。使用直接的矩阵乘法,这给了我们O(k ^ 3 * log(n))的复杂性。

编辑:

以下是Python中的一些代码我一起入侵以说明我说的更好:

from itertools import izip

def expo(matrix,power, identity):
    if power==0:
        return identity
    elif power==1:
        return matrix
    elif power&1:
        return multiply(matrix,expo(matrix,power-1,identity))
    else:
        x=expo(matrix,power>>1,identity)
        return multiply(x,x)

def multiply(A,B):
    ret=[list() for i in xrange(len(B))]
    for i,row in enumerate(B):
        for j in xrange(len(A[0])):
            coloumn=(r[j] for r in A)
            ret[i].append(vector_multiply(row,coloumn))
    return ret

def vector_multiply(X,Y):
    return sum(a*b for (a,b) in izip(X,Y))

def fibonacci(n,start=[[1],[0]], k=2):
    identity=[[1 if col==row else 0 for col in xrange(k)] for row in xrange(k)] # identity matrix
    # build the matrix for k
    matrix=[[1]*k]
    for i in xrange(1,k):
        matrix.append([0]*(i-1)+[1]+[0]*(k-i))
    return multiply(start,expo(matrix,n-1,identity))[0][0]

print fibonacci(10)

答案 4 :(得分:1)

为了练习它,我在Haskell中实现了它。以下是fib通常作为列表理解编写的方式:

fib = 1:1:[x + y | (x,y) <- zip fib $ tail fib]

推广到'k'术语很困难,因为zip需要两个参数。有一个zip3zip4等,但没有一般zipn。但是,我们可以废除创建对的技术,而是生成“序列的所有尾部”,并对这些成员的第一个k成员求和。以下是查找k = 2情况的方法:

fib2 = 1:1:[sum $ take 2 x | x <- tails fib2]

推广到任何k

fibk k = fibk'
  where fibk' = take (k - 1) (repeat 0) ++ (1:[sum $ take k x | x <- tails fibk'])


> take 10 $ fibk 2
[0,1,1,2,3,5,8,13,21,34]

> take 10 $ fibk 3
[0,0,1,1,2,4,7,13,24,44]

> take 10 $ fibk 4
[0,0,0,1,1,2,4,8,15,29]

答案 5 :(得分:1)

下面的另一个log(n)解决方案 Source and explanation here
如果进行了大量调用,您可以缓存解决方案。

public class Main {
    /* 
     * F(2n) = F(n) * (2*F(n+1) - F(n))
     * F(2n+1) = F(n+1)^2 + F(n)^2
     * Compute up to n = 92, due to long limitation<br>
     * Use different type for larger numbers
     */
    public static long getNthFibonacci(int n) {
        long a = 0;
        long b = 1;
        for (int i = 31 - Integer.numberOfLeadingZeros(n); i >= 0; i--) {

            long d = a * ((b << 1) - a); // F(2n)
            long e = a * a + b * b; // F(2n+1)
            a = d;
            b = e;

            if (((1 << i) & n) != 0) { // advance by one
                long c = a + b;
                a = b;
                b = c;
            }
        }
        return a;
    }
}

答案 6 :(得分:1)

人们已经提到了O(logN)解决方案。没有多少人理解矩阵中被取幂的常数是如何形成的。如果您想详细分析如何使用矩阵来解决线性递归问题,请查看Code Overflow.

答案 7 :(得分:0)

我想你需要的东西比O(nk)更好 对于O(nk),你可以天真地计算它 如果您有n <= Nk <= K的上限,您还可以构建一次矩阵NxK,并在需要该值时随时查询它。

修改
如果您想进一步深入学习数学,可以尝试阅读this paper on Generalized Order-k Pell Numbers

答案 8 :(得分:0)

简单的强力解决方案

    Scanner scanner = new Scanner(System.in);
    int n = scanner.nextInt();
    int k = scanner.nextInt();
    long formula ;
    formula = k ;


    long[] fib = new long[n+1];

    for (int i = 1; i <=n ; i++) {
        if(i<=k) fib[i]  = 1;
        else {
            fib[i] = formula;
            formula =(formula*2-fib[i-k]);
        }
    }


    for (int i = 1; i <=fib.length-1 ; i++) {
        System.out.print(fib[i]+" ");
    }

答案 9 :(得分:0)

这是一个高效,简洁,精确的封闭式解决方案。

A

这是输出:

X

该方法有效地计算多项式环Z [X] /(X ^ kX ^(k-1)-X ^(k-2)-...- 1)中的X ^(n + k)(其中最终多项式中常量项的结果有点令人惊讶的是fibk(n,k)),但使用足够大的整数(function () { var app = angular.module('myApp', []); app.controller('TabController', function () { if(typeof(localStorage.currentTab)=="undefined") this.tab = 1; else this.tab = parseInt(localStorage.currentTab); this.setTab = function (tabId) { localStorage.currentTab=tabId; this.tab = tabId; }; this.isSet = function (tabId) { return this.tab === tabId; }; }); })(); 代替# This works, but it is not optimal, could crash queue_listen_name = config["database"]["listen_channel"] cur.execute("LISTEN %s;" % ext.quote_ident(queue_listen_name)) 来执行整数计算。