Complex Numbers

Complex Numbers

std::complex<float> fc;
std::complex<double> dc;
std::complex<long double> ldc;

C++ supports mathematical complex numbers via the std::complex class. Recall from mathematics that a complex number has a real component and an imaginary component.

// File complex.cpp
#include <iostream>

#include <complex>

int main() {
    // c1 = 2 + 3i, c2 = 2 - 3i; c1 and c2 are complex conjugates
    std::complex < double > c1(2.0, 3.0), c2(2.0, -3.0);
    // Compute product "by hand"
    double real1 = c1.real(),
        imag1 = c1.imag(),
        real2 = c2.real(),
        imag2 = c2.imag();
    std::cout << c1 << " * " << c2 << " = " <<
        real1 * real2 + imag1 * real2 + real1 * imag2 - imag1 * imag2 <<
        '\n';
    // Use complex arithmetic
    std::cout << c1 << " * " << c2 << " = " << c1 * c2 << '\n';
}
(2,3) * (2,-3) = 13
(2,3) * (2,-3) = (13,0)

Observe that the program displays the complex number 2 − 3i as the ordered pair (2,-3). The first element of the pair is the real part, and the second element is the imaginary coefficient.

Better Pseudorandom Number Generation

Suppose we wish to generate pseudorandom numbers in the range 0…9,999. This range spans 10,000 numbers. Under Visual C++ RAND_MAX is 32,767, which is large enough to handle a maximum value of 9,999. The expression rand() % 10000 will evaluate to number in our desired range. In a program that generates one billion pseudorandom values in the range 0…9,999, we would expect any given number to appear approximately 1,000,000,000 10,000 = 100,000 times.

#include <iostream>

#include <iomanip>

#include <cstdlib>

#include <ctime>

int main() {
    // Initialize a random seed value
    srand(static_cast < unsigned > (time(nullptr)));
    // Verify the largest number that rand can produce
    std::cout << "RAND_MAX = " << RAND_MAX << '\n';
    
    double total5 = 0.0, total9995 = 0.0;
    // Accumulate the results of 10 trials, with each trial
    // generating 1,000,000,000 pseudorandom numbers
    const int NUMBER_OF_TRIALS = 10;
    for (int trial = 1; trial <= NUMBER_OF_TRIALS; trial++) {
        // Initialize counts for this run of a billion trials
        int count5 = 0, count9995 = 0;
        // Generate one billion pseudorandom numbers in the range
        // 0...9,999 and count the number of times 5 and 9,995 appear
        for (int i = 0; i < 1000000000; i++) {
            // Generate a pseudorandom number in the range 0...9,999
            int r = rand() % 10000;
            if (r == 5)
                count5++; // Number 5 generated, so count it
            else if (r == 9995)
                count9995++; // Number 9,995 generated, so count it
        }
        // Display the number of times the program generated 5 and 9,995
        std::cout << "Trial #" << std::setw(2) << trial << " 5: " << count5 <<
            " 9995: " << count9995 << '\n';
        total5 += count5; // Accumulate the counts to
        total9995 += count9995; // average them at the end
    }
    std::cout << "-------------------\n";
    std::cout << "Averages for " << NUMBER_OF_TRIALS << " trials: 5: " <<
        total5 / NUMBER_OF_TRIALS << " 9995: " <<
        total9995 / NUMBER_OF_TRIALS << '\n';
}
RAND_MAX = 32767
Trial # 1 5: 122295 9995: 91255
Trial # 2 5: 121789 9995: 91862
Trial # 3 5: 122440 9995: 91228
Trial # 4 5: 121602 9995: 91877
Trial # 5 5: 122599 9995: 91378
Trial # 6 5: 121599 9995: 91830
Trial # 7 5: 122366 9995: 91598
Trial # 8 5: 121839 9995: 91387
Trial # 9 5: 122295 9995: 91608
Trial #10 5: 121898 9995: 91519
-------------------
Averages for 10 trials: 5: 122072 9995: 91554.2
Elements in each row are equivalent modulus 10,000 AndroWep-Tutorials
Elements in each row are equivalent modulus 10,000 AndroWep-Tutorials

billion number generations. Note how consistent the results are among the runs

#include <iostream>
#include <iomanip>
#include <cstdlib>
#include <ctime>

int main() {
    // Set random number seed value
    srand(42);
    // Need to use numbers larger than regular integers; use long long ints
    for (long long i = 1; i < 4294967400 LL; i++) {
        int r = rand();
        if (1 <= i && i <= 10)
            std::cout << std::setw(10) << i << ":" << std::setw(6) << r << '\n';
        else if (2147483645 <= i && i <= 2147483658)
            std::cout << std::setw(10) << i << ":" << std::setw(6) << r << '\n';
        else if (4294967293 LL <= i && i <= 4294967309 LL)
            std::cout << std::setw(10) << i << ":" << std::setw(6) << r << '\n';
    }
}

During this particular program run we see that in 1,000,000,000 attempts the program generates the value 5 on average 99,889.9 times and generates 9,995 on average 100,033 times. Both of these counts approximately equal the expected 100,000 target. Examining the 10 trials individually, we see that neither the count for 5 nor the count for 9,995 is predisposed to be greater than or less than the other

mt19937 gen(20); // Use fixed seed instead of random_device
uniform_int_distribution<int> dist(0, 9999);
std::cout << dist(gen) << '\n';
std::cout << dist(gen) << '\n';
std::cout << dist(gen) << '\n';

The use of random_device, mt19937, and uniform_int_distribution is a little more complicated than using srand and rand with the modulus operator, but the extra effort is worth it for many applications. This object-oriented approach is more modular because it allows us to substitute an object of a different pseudorandom number generator class in place of mt19937 if we so choose. We also may swap out the normal distribution for a different distribution.

Exercises

Suppose s is a std::string object.
(a) What expression represents the number of characters that make up s?
(b) What expression represents the first character in s?
(c) What statement would insert the character 'x' onto the front of s?
(d) What statement would append the character 'x' onto the back of s?