如何使用C ++找到解决此问题的算法:给定一个整数z <= 10 ^ 100,找到包含数字z的Pascal三角形的最小行。
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
例如,如果z = 6 =&gt;结果在第4行。
描述问题的另一种方法:给定整数z <= 10 ^ 100,找到最小整数n:存在整数k,使得C(k,n)= z。
C(k,n)是一次取k而不重复的n个事物的组合
答案 0 :(得分:4)
编辑此解决方案需要对数时间,即O(Log z)
。或者也许O( (Log z)^2 )
。
假设您正在寻找给定z的n,k
Binomial(n,k)==z
。
每一行中间都有最大的值,因此从n=0
开始,只要中间值小于给定的数字,就会增加行号n
。实际上,10 ^ 100不是那么大,所以在第340行之前你找到一个位置n0,k0=n0/2
,其中三角形的值大于或等于z
:Binomial(n0,k0)>=z
您走到左侧,即减少列号k
,直到最终找到小于z
的值。如果该行中存在匹配值,您现在就可以使用它。 k
不是很大,小于170,所以此步骤不会比这更频繁地执行,并且不会出现性能问题。
从这里走下来,增加n
。在这里,您会发现Binomial[n,k]
的价值稳步上升。继续3,直到值大于或等于z
,然后转到2.
编辑:当行号n
很大时,此步骤3可以循环很长时间,因此不是线性地检查每个n
,而是可以执行二进制使用n
搜索Binomial(n,k) >= z > Binomial(n-1,k)
,然后只需Log(n)
次。
Python实现看起来像这样,C ++类似但有点麻烦,因为你需要使用额外的库来获得任意精度整数:
# Calculate (n-k+1)* ... *n
def getnk( n, k ):
a = n
for u in range( n-k+1, n ):
a = a * u
return a
# Find n such that Binomial(n,k) >= z and Binomial(n-1,k) < z
def find_n( z, k, n0 ):
kfactorial = k
for u in range(2, k):
kfactorial *= u
xk = z * kfactorial
nk0 = getnk( n0, k )
n1=n0*2
nk1 = getnk( n1, k )
# duplicate n while the value is too small
while nk1 < xk:
nk0=nk1
n0=n1
n1*=2
nk1 = getnk( n1, k )
# do a binary search
while n1 > n0 + 1:
n2 = (n0+n1) // 2
nk2 = getnk( n2, k )
if nk2 < xk:
n0 = n2
nk0 = nk2
else:
n1 = n2
nk1 = nk2
return n1, nk1 // kfactorial
def find_pos( z ):
n=0
k=0
nk=1
# start by finding a row where the middle value is bigger than z
while nk < z:
# increase n
n = n + 1
nk = nk * n // (n-k)
if nk >= z:
break
# increase both n and k
n = n + 1
k = k + 1
nk = nk * n // k
# check all subsequent rows for a matching value
while nk != z:
if nk > z:
# decrease k
k = k - 1
nk = nk * (k+1) // (n-k)
else:
# increase n
# either linearly
# n = n + 1
# nk = nk * n // (n-k)
# or using binary search:
n, nk = find_n( z, k, n )
return n, k
z = 56476362530291763837811509925185051642180136064700011445902684545741089307844616509330834616
print( find_pos(z) )
应打印
(5864079763474581, 6)
答案 1 :(得分:1)
n的斯特林估计!可用于查找三角形中的第一行,二项式系数大于或等于给定的 x 。使用这个估计,我们可以导出
的下限和上限
然后通过观察,这是扩展2n的行中的最大系数:
P(2n,0),P(2n,1),P(2n,2),...,P(2n,2n-1),P(2n,2n)
我们可以找到最大二项式系数大于或等于给定 x 的第一行。这是 x 可以查找的第一行,无法在小于此的行中找到 x 。注意:这可能是正确的提示,并在某些情况下立即给出答案。目前我看不到其他方式,只能从这一行开始强力搜索。
template <class T>
T binomial_coefficient(unsigned long n, unsigned long k) {
unsigned long i;
T b;
if (0 == k || n == k) {
return 1;
}
if (k > n) {
return 0;
}
if (k > (n - k)) {
k = n - k;
}
if (1 == k) {
return n;
}
b = 1;
for (i = 1; i <= k; ++i) {
b *= (n - (k - i));
if (b < 0) return -1; /* Overflow */
b /= i;
}
return b;
}
斯特林:
double stirling_lower_bound( int n) {
double n_ = n / 2.0;
double res = pow( 2.0, 2 * n_);
res /= sqrt( n_ * M_PI);
return res * exp( ( -1.0) / ( 6 * n_));
}
double stirling_upper_bound( int n) {
double n_ = n / 2.0;
double res = pow( 2.0, 2 * n_) ;
res /= sqrt( n_ * M_PI);
return res * exp( 1.0 / ( 24 * n_));
}
int stirling_estimate( double x) {
int n = 1;
while ( stirling_lower_bound( n) <= x) {
if ( stirling_upper_bound( n) > x) return n;
++n;
}
return n;
}
用法:
long int search_coefficient( unsigned long int &n, unsigned long int x) {
unsigned long int k = n / 2;
long long middle_coefficient = binomial_coefficient<long long>( n, k);
if( middle_coefficient == x) return k;
unsigned long int right = binomial_coefficient<unsigned long>( n, ++k);
while ( x != right) {
while( x < right || x < ( right * ( n + 1) / ( k + 1))) {
right = right * ( n + 1) / ( ++k) - right;
}
if ( right == x) return k;
right = right * ( ++n) / ( ++k);
if( right > x) return -1;
}
return k;
}
/*
*
*/
int main(int argc, char** argv) {
long long x2 = 1365;
unsigned long int n = stirling_estimate( x2);
long int k = search_coefficient( n, x2);
std::cout << "row:" << n <<", column: " << k;
return 0;
}
输出:
行:15,列:11