Educational Objectives: After completing this assignment, the student should be able to accomplish the following:
Operational Objectives: Implement the class alt::BitVector and provide a client program sieve.cpp for alt::BitVector that implements the Sieve of Eratosthenes algorithm to compute all prime numbers less than or equal to an input integer.
Deliverables: Two files: sieve.cpp and bitvect.cpp.
Algorithm Assumptions
b is a bitvector indexed in the range [0 ... n). Bit k of b is denoted by b[k].
Algorithm Outcomes
After invoking the sieve algorithm body, an integer k in the range [0 ... n) is prime iff b[k] is set.
Algorithm Body
- Begin with a bitvector b indexed in the range 0 <= k < n.
- Initialize b by setting all bits.
- Clear b[0] and b[1] (because 0 and 1 are not prime).
- For k between 2 and the square root of n, stepsize 1:
if b[k] is set
for j between k + k and n, stepsize k:
clear b[j].
- Stop.
In short, clear the bits of all multiples of primes less than the square root of n.
Algorithm Proof
Given a bitvector b, indexed from 0 to n, the sieve algorithm begins with all bits set. The outcome to be proved is that the algorithm ends with b[k] set iff k is prime. The proof makes use of two useful observations about prime numbers.
- If k is not prime, then k is divisible by a prime. In other words, the non-prime numbers are precisely the numbers that are multiples of smaller prime numbers.
- If a number is not prime, then it is divisible by a prime that is no greater than the square root of the number.
Proof of observation 2: Let k = pq, and suppose p <= q. Then
p2 <= pq = k
and hence the square root of p is no greater than the square root of k, using the monotonicity of the square root function.
Proof of Sieve: Suppose that k is an integer in the range 0 <= k < n and that the sieve algorithm body has been invoked. We must show that k is prime iff b[k] is set. The cases k = 0 and k = 1 are handled by the initialization process, so we may assume that k is in the range [2 ... n).
Suppose b[k] is set. If k is not prime then k is a multiple of a prime less than the square root of k (by observation 2) and therefore b[k] would have been cleared by the algorithm, contradicting the assumption that b[k] is set.
Conversely, suppose b[k] is cleared. Then that bit was cleared because k was a multiple of some smaller prime, so k is not prime.
Final remark - what Eratosthenes was thinking: Clearly, the big E did not use bitvectors. His approach went something like this: Imagine the numbers 1..n all written down in a list. We will cross all the composit numbers off of the list, so that those that are left must be all of the non-composit, that is, prime, numbers. The E-man went on to describe how to cross numbers off: first cross off 1, keep 2, and then cross off all multiples of 2. Go to the next number not crossed off (which must be prime) and cross of all of its multiples. Keep going until the list is exhausted.
Bit vectors, the BitVector API (application programming interface), and implementation techniques are discussed at length in the lecture notes. We repeat here a statement of the API:
std::ostream& operator << (std::ostream&, const BitVector&); // send representation of bits as 0,1 integers class BitVector { public: explicit BitVector (unsigned int); // construct a BitVector with specified size BitVector (const BitVector&); // copy constructor ~BitVector (); // destructor BitVector& operator = (const BitVector& a); // assignment operator unsigned int Size () const; // return size of bitvector void Set (); // make all bits = 1 void Set (unsigned int index); // make bit at index = 1 void Unset (); // make all bits = 0 void Unset (unsigned int index); // make bit at index = 0 void Flip (); // change all bits void Flip (unsigned int index); // change bit at index int Test (unsigned int index) const; // return bit value as int } ; // class BitBector
Both fsu::BitVector and alt::BitVector will conform to this API.
All of the work for this assignment should be done in your subdirectory cop4530/hw1.
Begin by copying the following files from HOME into your assignment directory:
tests/fbitvect.cpp hw1/bitvect.h hw1/makefile hw1/hw1submit.sh
Then change permissions on the script to x-- (executable by you only) with the command chmod 700 hw1submit.sh.
Create the files sieve.cpp and bitvect.cpp, according to the specifications below.
Turn in the two files sieve.cpp and bitvect.cpp using the script hw1submit.sh.
Warning: Submit scripts do not work on the program and linprog servers. Use shell.cs.fsu.edu to submit this assignment. If you do not receive the second confirmation with the contents of your project, there has been a malfunction.
The file sieve.cpp should contain these elements:
typedef fsu::BitVector BitVector; // typedef alt::BitVector BitVector; ... void Sieve (BitVector& bv); ... int main() { ... }
Function Sieve() should take a BitVector object bv by reference and perform the sieve algorithm on it, so that when the function returns, bits of bv are set if and only if the index of the bit is a prime number. Note that Sieve() is a function only. It does not do any interaction with users, streams, or files; it only alters the state of the BitVector passed in by reference.
Function main() should handle the I/O: query the user for a positive integer and output a list consisting of all prime numbers that are less than or equal to the input. (See example executable.)
The entire sieve program should be a BitVector client, that is, use the BitVector public interface (API). This means that the application should work equally well as a client of fsu::BitVector and a client of alt::BitVector.
Thoroughly test your sieve program using the fsu::BitVector type definition.
The file bitvect.h should not be modified in any way, because your solution must work with the distributed version.
The file bitvect.cpp should implement all of the alt::BitVector methods, including friends and members, functions and operators, as defined in the file hw1/bitvect.h. Note that alt::BitVector uses a vector of size_t as its private storage mechanism, that is, an fsu::TVector<size_t> object.
The body of the constructor should be as follows:
{ if (sizeof(size_t) != 8) { std::cerr << "** size mismatch: sizeof(size_t) = " << sizeof(size_t) << '\n'; exit (EXIT_FAILURE); } }
This code checks that the type size_t is an 8 byte (64 bit) word. The
initializations should be handled in an initialization list.
Note: The Linprog machines are 64-bit architecture. Most Intel
machines are 32-bit, as are the program machines, so it is likely you will need
to work on linprog.
You should use the public interface of fsu::TVector for all implementation code. Do not create any arrays directly - memory management is taken care of by fsu::TVector.
Test alt::BitVector thoroughly, using both your sieve.cpp discussed above as well as fbitvect.cpp.
Your project should build without warnings using the distributed makefile.
Please note that you need the directories . (current directory), and /home/courses/cop4530p/spring09/cpp listed as Include directories in your makefile compile statements so that g++ can find sieve.cpp (in .) and bitvect.h and bitvect.cpp (in cpp). Using the relative path to designate the project directory and the absolute path to designate the library directory so that when the files are moved into your portfolio after submission, the makefile will continue to work correctly.
You want to develope and test sieve.cpp using the distributed bit vector class fsu::BitVector, found in HOME/cpp/bitvect.*. Here is a way to accomplish this and avoid name conflicts between files in your project directory and those in the course library:
The following executables are distributed in HOME/area51:
sieve_i.x // sample executable of sieve client fbitvect8_i.x // fbitvect.cpp compiled as client of fsu::BitVector fbitvect64_i.x // fbitvect.cpp compiled as client of alt::BitVector
The fbitvect executables are compiled as clients of both fsu::BitVector (8-bit implementation) and alt::BitVector (64-bit implementation).