【问题标题】:Sum of multiplication of all combination of m element from an array of n elementsn 个元素的数组中 m 个元素的所有组合的乘法总和
【发布时间】:2014-05-08 08:55:07
【问题描述】:

假设我有一个数组{1, 2, 5, 4}m = 3。 我需要找到:

1*2*5 + 1*2*4 + 1*5*4 + 2*5*4

即n个元素的数组中m个元素的所有组合的乘积之和。

可能的解决方案之一是找到所有组合然后解决它,但那将是O(nCm) 解决方案。有没有更好的算法?

【问题讨论】:

  • 除此之外,您还想如何解决它?
  • 您希望我们为此定义算法吗?先自己试试吧。
  • 如果您想知道是否有一个公式而不是查找所有组合,请尝试 math.stackexchange.com。
  • @PhamTrung 为什么不呢?大哦符号表示一个上限。
  • 有一个使用 Newton's identities 的替代 O(mn) 时间算法(您正在评估第 m 个基本对称多项式)。

标签: algorithm


【解决方案1】:

这个问题相当于计算具有给定根的多项式的第 M 个系数 (Vieta's theorem)。 Delphi中的自适应算法(O(N)内存和O(N^2)时间):

 function CalcMultiComb(const A: array of Integer; const M: Integer): Integer;
  var
    i, k, N: Integer;
    Coeff: TArray<Integer>;
  begin
    N := Length(A);
    if (N = 0) or (M > N) then
      Exit;
    SetLength(Coeff, N + 1);

    Coeff[0] := -A[0];
    Coeff[1] := 1;
    for k := 2 to N do begin
      Coeff[k] := 1;
      for i := k - 2 downto 0 do
        Coeff[i + 1] := Coeff[i] - A[k-1] * Coeff[i + 1];
      Coeff[0] := -A[k-1] * Coeff[0];
    end;

    Result := Coeff[N - M];
    if Odd(N - M) then
      Result := - Result;
  end;

使用 M=1..4 调用 CalcMultiComb([1, 2, 3, 4], M) 得到结果 10, 35, 50, 24

【讨论】:

  • 有趣的是,使用 FFT 的快速多项式插值可以将其改进为 O(n log n)。
【解决方案2】:

我心中有一个动态编程解决方案,只是想分享一下。时间复杂度为 O(k*n^2),其中 n 为总数。

想法是,我们从0到k -1开始填充每个位置。所以如果我们假设在ith位置,这个位置要填充的数字是a,所以所有以a开头的组合的总和将是a乘以从位置(i + 1)th开始的所有组合的总和以(a + 1)开头

注意:我已经更新了解决方案,所以它可以使用任何数组data,我的语言是Java,所以你可以注意到数组的索引是从0开始的,从0到n-1。

public int cal(int n, int k , int[]data){
   int [][] dp = new int[k][n + 1];
   for(int i = 1; i <= n; i++){
       dp[k - 1][i] = data[i - 1];
   }
   for(int i = k - 2; i >= 0; i--){
       for(int j = 1 ; j <= n; j++){
           for(int m = j + 1 ; m <= n; m++){
               dp[i][j] += data[j - 1]*dp[i + 1][m];
           }
       }
   }
   int total = 0;
   for(int i = 1; i <= n; i++){
       total += dp[0][i];
   }
   return total;
}

【讨论】:

  • @JanDvorak 是的,天真的方法是 O((n!)/(k!)(n-k)!),这是多项式 O(k*n^2)
  • 对不起;我把指数误读为在某些参数中是指数的。
  • 此解决方案是否仅适用于具有差值 = 1 且第一个元素为 1 的算术级数中的元素的数组?
  • @JanDvorak 没问题 :)
  • 为什么是 O(k * n^2)?在 O(k * n) 中实现这一点很简单,状态 f(i, j) = 前 i 个元素的所有 size-j 子集的乘积之和。然后我们有f(i, j) = f(i-1, j-1) * a[i] + f(i-1, j),一个 O(1) 转换。
【解决方案3】:

我也遇到了同样的问题。我也通过 Vieta 的算法找到了解决方案。我调整了算法而不是计算不需要的多项式的系数。复杂度为 O(N*M)。我研究了 DFT 和 FFT,但如果 M 足够小,这种方法将比快速傅里叶变换算法更快。这是java顺便说一句。

public BigInteger sumOfCombMult(Integer[] roots, int M)
{
    if (roots.length < M)
    {
        throw new IllegalArgumentException("size of roots cannot be smaller than M");
    }

    BigInteger[] R = new BigInteger[roots.length];

    for (int i = 0; i < roots.length; i++)
    {
        R[i] = BigInteger.valueOf(roots[i]);
    }

    BigInteger[] coeffs = new BigInteger[roots.length + 1];

    coeffs[0] = BigInteger.valueOf(roots[0]);

    int lb = 0;

    for (int i = 1; i < roots.length; i++)
    {
        lb = Math.max(i - M, 0);

        coeffs[i] = R[i].add(coeffs[i - 1]);

        for (int j = i - 1; j > lb; j--)
        {
            coeffs[j] = R[i].multiply(coeffs[j]).add(coeffs[j - 1]);

        }

        if (lb == 0)
        {
            coeffs[0] = coeffs[0].multiply(R[i]);
        }
    }

    return coeffs[roots.length - M];
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-12-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-02-24
    相关资源
    最近更新 更多