问题
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 from sum(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