An Introduction to C++#

This week, we start looking at a new programming language, C++. In these exercises, you will get a chance to solve some exercises using this new language. As the goal is to get you familiarized with the C++ syntax, and how to compile and run the programs, the exercises themselves are not that technical.

If you have not set up your computer for C++ programming yet, have a look at the Setup guide. To refresh the basic concepts about C++, we refer to the notes at An introduction to C++.

Exercise 6 (Hello World)

a) Create a file hello_world.cpp and write code that writes out a small message to the user when run.

b) Compile your file through the terminal. This is explained in detail in the introduction to C++ material.

c) Execute your compiled program and verify that it works.

Exercise 7 (Reduction by halves)

a) Define an integer \(n=100\)

b) Define a while loop that first prints out \(n\), then divides it by 2, until it reaches 0.

c) Compile and run your program. It should print out \(100, 50, 25, ..., 1.\)

d) Change your program so that the while loop is inside a function, you should use the following signature:

void reduction_by_halves(int n)
{
    ...
}

Note that we use void, as the function should not return anything.

e) Call your function from the main function with \(n=1000\).

Exercise 8 (Stirling’s Approximation)

Stirling’s approximation is a much-used approximation for the logarithm of factorials. It is given by

\[\ln x! \approx x\ln x - x.\]

a) Define a function stirling that takes in the integer \(x\), and returns the approximation as a double.

b) Define the values \(x = (2, 5, 10, 50, 100, 1000)\) as a vector. Use a for loop to iterate through these values and compute Stirling’s approximation for each one.

c) For each value, compare the approximation to the correct value of \(\ln x!\). You can compute the correct value by using std::lgamma by including the <cmath> header, note that \(\ln x!\) is given by lgamma(x+1).

Exercise 9 (Triangle Numbers)

The \(n\)’th triangle number is the sum of the integers from 1 up to and including \(n\).

a) Define a function triangle that takes in an integer \(n\), and returns the \(n\)’th triangle number. The function should use a loop and calculate the sum explicitly

b) Call the function from the main loop, and verify that it gives the right answer for the first 5 triangle numbers.

c) Check that the function gives the correct value for \(n=761\). To see if the value is correct, you can use the analytical expression for the \(n\)’th triangle number, which is \(n(n+1)/2.\)

d) Change your main function so that it asks the user for a number when executed. To ask for input, you can use cin as follows:

int n;

cout << "Please enter a number: ";
cin >> n;

Exercise 10 (Linspace)

You might be familiar with the Python function np.linspace(a, b), which gives a NumPy array of 50 linearly spaced points between \(a\) and \(b\) inclusive.

a) Define a function with the signature

vector<double> linspace(double a, double b)

It should return a vector with 50 linearily spaced points, just like np.linspace.

b) The function np.linspace can also take a third argument if we want a different number of points than 50, so np.linspace(0, 2*pi, 1000) would give 1000 points for example. Overload your linspace function by implementing the signature:

vector<double> linspace(double a, double b, int n)

c) Verify that you can call on linspace with either 2 or 3 arguments. And that the results are as expected in either case.

Exercise 11 (Finite Differences)

In this exercise, you will numerically solve the exponential decay ODE model:

\[\frac{{\rm d}u}{{\rm d}t} = -au,\]

using C++.

a) Allocate two arrays, \(u\) and \(t\), each with 10001 elements.

b) Let \(u_0 = 15.7\) and \(a=4.3\).

c) Compute \(t_i\) and \(u_i\) for \(i = 1, \ldots, 10001\), with a time step \(\Delta t = 0.001\).

Hint: Use a for-loop, and a forward Euler scheme, which you can derive from the expression:

\[\frac{u_{i+1} - u_i}{\Delta t} = -au_i.\]

Additional exercises - Challenges#

If you have finished all the exercises for this week, but want to do more, the following exercises are an additional challenge.

Exercise 12 (Challenge 1 - Checking Primality by Trial Division)

Integers have many different mathematical properties that could be of interest, in this exercise we will look at prime numbers. Wikipedia’s definition of a prime number is as follows:

A prime number (or a prime) is a natural number greater than 1 that has no positive divisors other than 1 and itself. A natural number greater than 1 that is not a prime number is called a composite number. As an example, 5 is prime because 1 and 5 are its only positive integer factors, whereas 6 is composite because it has the divisors 2 and 3 (in addition to 1 and 6).

The goal of this exercise is to write a function is_prime(n) that takes an integer and checks if it is prime. We will start off with the naive solution and then make it more effective.

Checking divisibility

We first have to check if an integer is divisible by another integer. For any two integers, \(a\) and \(b\), we can write \(a/b\) as

\[\frac{a}{b} = N + \frac{r}{b},\]

where \(N\) is an integer denoting how many times \(a\) evenly divides into \(b\), and \(r\) is a non-negative integer smaller than \(b\), so that \(r/b\) is a decimal number in the interval \([0, 1)\).

For example, if \(a\) is 7 and \(b\) is 3 we get

