CSCI 159 Lab 4 exercises
Lab 4 is due prior to the start of your lab 5's on Nov. 3rd/5th
As usual, there are two halves (first half in basic.cpp and second half in lab4.cpp), where the second half is an
extended independent design section.
- the basic/warmup exercise (basic.cpp), worth 5 marks
- the design/implementation exercise (lab4.cpp), worth 10 marks
- both parts are due before the start of lab 5 (Nov. 3/5).
Hopefully most people complete nearly the entire first half of lab 4 in their scheduled lab section
on Oct. 13th/15th.
Here is the collection of new C++ syntax elements we'll be using
for lab4.
Follow our usual setup process
- Log in, open a browser, go to the lab4 page, open a terminal window:
(see lab 2 if you need a refresher on any of the steps)
- get lab4:
make -f make159 csci159/lab4
- Go into your lab4 directory and begin the edit/compile/test/submit routine:
for the first half of today's lab:
- to edit basic.cpp: pluma basic.cpp &
- to compile basic.cpp: make basicx
- to test basicx: ./basicx
- to submit: make submit
once we get to the second half of today's lab:
- to edit lab4.cpp: pluma lab4.cpp &
- to compile lab4.cpp to create lab4x: make lab4x
- to test lab4x: ./lab4x
- to submit: make submit
As with previous labs, you'll see that the two .cpp files are nearly empty to start with.
First half: the basics of loops and booleans (to be done in basic.cpp)
We'll be using the string and iostream libraries for basic.cpp, and writing three functions:
- skipThis (takes a string parameter and returns a char): the string parameter describes
a section (step one or step two), and the function
asks the user Y/N do they wish to skip this section. It gets their response (using a do while
loop to repeat until they answer one of YyNn) and returns true if they wish to skip, false otherwise.
- stepOne (takes no parameters and returns a float): the function asks the user to
enter a number > 1, gets and checks their response (Using a while loop to repeat
until a valid value is given), and returns the eventual valid answer.
- stepTwo (takes no parameters and has a void return type): this uses stepOne to
get a number from the user then uses a for loop to calculate a series of better and better
estimates for the square root of that number.
We'll need several constants as well:
- a const char, 'Y', specifying which character represents yes
- a const char, 'N', specifying which character represents no
- an integer, 10, specifying how many estimates our root calculation will work through
- an integer, 80, specifying how many characters to discard on bad input
The main routine will:
- call skipThis("step one") and check the return value to see if the user wishes to
skip step one. If skipThis returns false then we do want to try stepOne
here, so the test will look something like
if (!skipThis("step one")) {
If the user didn't skip then we want to call stepOne and store/print the value
it returns (so we can check it did return the value correctly).
- do similarly for step two, but here the body of the if can simply call stepTwo.
The starting code will thus look something like this (with empty implementations of
the three functions down at the bottom):
// set which characters we'll for yes/no
const char YES = 'Y';
const char NO = 'N';
// set the maximum number of input characters to clear on broken input
const int LineLen = 80;
// how many estimates we'll go through in our square root calculations
const int AccuracyIterations = 10;
// asks the user if they want to skip a section (Y or N)
// repeats the question (using do while) until they enter Y or y or N or n
// returns true if they want to skip, false otherwise
bool skipThis(string section);
// stepOne uses a while loop to get a number greater than one from the user then returns it
// (repeats until a valid value is supplied)
float stepOne();
// stepTwo uses a for loop to approximate the square root of a number
// (each pass through the loop gets a better approximation than the previous pass,
// stepTwo uses stepOne to get the number)
void stepTwo();
int main()
{
// run the two steps, giving the user chances to skip either or both
if (!skipThis("step one by itself")) {
float positiveNum;
positiveNum = stepOne();
cout << "Your number was " << positiveNum << endl;
cout << "End of step one" << endl;
}
if (!skipThis("step two")) {
stepTwo();
}
}
bool skipThis(string section)
{
return true; // <== we'll replace this when we write the real version of the function
}
float stepOne()
{
return 0; // <== we'll replace this when we write the real version of the function
}
void stepTwo()
{
}
|
Writing skipThis
(This code will replace the "return true;" we had in skipThis originally.)
This is meant to prompt the user to enter Y or N (for yes or no) and return
true if they said Y or false if they said N.
We'll use a do-while loop to repeat until they enter something valid,
and we'll convert the character they enter to uppercase (so they can enter y or n
in addition to Y or N).
The nice thing about char input is that it cannot cause cin to fail,
so we don't need to worry about cin.fail, cin.clear, and cin.ignore.
In this case, we'll use a boolean variable (validAnswer) to keep track of whether
or not we've seen valid input from the user. This will be set to false initially
(since we haven't seen any input from the user yet) and will be changed
to true once we have valid input.
Thus our loop structure is something like
char skipOrNot; // the variable where we'll store the user input
bool validAnswer = false; // do we have valid input yet?
do {
// get the user to enter their character, store it in skipOrNot
// convert it to uppercase
// if it's yes then we can simply return true (we're done, they want to skip)
// otherwise if it's no then we can set validAnswer to true
// (we have a valid answer, asking not to skip)
// otherwise we can display an error/try again message
} while (!validAnswer);
return false; // we got out of the loop because they said No to skipping
Remember you have the constants YES and NO, be sure to use those rather
than hard-coding 'Y' and 'N' inside your function.
Converting to uppercase is done by the 'toupper' function (from the cctype library, which
is included for you by the string library), e.g.
myvar = toupper(myvar); // where myvar is a char variable
Writing stepOne
(This code will replace the "return 0;" we had in stepOne originally.)
This will look more like our earlier exercises where a function gets and checks a number,
but we're doing it in a while loop instead of using recursion.
We'll need a float to store the user number in, and (like in skipThis) we'll use a boolean
variable to keep track of whether or not we've obtained a valid value from the user.
Our loop logic can thus look something like:
float finalNum;
bool validNumber = false;
while (!validNumber) {
// do your cout and cin to get the user's input
// if cin.failed then give them an error/try again message
// (but no recursive call this time, since the loop will take care of repeating)
// otherwise if the number is <= 1 give them an error/try again message
// (again, no recursive call in this lab)
// otherwise set validNumber to true, so the loop will stop
// when it gets checked at the beginning of the next pass
}
return finalNum;
Writing stepTwo
We'll need variables to keep track of:
- the number we're computing the root of, e.g. originalNum (a float)
- our latest estimate, e.g. estimate (a float)
- the square of the latest estimate, e.g. square (a float)
- upper and lower bounds on the root values, e.g. lowerBound, UpperBound (also floats)
- which estimate # this is (estimate 1, estimate 2, etc), e.g. estimateNum (an int)
To get started, we need to call stepOne and assign its return value to originalNum,
and as our initial lower/upper bounds on the square root of that we can use 1 and originalNum
(e.g. if originalNum is 12 we can safely assume its square root is somewhere in the range 1..12).
Our for loop will then go through each of the estimates, getting more accurate each time:
for (estimateNum = 1; estimateNum <= AccuracyIterations; estimateNum++) {
// set our new estimate to be (lowerBound + upperBound)/2
// set square to be (estimate * estimate)
// if square is bigger than originalNum
// then set upperBound = estimate
// otherwise if square is smaller than originalNum
// then set lowerBound = estimate
// otherwise
// tell the user we got the exact answer (!)
// and 'break' from the loop
}
output our final estimate, square, and the originalNum we were trying to compute the root of
Sample run
The output below shows a sample run where the user chose to skip step one and entered
12 as the number to compute the root of.
(User input illustrated using bold italics here just for clarity)
Do you want to skip step one by itself (Y or N): y
Do you want to skip step two (Y or N): n
Please enter a number greater than 1.0: 12
Guess number 1: 6.5
Guess number 2: 3.75
Guess number 3: 2.375
Guess number 4: 3.0625
Guess number 5: 3.40625
Guess number 6: 3.57812
Guess number 7: 3.49219
Guess number 8: 3.44922
Guess number 9: 3.4707
Guess number 10: 3.45996
Our final estimate was 3.45996, whose square is 11.9713 (aiming for 12)
|
As always, check you've followed code standards and do a make submit
Second half: design and implementation problem with loops (to be done in lab4.cpp)
We're still a little before the 314th day of the year,
but for this problem you'll be writing a program that makes multiple attempts to approximate
the value of Pi, using a simulation technique and random number generator described below.
Estimating pi by random dart throwing
- Suppose we have a dart board of radius 1 foot, exactly centred on a background square
that is 2 feet by 2 feet.
- If we randomly throw darts at the square (with equal likelihood of the dart landing
anywhere within the square), then the probability of hitting the dartboard is
the area of the dart board divided by the area of the square, i.e.
pi r2 / 2 * 2 where r is 1, and thus pi / 4
- If we throw N darts and K of them hit the board, then we can estimate pi as 4*K/N.
- The more darts we throw, the more accurate our estimate will be.
Requirement: all repetition must be done using
loops. No recursion at all is permitted in lab4.cpp. |
Your desired program behaviour
Your program will do the following:
- Ask the user how big a sample size they wish to use (i.e. how many darts to "throw").
This part should use a loop (not recursion) to repeatedly prompt/read/check
until the user provides a valid value (an integer value greater than zero),
providing suitable error messages each time the user enters a non-integer or integer less than 1.
- Generate that many random (x,y) points where x is a random float in the range -1 to +1
and y is a random float in the range -1 to +1.
// one approach to getting an appropriate rval in the range -1 to +1
rval = 2.0f * (float)(rand()) / (float)(RAND_MAX); // gives a value in 0..2
rval -= 1.0f; // shifts the value into the range -1 to +1
|
- Count how many of those points would have hit the dart board, i.e. are a distance at
most 1 from the centre.
sqrt( x*x + y*y) gives the distance from the origin, if it's <= 1 then it hit the board
|
- Estimate pi as 4.0 * darts that hit the board / total darts.
Sample run
(The sample run below shows user input in bold italics just for the sake of clarity.)
Please enter the desired sample size (number of points) as an integer (e.g. 217): foo
Sorry, that was not an integer: please try again
Please enter the desired sample size (number of points) as an integer (e.g. 217): -1
Sorry, the value needs to be greater than zero: please try again
Please enter the desired sample size (number of points) as an integer (e.g. 217): ick
Sorry, that was not an integer: please try again
Please enter the desired sample size (number of points) as an integer (e.g. 217): blah
Sorry, that was not an integer: please try again
Please enter the desired sample size (number of points) as an integer (e.g. 217): -3
Sorry, the value needs to be greater than zero: please try again
Please enter the desired sample size (number of points) as an integer (e.g. 217): -4
Sorry, the value needs to be greater than zero: please try again
Please enter the desired sample size (number of points) as an integer (e.g. 217): 90567
Of the 90567 points tested, 70922 landed in the unit circle,
giving a pi estimate of 3.13235
|
Experiment with different sample sizes and see how the estimate gets better as the sample size
increases.
(Don't be surprised if you have to get into sizes in the millions before consistently
seeing it accurate to a few decimal places.)
As always, check you've followed code standards and do a make submit