Java库中是否有内置方法可以计算任意N、R的组合数'C(N,R)'?
实际上,即使不计算阶乘,也很容易计算N choose K
。
我们知道(N choose K)
的公式为:
N!
--------
(N-K)!K!
因此,(N choose K+1)
的公式为:
N! N! N! N! (N-K)
---------------- = --------------- = -------------------- = -------- x -----
(N-(K+1))!(K+1)! (N-K-1)! (K+1)! (N-K)!/(N-K) K!(K+1) (N-K)!K! (K+1)
那是指:(N choose K+1) = (N choose K) * (N-K)/(K+1)
我们也知道 (N choose 0)
是:
N!
---- = 1
N!0!
因此,这给了我们一个简单的起点,使用上面的公式,我们可以找到任何K>0
的(N choose K)
,只需进行K
次乘法和K
次除法。
将以上内容结合起来,我们可以轻松地生成Pascal三角形,具体如下:
for (int n = 0; n < 10; n++) {
int nCk = 1;
for (int k = 0; k <= n; k++) {
System.out.print(nCk + " ");
nCk = nCk * (n-k) / (k+1);
}
System.out.println();
}
这将打印:
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
1 5 10 10 5 1
1 6 15 20 15 6 1
1 7 21 35 35 21 7 1
1 8 28 56 70 56 28 8 1
1 9 36 84 126 126 84 36 9 1
BigInteger
版本使用 BigInteger
的公式很简单:
static BigInteger binomial(final int N, final int K) {
BigInteger ret = BigInteger.ONE;
for (int k = 0; k < K; k++) {
ret = ret.multiply(BigInteger.valueOf(N-k))
.divide(BigInteger.valueOf(k+1));
}
return ret;
}
//...
System.out.println(binomial(133, 71));
// prints "555687036928510235891585199545206017600"
根据谷歌(Google)的说法,133个数中选择71个数的组合数 = 5.55687037 × 1038。
Apache Commons的“Math”库可以实现这一功能,使用方法如下: org.apache.commons.math4.util.CombinatoricsUtils
我只是试图计算不同牌堆大小的2张牌组合数量...
无需导入外部库-根据组合的定义,使用 n
张卡牌会得到 n*(n-1)/2
种组合。
奖励问题:这个公式也可以计算前 n-1
个整数的和 - 你看出它们为什么相同了吗? :)
n-1
种与另一张牌配对的方法,第二张牌有n-2
种与剩余牌配对的方法,以此类推。) - BlueRaja - Danny PflughoeftbinomialCoefficient
,在Commons Math中,返回二项式系数的精确表示,即从n个元素集合中选择k个元素的组合数。
返回二项式系数的精确表示,即从n个元素集合中选择k个元素的组合数。
N!/((R!)(N-R)!)
在这个公式中,有很多可以约分的部分,所以通常阶乘不是问题。假设R > (N-R),那么将N!/R!约分为(R+1) * (R+2) * ... * N。但是,int类型非常有限(大约只能到13!)。
但是,每次迭代也可以进行除法运算。伪代码如下:
d := 1
r := 1
m := max(R, N-R)+1
for (; m <= N; m++, d++ ) {
r *= m
r /= d
}
即使这看起来是多余的,但从一开始划分非常重要。让我们举一个例子:
for N = 6, R = 2: 6!/(2!*4!) => 5*6/(1*2)
r := max(R, N-R)+1
for (m := r+1,d := 2; m <= N; m++, d++ ) {
r *= m
r /= d
}
inline unsigned long long n_choose_k(const unsigned long long& n,
const unsigned long long& k)
{
if (n < k) return 0;
if (0 == n) return 0;
if (0 == k) return 1;
if (n == k) return 1;
if (1 == k) return n;
typedef unsigned long long value_type;
value_type* table = new value_type[static_cast<std::size_t>(n * n)];
std::fill_n(table,n * n,0);
class n_choose_k_impl
{
public:
n_choose_k_impl(value_type* table,const value_type& dimension)
: table_(table),
dimension_(dimension)
{}
inline value_type& lookup(const value_type& n, const value_type& k)
{
return table_[dimension_ * n + k];
}
inline value_type compute(const value_type& n, const value_type& k)
{
if ((0 == k) || (k == n))
return 1;
value_type v1 = lookup(n - 1,k - 1);
if (0 == v1)
v1 = lookup(n - 1,k - 1) = compute(n - 1,k - 1);
value_type v2 = lookup(n - 1,k);
if (0 == v2)
v2 = lookup(n - 1,k) = compute(n - 1,k);
return v1 + v2;
}
value_type* table_;
value_type dimension_;
};
value_type result = n_choose_k_impl(table,n).compute(n,k);
delete [] table;
return result;
}
N!/((R!)(N-R)!)
ArithmeticUtils.factorial
现在已经被弃用。请尝试使用CombinatoricsUtils.binomialCoefficientDouble(n,r)
80658175170943878571660636856403766975289505440883277824000000000000
吗?因为根据你所接受的答案,你似乎没有考虑到该公式中涉及到的数字有多大。顺便提一下,两张卡牌的答案是(n*(n-1))/2。你不需要完整实现“n选r”就能得到这个答案。 - Mark Byers