给定整数z <= 10 ^ 100,找到包含z的Pascal三角形的最小行

时间:2014-04-14 10:05:33

标签: algorithm math pascals-triangle

如何使用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个事物的组合

2 个答案:

答案 0 :(得分:4)

编辑此解决方案需要对数时间,即O(Log z)。或者也许O( (Log z)^2 )

假设您正在寻找给定z的n,k Binomial(n,k)==z

  1. 每一行中间都有最大的值,因此从n=0开始,只要中间值小于给定的数字,就会增加行号n。实际上,10 ^ 100不是那么大,所以在第340行之前你找到一个位置n0,k0=n0/2,其中三角形的值大于或等于zBinomial(n0,k0)>=z

  2. 您走到左侧,即减少列号k,直到最终找到小于z的值。如果该行中存在匹配值,您现在就可以使用它。 k不是很大,小于170,所以此步骤不会比这更频繁地执行,并且不会出现性能问题。

  3. 从这里走下来,增加n。在这里,您会发现Binomial[n,k]的价值稳步上升。继续3,直到值大于或等于z,然后转到2.

  4. 编辑:当行号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 。使用这个估计,我们可以导出

的下限和上限

enter image description here

然后通过观察,这是扩展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