合并两个阵列的最大可能方式

时间:2013-01-11 05:02:36

标签: algorithm math combinatorics

假设我有两个大小为mn的数组:

a[1] a[2] a[3] ..... a[m]

b[1] b[2] b[3] ..... b[n]

我想形成一个合并这两个数组的新数组,这样在新的m + n元素数组中,a[i]始终位于a[i + 1]之内,而b[i]总是放在b[i + 1]之前。例如,a[1] a[2] b[1] b[2]... b[n] a[m]将是有效数组,但a[2] a[1] b[1] b[2] ... b[n] a[m]不会。给定mn,允许重复时可以使用多少个此类组合?

我有直觉来解决问题:

- b[1] - b[2] - b[3] - ..... - b[n]

我可以将a[1]放在数组n - 1中的任何b个地方,考虑到前面和最后一个地方,我有n + 1种放置方式a[1]。如果我将a[1]置于首位(就在b[1]之前),我现在可以将a[2]放在n + 1个地方。但是,如果我在a[1]之后放置b[1],我会n方式放置a[2]。我可以递归地为所有a[i] 1 <=i <= n应用此方法。但我找不到任何数学公式来表达解决方案,除了我无法理解在允许重复时如何处理。

2 个答案:

答案 0 :(得分:3)

考虑这个问题的一种方法是 - 不是按顺序列出元素,而是考虑在每个时刻选择是选择第一个未使用的A还是第一个未使用的B.所有可能的“选择A”和“选择B”的排序将产生产生这些序列的所有可能方式。

如果你假设所有元素都是不同的,那么答案将通过你可以置换m A序列后跟n B的方式的数量来给出。这是由

给出的
   (m + n)!
 -----------
    m!  n!

但是,在有一些重复元素的情况下,我没有答案。如果我想到任何事情,我一定会更新这个答案。

与此同时,我希望这有帮助!

答案 1 :(得分:1)

您正在寻找Stars and Bars公式。阵列A的位置是箱子;在每个位置,将插入来自A的相应元素和来自B的任意数量的元素。 (选择哪些元素已经由它们的原始顺序决定,因此我们将它们视为无法区分。)如果A中有(n-1)个元素(在一端添加一个bin)和B中有k个元素,则公式给出的是二项式系数

  n + k - 1  
(            )
      k

= ( (n+k-1)! / ( k! (n-1)! )
= (a.size() + b.size())! / (a.size()! * b.size()!)

与templatetypedef的答案相同:)

实际上计算这些大数的商是另一个问题。在大多数语言中,分子通常会产生整数溢出。 C ++中的一个简单策略(不一定是最优的)将是

unsigned long long gcd( unsigned long long &a, unsigned long long &b )
    { return b? gcd( b, a % b ) : a; }

std::vector< std::size_t > numerator( a.size() ); // factors of (a+b)!/b!
std::iota( numerator.begin(), numerator.end(), b.size()+1 );

for ( std::size_t afactor = 2; afactor != a.size()+1; ++ afactor ) {
    std::size_t reduced = afactor;
    for ( auto &&nfactor : numerator ) {
        auto common = gcd( afactor, nfactor );
        nfactor /= common;
        reduced /= common;
        if ( reduced == 1 ) goto next_afactor;
    }
    throw std::logic_error( "Fractional combinations" );
next_afactor: ;
}

return std::accumulate( numerator.begin(), numerator.end(), 1,
                        std::multiplies< std::size_t >() );