n-й член ряда

мы должны найти n-й член этого ряда http://oeis.org/A028859

n<=1000000000

ответ должен быть по модулю 1000000007

я написал код, но срок превышается, когда n a является огромным числом.

#include<iostream>
using namespace std

int main()
{
    long long int n;
    cin>>n;

    long long int a,b,c;
    a=1;
    b=3;

    int i;
    for(i=3;i<=n;i++)
    {
        c=(2ll*(a+b))%1000000007;
        a=b;
        b=c; 
    }

    cout<<c;
}

person user1484638    schedule 01.07.2012    source источник
comment
Есть ли шанс, что вы могли бы вставить более чистый пример кода, чем этот, используя правильные отступы и избегая чрезмерного пробела?   -  person Robert Harvey    schedule 02.07.2012
comment
Какое это имеет отношение к динамическому программированию?   -  person Mathias    schedule 02.07.2012
comment
Попробуйте рекурсивную версию этого алгоритма, и вы поймете, что это за алгоритм динамического программирования. По сути, мы храним вычисленные значения n-1 и n-2. Скажем, это базовая версия DP.   -  person Samy Arous    schedule 02.07.2012
comment
Две простые оптимизации: вы можете начать с трех шагов для каждой итерации в вашем цикле без копий, выполняя c=2(a+b)... b=2(c+a)... a=2(b +c).... Затем вы также можете выполнить мод только один раз за цикл, на c в первом шаге. Это должно более чем удвоить вашу скорость.   -  person David Schwartz    schedule 02.07.2012
comment
это не связано с ДП. DP - это поиск оптимума в проблеме решения. это просто рекурсия   -  person stefan    schedule 02.07.2012
comment
? Извините, чувак, вы неправильно понимаете значение DP. Dp предназначен для хранения вычисленных значений, чтобы использовать их в будущих вычислениях и не вычислять их снова.   -  person Samy Arous    schedule 02.07.2012
comment
@lcfseth нет, ну отчасти, но все же: нет. вы говорите о запоминании.   -  person stefan    schedule 02.07.2012
comment
Ключевая идея динамического программирования довольно проста. В общем, чтобы решить данную проблему, нам нужно решить разные части проблемы (подзадачи), а затем объединить решения подзадач для достижения общего решения. Часто многие из этих подзадач на самом деле одинаковы. Подход динамического программирования стремится решить каждую подзадачу только один раз, тем самым сокращая количество вычислений: как только решение данной подзадачи было вычислено, оно сохраняется или запоминается: в следующий раз, когда нужно то же самое решение, его просто посмотрел вверх.   -  person Samy Arous    schedule 02.07.2012
comment
Хорошо, теперь с этим объяснением DP, не могли бы вы объяснить, почему кто-то в здравом уме может попробовать DP с ЭТОЙ проблемой? Очевидно, что это не может быть быстрее, чем простая рекурсия, поскольку нужно вычислить те же a(n). На самом деле это будет медленнее из-за ветвлений в коде.   -  person stefan    schedule 02.07.2012
comment
Рекурсивная форма сложности числа Фибоначи равна O (2 ^ n), где сложность DP равна O (n). stackoverflow.com/questions/360748/   -  person Samy Arous    schedule 02.07.2012
comment
Интеллектуальная форма рекурсивного Фибоначчи делает это снизу вверх, что также O (n) и без каких-либо ветвей, поэтому быстрее.   -  person stefan    schedule 02.07.2012
comment
@lcfseth stackoverflow. com/questions/6184869/   -  person Jim Balter    schedule 02.07.2012


Ответы (2)


Стандартный метод решения задач такого типа состоит в том, чтобы переписать ее как умножение матриц, а затем использовать возведение в степень путем возведения в квадрат для эффективного вычисления степени матрицы.

В таком случае:

a(n+2) = 2 a(n+1) + 2 a(n)
a(n+1) = a(n+1)

(a(n+2)) = (2  2) * ( a(n+1) )
(a(n+1))   (1  0)   ( a(n)   )

Итак, если мы определим матрицу A=[2,2 ; 1,0], то вы можете вычислить n-й член с помощью

[1,0] * A^(n-2) * [3;1]

Все эти операции можно выполнять по модулю 1000000007, поэтому библиотека больших чисел не требуется.

Для вычисления A ^ N требуется O (log (n)) 2 * 2 матричных умножения, поэтому в целом этот метод равен O (log (n)), тогда как ваш исходный метод был O (n).

РЕДАКТИРОВАТЬ

Вот хорошее объяснение и реализация этого метода на C++ .

person Peter de Rivaz    schedule 01.07.2012
comment
Здесь я ответил на аналогичный вопрос, но дополнительно использовал теорему Эйлера. 1000...07 — простое число, поэтому вам не нужно вычислять A^N, но A^(N % (p-1)) достаточно. Таким образом, метод становится O(1) (или O(p), но p является постоянным) вместо O(log(n)). - person Unapiedra; 10.10.2014

Когда long long недостаточно, вы, вероятно, захотите использовать библиотеку bignum. Например, GNU MP.

person iblue    schedule 01.07.2012
comment
но мы должны дать ответ по модулю 1000000007 - person user1484638; 02.07.2012
comment
Мало того, что long long int достаточно, я считаю, что unsigned long int также достаточно, поскольку максимально возможное значение равно 4*10000007 ‹ 4294967295. - person Samy Arous; 02.07.2012