計算組合的數量可以在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)的n個< 0 = 0這種復發的可以以矩陣尺寸的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)
,可以使用exponentiation by squaring計算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;
}
如果您只能使用每個數字一次,則此問題很難定義爲NP-Hard。我認爲對於每個號碼的無限制使用也是如此,儘管在此刻的減少不會突然出現在我的頭上 – amit
@Amit。是的,那麼它就成爲NP-Hard的子集合問題。但這不是。它與(限制)一個數字的構成有關,我想。 – Andrew
由於你想要認識到「不同」的方式,這很難。如果F(4)是3或32,那將會容易得多。 – harold