问题
I want to calculate multinomial coefficient mod 1e9 + 7. It equals: n! / (k0! * k1! * k2 * ... * km!)
In my case m = 3, k0 + k1 + k2 = n, so it would be: n! / (k0! * k1! * k2!) My code for this:
....
long long k2 = n - k1 - k0;
long long dans = fact[n] % MOD;
long long tmp = fact[i] % MOD;
tmp = (tmp * fact[j]) % MOD;
tmp = (tpm * fact[k]) % MOD;
res = (fact[n] / tmp) % MOD; // mb mistake is here...
cout << res;
fact[i] - factorial of i mod 1e9+7 It does not work on big tests
回答1:
I hope I'm not linkfarming here, but here is a process of work, to solve your problem :
Naive implementations will always suffer from overflow errors. You have to be ready to exploit certain mathematical properties of the polynomial coefficient to reach a robust solution. Dave Barber does that in his library, where the recursive property is used (example for 4 numbers - the recursion stops when all branches are replaced by zero)
multi (a, b, c, d) = multi (a − 1, b, c, d) + multi (a, b − 1, c, d) + multi (a, b, c − 1, d) + multi (a, b, c, d − 1)
Based on the above, David Serrano Martínez shows how an implementation that provides overflow control can be divised. His code can be used as easily as
unsigned long long result_2 = multinomial::multi<unsigned long long>({9, 8, 4});
A third alternative would be to use (or learn from) libraries that are dedicated to combinatorics, like SUBSET. This is a bit more difficult code to read through due to dependencies and length, but invokation is as easy as
int factors[4] = {1, 2, 3, 4}; Maths::Combinatorics::Arithmetic::multinomial(4, factors)
回答2:
You can calculate
by multiplying the numerator down fromsum(ks)
and dividing up in the denominator up from 1
. The result as you progress will always be integers, because you divide by i
only after you have first multiplied together i
contiguous integers.
def multinomial(*ks):
""" Computes the multinomial coefficient of the given coefficients
>>> multinomial(3, 3)
20
>>> multinomial(2, 2, 2)
90
"""
result = 1
numerator = sum(ks)
ks = list(ks) # These two lines are unnecessary optimizations
ks.remove(max(ks)) # and can be removed
for k in ks:
for i in range(k):
result *= numerator
result //= i + 1
numerator -= 1
return result
回答3:
I recently came across this problem, and my solution was to first map to log-space, do the work there, and then map back. This is helpful as we avoid the overflow issues in log-space, and also multiplications become sums which can be more efficient. It may also be useful to work directly with the log-space result.
The maths:
C(x1, ..., xn) = sum(x)! / (x1! * ... * xn!)
Therefore
ln C(x1, ..., xn) = ln sum(x)! - ln {(x1! * ... * xn!)}
= sum{k=1->sum(x)} ln k - sum(ln xi!)
= sum{k=1->sum(x)} ln k - sum(sum{j=1->xi} (ln j))
If any of the xi
, or sum(x)
are big (e.g. > 100), then we could actually just use Sterling's approximation:
ln x! ~= x * ln x - x
Which would give:
ln C(x1, ..., xn) ~= sum(x) * ln sum(x) - sum(x) - sum(xi * ln xi - xi)
Here's the code. It's helpful to first write a log factorial helper function.
#include <vector>
#include <algorithm> // std::transform
#include <numeric> // std::iota, std:: accumulate
#include <cmath> // std::log
#include <type_traits> // std::enable_if_t, std::is_integral, std::is_floating_point
template <typename RealType, typename IntegerType,
typename = std::enable_if_t<std::is_floating_point<RealType>::value>,
typename = std::enable_if_t<std::is_integral<IntegerType>::value>>
RealType log_factorial(IntegerType x)
{
if (x == 0 || x == 1) return 0;
if (x == 2) return std::log(2); // can add more for efficiency
if (x > 100) {
return x * std::log(x) - x; // Stirling's approximation
} else {
std::vector<IntegerType> lx(x);
std::iota(lx.begin(), lx.end(), 1);
std::vector<RealType> tx(x);
std::transform(lx.cbegin(), lx.cend(), tx.begin(),
[] (IntegerType a) { return std::log(static_cast<RealType>(a)); });
return std::accumulate(tx.cbegin(), tx.cend(), RealType {});
}
}
Then the log factorial function is simple:
template <typename RealType, typename IntegerType>
RealType log_multinomial_coefficient(std::initializer_list<IntegerType> il)
{
std::vector<RealType> denoms(il.size());
std::transform(il.begin(), il.end(), denoms.begin(), log_factorial<RealType, IntegerType>);
return log_factorial<RealType>(std::accumulate(il.begin(), il.end(), IntegerType {})) -
std::accumulate(denoms.cbegin(), denoms.cend(), RealType {});
}
And finally the multinomial coefficient method:
template <typename RealType, typename IntegerType>
IntegerType multinomial_coefficient(std::initializer_list<IntegerType> il)
{
return static_cast<IntegerType>(std::exp(log_multinomial_coefficient<RealType, IntegerType>(std::move(il))));
}
e.g.
cout << multinomial_coefficient<double, long long>({6, 3, 3, 5}) << endl; // 114354240
For any inputs much greater than this we are going to overflow with built in types, but we can still obtain the log-space result, e.g.
cout << log_multinomial_coefficient<double>({6, 3, 11, 5, 10, 8}) << endl; // 65.1633
来源:https://stackoverflow.com/questions/22892138/calculate-multinomial-coefficient