Вычисление количества комбинаций может быть выполнено в O(log x)
без учета времени, необходимого для умножения матриц на целые числа произвольного размера.
Количество комбинаций может быть сформулировано как повторение.Пусть S(n)
будет количеством способов сделать число n
путем добавления чисел из набора.Повторение -
S(n) = a_1*S(n-1) + a_2*S(n-2) + ... + a_15*S(n-15),
, где a_i
- это количество раз, которое i
происходит в наборе.Также S (n) = 0 для n <0.Этот вид рецидива можно сформулировать в виде матрицы <code>A размером 15 * 15 (или меньше, если наибольшее число в наборе меньше).Тогда, если у вас есть вектор-столбец V
, содержащий
S(n-14) S(n-13) ... S(n-1) S(n),
, тогда результат умножения матрицы A*V
будет
S(n-13) S(n-12) ... S(n) S(n+1).
Матрица A
определяется какследует:
0 1 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 1 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 1 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 1 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 1 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 1 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 1 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 1 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 1 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 1 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 1 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 1 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 1
a_15 a_14 a_13 a_12 a_11 a_10 a_9 a_8 a_7 a_6 a_5 a_4 a_3 a_2 a_1
, где a_i
такое, как определено выше.Доказательство того, что умножение этой матрицы на вектор S(n_14) ... S(n)
работает, можно сразу же увидеть, выполнив умножение вручную;последний элемент в векторе будет равен правой части повторения с n+1
.Неофициально элементы в матрице сдвигают элементы в векторе столбцов на одну строку вверх, а последняя строка матрицы вычисляет самый новый член.
Чтобы вычислить произвольный член S(n)
повторениядля вычисления A^n * V
, где V
равно
S(-14) S(-13) ... S(-1) S(0) = 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1.
Чтобы уменьшить время выполнения до O(log x)
, можно использовать возведение в квадрат путем квадрата для вычисления A^n
.
На самом деле, достаточно игнорировать вектор столбца, нижний правый элемент A^n
содержит требуемое значение S(n)
.
В случае, если приведенное выше объяснение было затруднительнымДалее я предоставил программу на C, которая рассчитывает количество комбинаций так, как я описал выше.Помните, что он очень быстро переполнит 64-битное целое число.Вы сможете продвинуться намного дальше с высокоточным типом с плавающей запятой, используя GMP , хотя вы не получите точного ответа.
К сожалению, я не вижубыстрый способ получить точный ответ для чисел, таких как x=10^18
, поскольку ответ может быть намного больше, чем 10^x
.
#include <stdio.h>
typedef unsigned long long ull;
/* highest number in set */
#define N 15
/* perform the matrix multiplication out=a*b */
void matrixmul(ull out[N][N],ull a[N][N],ull b[N][N]) {
ull temp[N][N];
int i,j,k;
for(i=0;i<N;i++) for(j=0;j<N;j++) temp[i][j]=0;
for(k=0;k<N;k++) for(i=0;i<N;i++) for(j=0;j<N;j++)
temp[i][j]+=a[i][k]*b[k][j];
for(i=0;i<N;i++) for(j=0;j<N;j++) out[i][j]=temp[i][j];
}
/* take the in matrix to the pow-th power, return to out */
void matrixpow(ull out[N][N],ull in[N][N],ull pow) {
ull sq[N][N],temp[N][N];
int i,j;
for(i=0;i<N;i++) for(j=0;j<N;j++) temp[i][j]=i==j;
for(i=0;i<N;i++) for(j=0;j<N;j++) sq[i][j]=in[i][j];
while(pow>0) {
if(pow&1) matrixmul(temp,temp,sq);
matrixmul(sq,sq,sq);
pow>>=1;
}
for(i=0;i<N;i++) for(j=0;j<N;j++) out[i][j]=temp[i][j];
}
void solve(ull n,int *a) {
ull m[N][N];
int i,j;
for(i=0;i<N;i++) for(j=0;j<N;j++) m[i][j]=0;
/* create matrix from a[] array above */
for(i=2;i<=N;i++) m[i-2][i-1]=1;
for(i=1;i<=N;i++) m[N-1][N-i]=a[i-1];
matrixpow(m,m,n);
printf("S(%llu): %llu\n",n,m[N-1][N-1]);
}
int main() {
int a[]={1,1,0,0,0,0,0,1,0,0,0,0,0,0,0};
int b[]={1,1,1,1,1,0,0,0,0,0,0,0,0,0,0};
solve(13,a);
solve(80,a);
solve(15,b);
solve(66,b);
return 0;
}