If there are 18 people in your class and you want to divide the class into programming teams of 3
members, you can compute the number of different teams that can be arranged using this formula
(π! π!(πβπ)!).
Write a C++ program that determines the number of potential team arrangements. You will need
to use the double type variable for this computation.
The binomial coefficient "n over k" tells you how many different ways there are to pick k elements from a set of n elements. You can look up the correct formulae to compute the binomial coefficient on Wikipedia.
You obviously will need to implement the factorial (i.e. n!) function, because it's needed for the computation.
The double type needs to be used for the final division, as you do not want integer division here!
(Anything before the final division can be done with integers)
#include <iostream>
double nCr( int n, int r ) { return r ? nCr( n - 1, r - 1) * n / r : 1.0; }
int main()
{
std::cout << nCr( 18, 3 ) << '\n';
}
You don't strictly need a double, either.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
#include <iostream>
unsignedlonglong nCr( int n, int r )
{
if ( r > n - r ) return nCr( n, n - r ); // slightly more efficient
unsignedlonglong result = 1;
for ( int i = 1, j = n; i <= r; i++, j-- ) result = result * j / i;
return result;
}
int main()
{
std::cout << nCr( 18, 3 ) << '\n';
}
#include <iostream>
#include <utility>
using T = unsignedlonglong;
T ncr(T n, T r) {
if (r > n)
std::swap(n, r);
T res { 1 };
for (; r >= 1; --r, --n)
res = (res * n) / r;
return res;
}
int main() {
std::cout << "18C3: " << ncr(18, 3) << '\n';
}
Your code is wrong, @seeplus and the problem is here:
1 2
for (; r >= 1; --r, --n)
res = (res * n) / r;
Try 18C5 instead - it will produce the wrong answer.
To avoid problems with integer division (i.e. ensure that the required factors are already present in the result) you have to work the divisor upwards from 1, not downwards from the original r.
Your code only works for 18C3 because, fortuitously, 18 happens to have a factor of 3 ... it doesn't have a factor of 5.
The lines
1 2
if (r > n)
std::swap(n, r);
also do not make sense. 3C18 does not exist, for example. There is no question of it being 18C3.
#include <iostream>
#include <algorithm>
using T = unsignedlonglong;
constexpr T ncr(T n, T r) {
T num { n }, dom { r = std::min(r, n - r) };
for (; r > 1; num *= --n, dom *= --r);
return num / dom;
}
int main() {
std::cout << "18C5: " << ncr(18, 5) << '\n';
std::cout << "18C4: " << ncr(18, 4) << '\n';
std::cout << "18C3: " << ncr(18, 3) << '\n';
}
#include <iostream>
#include <valarray>
usingnamespace std;
using ARRAY = valarray<unsignedlonglong>;
ARRAY Pascal( int n )
{
ARRAY result( 0ull, n + 1 ); result[0] = 1;
for ( int i = 1; i <= n; i++ ) result += result.shift( -1 );
return result;
}
int main()
{
for ( auto e : Pascal( 18 ) ) cout << e << " ";
}
The extra range of a long long certainly buys you more time. However, despite the formula, avoiding ever having to store r! allows you to calculate to much higher values. The Pascalβs Triangle approach is useful here, as it is purely additive.
Also make unsigned rather than signed. See lastchance's 2 versions and my version above.
As you are effectively decrementing r whilst positive, the smaller the value of r the fewer times round the loop - hence fewer multiplications and the higher the value of n that can be used without overflow.
As nCr is the same as nC(n-r), then for i use the minimum of either r or (n - r).
#include <iostream>
#include <map>
#include <boost/multiprecision/cpp_int.hpp>
boost::multiprecision::cpp_int nCr( unsignedint n, unsignedint r )
{
static std::map< std::pair< unsignedint, unsignedint >, boost::multiprecision::cpp_int > memo ;
boost::multiprecision::cpp_int& result = memo[ {n,r} ] ;
if( n == r || r == 0 ) result = 1 ; // base case
elseif( r == 1 ) result = n ; // base case
elseif( result == 0 ) result = nCr( n-1, r ) + nCr( n-1, r-1 ) ; // recurse
return result ;
}
int main()
{
std::cout << nCr( 1000, 300 ) << '\n' ;
}