【问题标题】:Unable to calculate number of combinations due to very large factorial由于非常大的阶乘,无法计算组合数
【发布时间】:2015-12-09 15:37:22
【问题描述】:

我正在尝试计算某个数组中元素数的组合数。我需要确切的组合数才能将其用作要在 GPU 中执行的线程数。

但是数据非常大,任何数据类型的大数都无法计算阶乘。

有没有一种方法可以计算组合的数量而无需找到阶乘?还是更有效的方法?

总结了问题:

int no_of_combinations = combination(500,2);
public static int factorial(int m)
{
        int x = 1;

        for (int i = m; i > 0; i--)
            x = x * i;

        return x;
}
public static int combination(int m, int n)
{
    int x = 0;
    x = factorial(m) / (factorial(n) * factorial(m - n));
    return x;
}

【问题讨论】:

  • 我不确定这是否会有所帮助,但您是否查看了斯特林的近似值?它是阶乘的近似值。
  • 如果你连一个已知数据类型的数字都不能表示,你要怎么做同样数量的线程?
  • @HansP,他在公式中使用阶乘。线程数(公式的结果)可能还可以(合理)。
  • 我建议事先简化方程,而不是尝试评估 500!,看看你是否可以简化分数
  • 从评论中你的问题更像是Calculate factorials in C#的重复

标签: c# algorithm combinations factorial


【解决方案1】:

在这种情况下,我将开始简化等式。在您的示例中,您正在寻找 500 选择 2,即 500!/498!/2!。这个可以很方便的改成500*499/2,可以计算出来。

一般来说如果你有n个选择k,你只需要计算一个从nmax(k, nk)的“部分阶乘” 然后除以 min(k, nk)! 因为结果被镜像了。这使得计算更加容易。

此外,在某些情况下,您可以在乘法时用 min(k, n-k)! 开始除法,但这会导致余数等。

【讨论】:

  • 其实这也是一种有效的方法,不过最好使用主要除数,使用拉根德公式:en.wikipedia.org/wiki/Legendre%27s_formula
  • @BorisStrandjev:从什么意义上说更好?例如,运行时间是多少?
  • @DouglasZare 你可以在我建议的方法中计算出更多的数字,而不必担心溢出。单个组合的运行时间会较慢,因为您需要预先计算埃拉托色尼筛,但对于多个计算,它会更快。
  • 运行时间是多少,需要多少空间?要说它比我给出的方法更好,您至少应该匹配在 min(n-k,k) 或恒定空间中线性的运行时间。在我看来,它既慢(在实践中和理论上)又占用更多空间,即使您允许大量预计算。这是 Lolcode 中的一个实现:thenerdiestshirts.ecrater.com/p/12755673/…。使用勒让德公式需要多少代码,提出原始问题的人正确实现它的机会有多大?
【解决方案2】:

使用帕斯卡的三角形属性:

C(n,k) = C(n - 1, k) + C(n - 1, k - 1) 和动态规划。不涉及阶乘。

帕斯卡三角形存在:

        1
      1   1
    1   2   1
  1   3   3   1
1   4   6   4   1

【讨论】:

    【解决方案3】:

    您不需要使用阶乘。如果 k>n/2,则使用 C(n,k)=C(n,n-k)。然后使用 C(n,0)=1 并且对于 k>0,C(n,k) = C(n,k-1) * (n-k+1)/k。这使您可以计算几乎与动态规划方法一样多的二项式系数,但它需要线性时间 (Theta(min(n-k,k))) 和恒定空间,而不是二次时间和线性空间。

    查看过去的问题:How to efficiently calculate a row in pascal's triangle?

    public static long combination(int n, int k)
    {
      if (n-k < k)
        return combination(n,n-k);
      if (k < 0)
        return 0;
      long result = 1;
      for (int i=1; i<=k; i++)
      {
        result *= n-i+1;
        result /=i;
      } 
      return result;
    }
    

    如果回答时间 n 超过最大 long,这可能会溢出。因此,如果您希望答案适合 32 位 int 并且您有 64 位 long,那么这不应该溢出。为避免溢出,请使用 BigIntegers 而不是 longs。

    【讨论】:

      【解决方案4】:

      你需要写一个新函数,我们称之为 FactorialMoverN

      int FactorialMOverN(int m, int n)
      {
          int x = 1;
              for (int i = m; i > n; i--)
                  x = x * i;
          return x;
      }
      

      然后将你的组合函数改为

      x = FactorialMOverN(m,n) * factorial(m - n));
      

      这应该会有所帮助。如果没有帮助,那么您需要使用不同的变量类型,或者重新考虑您的问题。

      感谢 Sami,我可以看到上面的函数有错误。 500选2需要通过

      计算
      int MChooseN(int m, int n)
          {
              int x = 1;
                  for (int i = m; i > (m-n); i--)
                      x = x * i;
              return x;
          }
      

      上面将采用 500, 2 并返回 500*499,前一个将采用 500,2 并返回 500*499*498...5*4*3 这不是您想要的。

      无论如何,以上是你能得到的最好的。

      【讨论】:

      • 这在例如 500 选择 498 的情况下无济于事。添加一个简单的最小值/最大值将解决此问题。
      • 是的,我很困惑,因为我只看他的代码,而不是他问的实际问题。 +1 给你。
      • 把它变成i = m; j &gt; max(n, m-n) 类型,它对500,2和500,498都是一样的,结果是一样的。
      猜你喜欢
      • 2014-09-23
      • 1970-01-01
      • 1970-01-01
      • 2020-06-27
      • 2016-11-29
      • 2010-11-25
      • 2023-03-22
      相关资源
      最近更新 更多