\[\frac{7}{3} = 2 + \frac{1}{3}.\]

So \(N=3\) and \(r=1\). We call \(r\) the remainder of the division. We say that \(a\) is divisible by \(b\) if there is no remainder, i.e., \(r = 0\).

We can find \(N\) by integer division, and \(r\) by using the modulo operator.

Exercise (a)

The naive solution to checking whether a number is a prime is to follow the definition of prime numbers and simply check every integer from 2 up to (but not including) the number itself.

Define a function is_prime that checks if a positive integer \(n\) is prime using the naive solution. Use it to find the first ten prime numbers.

Hints:

  1. You can handle the edge cases of n equal to 1, 2 and 3 by simply testing

  2. Be careful not to test divisibility by 1 or \(n\) itself, as all numbers are divisible by 1 and themselves

  3. If you find a number that evenly divides \(n\), you can immediately return false as the number is not a prime, no need to check all the numbers in this case.

Optimization

If implemented correctly, is_prime can now check the primality of any positive integer \(n\), but as we have to loop over all numbers from 1 to n (exclusive), it will be very slow for large \(n\). If we for example want to find all primes below 1 billion, we would have to loop over a lot of numbers. In the following exercises, you will improve your algorithm to make it a bit more efficient.

Exercise (b)

Our naive solution might be very basic and slow, but we are fairly sure it works precisely because of its simplicity, and we tested it by finding the first 10 primes. As we now go through and optimize our code, we must make sure we do not break our functionality. Write a test block or function that you can use after each change you make to ensure that while our function is getting faster, we are not changing its functionality.

Exercise (c)

The first thing we can ask is if we really need to check all numbers in the range \([2, n)\). Let us look at an example, 100 is a composite number and can be written out as the following products:

  1. \(2×50\)

  2. \(4×25\)

  3. \(5×20\)

  4. \(10×10\)

  5. \(20×5\)

  6. \(25×4\)

  7. \(50×2\)

Note that we find 7 ways to create 100 by multiplying 2 integers. However, the final three versions are just the first three ones flipped around. This will be the case for any number. In this case, the turning point is \(\sqrt{N}\) because \(100\) is a square number, but from this, we see that for any number \(n\) that can be written as a product of two divisors, at least one of them will be smaller or equal to \(\sqrt{n}\). Thus we can reduce our range of numbers to check from \([2, \sqrt{n}]\). For large numbers, such as \(10^6\), this is a huge reduction.

Update your is_prime function so that you only check the range [2, √n], and verify that your function still handles your test block/function from exercise (c).

Exercise (d)

We just narrowed down the range of numbers we check considerably, but do we have to check every single number in the range \([2, \sqrt{n}]\)? You have probably noticed that all primes, except for 2, are odd numbers (as opposed to being even). Why is this? It happens because all even numbers are evenly divisible by 2, and so are not prime. This means we only have to check odd numbers. But even better, this also means we only have to check odd-valued divisors to check primality (why? Try to convince yourself why this is, or discuss it with a classmate).

Update your function so that you check if \(n\) is even before you start looping over your divisor candidates. And then update your loop so that it skips all even numbers in the range \([3, \sqrt{n}]\). And then verify that your function still handles your test block/function from exercise (c).

Exercise (e)

By checking if a number was even, we could effectively skip checking half of the possible divisors, which is a very nice reduction. We can go one step further by also considering the number 3. No numbers that are divisible by \(3\) can be prime (other than 3 itself).

To implement this, note that any integer \(n\) can be written on exactly one of the six following forms:

  • \(n = 6k - 2\)

  • \(n = 6k - 1\)

  • \(n = 6k\)

  • \(n = 6k + 1\)

  • \(n = 6k + 2\)

  • \(n = 6k + 3\)

where \(k\) is an integer. As \(n\) grows, so does \(k\). Now, note that the first, third and fifth possibilities are instantly discardable because they are even. This leaves us with the fact that all primes can be written as:

  • \(n = 6k - 1\)

  • \(n = 6k + 1\)

  • \(n = 6k + 3\)

Of these remaining three, note that the last option is divisible by 3 for all \(k\), so we can disregard them as well. This means all primes can be written as the two possibilities:

  • \(n = 6k - 1\)

  • \(n = 6k + 1\)

This also means all possible candidate divisors are on one of these two forms (as all divisors are prime).

Update your is_prime function so that when looping over candidate divisors, you only check the candidates on the form \(6k\pm1\). Run your test block/function to verify your code.

Project Euler Prime Number Problems

You have now implemented a fairly optimized function for checking if a number is prime by trial division. Let us use your work to solve some Project Euler problems.

Exercise (f)

(This is the 7th problem of Project Euler.)

By listing the first six prime numbers: 2, 3, 5, 7, 11, and 13, we can see that the 6th prime is 13.

What is the 10001st prime number?

Exercise (g)

(This is the 10th problem of Project Euler.)

The sum of all primes below 10 is 2 + 3 + 5 + 7 = 17.

Find the sum of all the primes below two million.