Алгоритм для k-Фибоначчи

Мы все знаем ряд фибоначчи, когда k = 2.

I.e.: 1,1,2,3,5,8,13

Но это 2-фибоначчи. Таким образом, я могу считать третий-фибоначчи:

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

И 4-фибоначчи:

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

... и так далее

То, что я прошу, является алгоритмом для вычисления элемента "n" внутри ряда k-фибоначчи.

Вот так: если я попрошу fibonacci(n=5,k=4), результат должен быть: 8, т.е. пятый элемент внутри серии 4-фибоначчи.

Я не нашел его нигде в Интернете. Резолюция, которая могла бы помочь, могла быть mathworld

Кто-нибудь? И если вы знаете python, я предпочитаю. Но если нет, любой язык или алгоритм могут помочь.

Совет Думаю, что это может помочь:   Пусть анализируется ряд k-фибоначчи, где 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, ...

Анализируя это, мы видим, что массив [0: k] по ряду 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),

Ответы

Ответ 1

Вот итеративное решение, построенное на ответе Амберса:

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

Как и в случае с 2-фибоначчи, динамическое программирование - это путь. Запомните значения более раннего k, чтобы быстро вычислить более поздние, в O(n) времени.

Другая оптимизация, которую вы можете использовать для повышения скорости при больших значениях k, вместо этого добавляет f(n-k) через f(n-1), чтобы получить f(n), вместо этого просто используйте (2*f(n-1)) - f(n-k-1). Поскольку это использует только 2 поиска, 2 добавляет и умножает, он значительно превосходит поиски k и k добавляет, когда k становится большим (но он все еще O(n), только меньший постоянный множитель).

Ответ 3

Если вы просто хотите решить одно значение (т.е. 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)]

Ответ 4

Простым способом является просто добавить последние k терминов, чтобы каждый раз получать текущий термин. Это дает нам время выполнения O (n * k).

Другим способом было бы использование экспоненции матрицы. При k = 2 вы можете моделировать ситуацию с использованием матрицы. Из (Fn-1, Fn-2) можно вывести (Fn, Fn-1), вычислив (Fn-1 + Fn-2, Fn-1).

Таким образом, умножая матрицу coloumn

[
Fn-1
Fn-2
]

с квадратной матрицей

[
1 1
1 0
]

дает

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

что дает нам значение Fn.

Конечно, это на самом деле не лучше, чем O (n * k). Мы все равно будем запускать цикл O/n/recursion, чтобы получить n-й член.

Заметим, что (Сейчас я пишу цветовые векторы горизонтально для удобства, но они все еще 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 можно вычислить в O (log (n)) времени с помощью возведения в степень возведения в квадрат. Таким образом, вы можете вычислить n-й член k-фибоначчи, используя не более чем log (n) матричные умножения. Используя прямое матричное умножение, это дает нам сложность O (k ^ 3 * log (n)).

Edit:

Вот какой код в 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)

Ответ 5

Для этого я реализовал это в Haskell. Вот как fib обычно записывается как понимание списка:

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

Обобщение термина 'k' затруднено, потому что для zip требуются два аргумента. Существует zip3, zip4 и т.д., Но не общий 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]

Ответ 6

Другое решение для журнала (n) ниже.
Источник и объяснение здесь.
Вы можете кэшировать решения, если сделано много вызовов.

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;
    }
}

Ответ 7

Люди уже упомянули решения O (logN). Не так много людей понимают, как возникли константы в матрице, которая является выраженной. Если вы хотите детально проанализировать, как использовать матрицы для решения линейных рекуррентностей, посмотрите Переполнение кода.

Ответ 8

Я предполагаю, что вам нужно что-то лучшее, чем O(nk).
Для O(nk) вы можете просто вычислить его наивно.
Если у вас есть верхняя граница на n <= N и k <= K, вы можете также построить матрицу NxK один раз и запросить ее в любое время, когда вам нужно это значение.

ИЗМЕНИТЬ
Если вы хотите вникнуть в математику, вы можете попытаться прочитать эту статью в разделе Обобщенные номера заказов P-номера.

Ответ 9

простое решение грубой силы

    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]+" ");
    }

Ответ 10

Здесь эффективное, краткое и точное решение закрытой формы.

def fibk(n, k):
    A = 2**(n+k+1)
    M = A**k - A**k // (A - 1)
    return pow(A, n+k, M) % A

for k in xrange(1, 10):
    print ', '.join(str(fibk(n, k)) for n in xrange(15))

Здесь вывод:

1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610
1, 1, 2, 4, 7, 13, 24, 44, 81, 149, 274, 504, 927, 1705, 3136
1, 1, 2, 4, 8, 15, 29, 56, 108, 208, 401, 773, 1490, 2872, 5536
1, 1, 2, 4, 8, 16, 31, 61, 120, 236, 464, 912, 1793, 3525, 6930
1, 1, 2, 4, 8, 16, 32, 63, 125, 248, 492, 976, 1936, 3840, 7617
1, 1, 2, 4, 8, 16, 32, 64, 127, 253, 504, 1004, 2000, 3984, 7936
1, 1, 2, 4, 8, 16, 32, 64, 128, 255, 509, 1016, 2028, 4048, 8080
1, 1, 2, 4, 8, 16, 32, 64, 128, 256, 511, 1021, 2040, 4076, 8144

Метод эффективно вычисляет X ^ (n + k) в многочленном кольце Z [X]/(X ^ kX ^ (k-1) -X ^ (k-2) -...- 1) ( где результат постоянного члена в конечном многочлене несколько неожиданно является fibk (n, k)), но с использованием достаточно большого числа A вместо X для вычисления в целых числах.