factorial(1:10) [1] 1 2 6 24 120 720 5040 40320 362880
[10] 3628800
For this lab, we will implement a class representing a binomial distribution:
\[ \Pr{\left(Y=k; n, p\right)} = {n \choose k}p^k(1-p)^{n-k} \]
Implement the following class representing a binomial distribution:
class Binom {
private:
int n;
double p;
public:
// Binom(int n_, double p_) : n(n_), p(p_) {};
Binom(int n_, double p_) {
n = n_;
p = p_;
};
int factorial(int k) const;
double choose(int a, int b) const;
double dbinom(int k) const;
void print(int k) const;
};Remember that to implement the function, you have to use the following pattern:
inline [return type] [class name]::[function name]([arguments]) {
// Your code here
}C++ has no equivalent of R’s ?pow or apropos(). The references below can be used to look up functions, their signatures, and the headers they live in.
<cmath> (pow, sqrt, exp, log, …). The pages open with the full set of overloads and can look intimidating. Scroll down to Parameters, Return value, and especially the compilable Example at the bottom.Write a program that implements the factorial function. It should match the following results in R
factorial(1:10) [1] 1 2 6 24 120 720 5040 40320 362880
[10] 3628800
You can use the following code to test your function:
Binom b(10, 0.5);
for (int i = 0; i < 10; i++) {
std::cout << b.factorial(i) << std::endl;
}Write a program that implements the choose function. It should match the following results in R
choose(10, 1:10) [1] 10 45 120 210 252 210 120 45 10 1
You can use the following code to test your function:
Binom b(10, 0.5);
for (int i = 0; i < 10; i++) {
std::cout << b.choose(10, i) << std::endl;
}If your results come out as 0, or as whole numbers where R gives you something else, the likely cause is integer division. In C++ the / operator looks at the types of its operands, not at what you meant. If both sides are int, you get integer division and the remainder is thrown away:
int a = 7, b = 2;
a / b; // 3, not 3.5 -- the .5 is discardedSince factorial returns an int, an expression such as
return factorial(a) / (factorial(b) * factorial(a - b));divides one int by another, truncates, and only then converts the result to the double the function returns. The fix is to make at least one operand a double before the division happens, which is what static_cast is for:
return static_cast<double>(factorial(a)) /
(static_cast<double>(factorial(b)) * static_cast<double>(factorial(a - b)));static_cast<T>(expression) converts expression to type T at compile time. It is the C++ way of writing the C-style cast (double) x, and it is preferred because it is easy to search for and because the compiler checks that the conversion actually makes sense. Note that the cast has to be on the operands, not on the result: static_cast<double>(a / b) is too late – the truncation has already happened inside the parentheses.
One more thing to watch: think about what your function should return when b < 0 or b > a. R’s choose gives 0 there.
Write a program that implements the dbinom function. It should match the following results in R
dbinom(0:10, 10, 0.5) [1] 0.0009765625 0.0097656250 0.0439453125 0.1171875000 0.2050781250
[6] 0.2460937500 0.2050781250 0.1171875000 0.0439453125 0.0097656250
[11] 0.0009765625
You can use the following code to test your function:
Binom b(10, 0.5);
for (int i = 0; i < 10; i++) {
std::cout << b.dbinom(i) << std::endl;
}C++ has no ^ operator for exponentiation – p ^ k will not do what you want (^ is bitwise XOR, and it does not even compile for double). Use std::pow from the <cmath> header instead:
#include <cmath>
std::pow(0.5, 3); // 0.125
std::pow(2.0, 10); // 1024std::pow(base, exponent) returns a double, and both arguments are converted to floating point first, so passing an int exponent such as your k is fine.
For the binomial density you need two of these, one for the successes and one for the failures:
\[ P(Y = k) = \binom{n}{k} \, p^k \, (1 - p)^{\,n - k} \]
so the second term has exponent n - k. Both are ints, which is no problem: the subtraction is done in integer arithmetic and the result is then converted for std::pow.
Two small things to watch:
std::pow lives in <cmath>, so make sure that header is included at the top of your file.std::pow(1 - p, n - k) is the failure term; something like std::pow(1 - p, n) - k compiles just as happily and is not what you meant, since everything involved is a number.Write a program that implements the print function. It should match the following results in R
sprintf(
"P(Y=%-2d; n=%d, p=%.2f) = %.4f",
0:10, 10, 0.5, dbinom(0:10, 10, 0.5)
) |>
cat(sep = "\n")P(Y=0 ; n=10, p=0.50) = 0.0010
P(Y=1 ; n=10, p=0.50) = 0.0098
P(Y=2 ; n=10, p=0.50) = 0.0439
P(Y=3 ; n=10, p=0.50) = 0.1172
P(Y=4 ; n=10, p=0.50) = 0.2051
P(Y=5 ; n=10, p=0.50) = 0.2461
P(Y=6 ; n=10, p=0.50) = 0.2051
P(Y=7 ; n=10, p=0.50) = 0.1172
P(Y=8 ; n=10, p=0.50) = 0.0439
P(Y=9 ; n=10, p=0.50) = 0.0098
P(Y=10; n=10, p=0.50) = 0.0010