Проблема в том, что вы вычисляете значение по модулю ULONG_MAX
и уменьшаете результат по модулю 1000000007
в конце. Это не дает правильного результата. Вы должны уменьшить модуль 1000000007
на каждом шаге, чтобы избежать потенциального арифметического переполнения (которое не вызывает неопределенного поведения для типа unsigned long
, но дает результат, отличный от ожидаемого).
Вот модифицированная версия вашего кода с более быстрой альтернативой (более чем в два раза быстрее на моем ноутбуке):
#include <stdio.h>
#include <stdlib.h>
#define DIVIDER 1000000007ul
unsigned long fibo(unsigned long n, unsigned long k) {
unsigned long c = 1;
if (n > k) {
unsigned long *a = (unsigned long *)malloc(sizeof(*a) * k);
for (unsigned long i = 0; i < k; i++) { //T(n,k)=1 when n<=k
a[i] = 1;
}
for (unsigned long m = k; m < n; m++) {
c = a[0];
for (unsigned long j = 0; j < k - 1; j++) {
a[j] = a[j + 1];
#if 0
// slower version using modulo
c = (c + a[j]) % DIVIDER;
#else
// faster version with a test
if ((c += a[j]) >= DIVIDER)
c -= DIVIDER;
#endif
}
a[k - 1] = c;
}
free(a);
}
return c;
}
int main(int argc, char *argv[]) {
if (argc <= 2) {
printf("usage: fibo n k");
return 1;
} else {
unsigned long n = strtoul(argv[1], NULL, 10);
unsigned long k = strtoul(argv[2], NULL, 10);
printf("%lu\n", fibo(n, k));
}
return 0;
}
Выход:
$ time ./fibo 200000 100000
871925546
real 0m34.667s
user 0m34.288s
sys 0m0.113s
$ time ./fibo-faster 200000 100000
871925546
real 0m15.073s
user 0m14.846s
sys 0m0.064s
Учитывая ограничения на входные значения:
- значения
T(n, k)
находятся в диапазоне [0..1000000006], что соответствует int32_t
.
- сумма
k
членов находится в диапазоне [0..200000 * 1000000006], который соответствует int64_t
.
- следовательно, мы можем вычислить следующий член в 64 битах и использовать один результат по модулю.
Это дает еще более быструю версию (более чем в 3 раза быстрее):
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#define DIVIDER 1000000007
uint32_t fibo(uint32_t n, uint32_t k) {
uint32_t c = 1;
if (n > k) {
uint32_t *a = (uint32_t *)malloc(sizeof(*a) * k);
uint64_t temp;
for (uint32_t i = 0; i < k; i++) { //T(n,k)=1 when n<=k
a[i] = 1;
}
for (uint32_t m = k; m < n; m++) {
temp = a[0];
for (uint32_t j = 0; j < k - 1; j++) {
temp += a[j] = a[j + 1];
}
a[k - 1] = c = temp % DIVIDER;
}
free(a);
}
return c;
}
int main(int argc, char *argv[]) {
if (argc <= 2) {
printf("usage: fibo n k");
return 1;
} else {
uint32_t n = strtoul(argv[1], NULL, 10);
uint32_t k = strtoul(argv[2], NULL, 10);
printf("%lu\n", (unsigned long)fibo(n, k));
}
return 0;
}
Выход:
$ time ./fibo-faster 200000 100000
871925546
real 0m3.854s
user 0m3.800s
sys 0m0.018s