C++ Cookbook
Authors: Cogswell J. Diggins C. Stephens R.
Published year: 2006
Pages: 105-106/241
Buy this book on amazon.com >>

Recipe 7.3. Randomly Shuffling Data

Problem

You have a sequence of data, and you need to jumble it into some random order.

Solution

Use the random_shuffle standard algorithm, defined in <algorithm> . random_shuffle takes two random-access iterator s, and ( optionally ) a random-number generation functor, and rearranges the elements in the range at random. Example 7-3 shows how to do this.

Example 7-3. Shuffling a sequence at random
#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>
#include "utils.h" // For printContainer( ): see 7.10

using namespace std;

int main( ) {

   vector<int> v;
   back_insert_iterator<std::vector<int> > p =
      back_inserter(v);

   for (int i = 0; i < 10; ++i)
     *p = i;

   printContainer(v, true);

   random_shuffle(v.begin( ), v.end( ));

   printContainer(v, true);
}

Your output might look like this:

-----
0 1 2 3 4 5 6 7 8 9
-----
8 1 9 2 0 5 7 3 4 6

Discussion

random_shuffle is intuitive to use. Give it a range, and it will shuffle the range at random. There are two versions, and their prototypes look like this:

void random_shuffle(RndIter first, RndIter last);
void random_shuffle(RndIter first, RndIter last, RandFunc& rand);

In the first version, the "random" is using an implementation-specific random-number generation function, which should be sufficient for most of your needs. If it isn'tperhaps you want a nonuniform distribution, e.g., Gaussianyou can write your own and supply that instead using the second version.

Your random-number generator must be a functor that a single argument and returns a single value, both of which are convertible to iterator_traits<RndIter>::difference_type . In most cases, an integer will do. For example, here's my knock-off random-number generator:

struct RanNumGenFtor {
   size_t operator( )(size_t n) const {
      return(rand( ) % n);
   }
} rnd;

random_shuffle(v.begin( ), v.end( ),

rnd

);

The applications to random_shuffle are limited to sequences that provide random-access iterator s ( string s, vector s, and deque s), arrays, or your custom containers that do the same. You can't randomly shuffle an associative container because its contents are stored in sorted order. In fact, you can't use any algorithm that modifies its range (often referred to as a mutating algorithm) on an associative container.



Recipe 7.4. Comparing Ranges

Problem

You have two ranges, and you need to compare them for equality or you need to see which one comes first based on some ordering on the elements.

Solution

Depending on what kind of comparison you want to do, use one of the standard algorithms equal , lexicographical_compare , or mismatch , defined in <algorithm> . Example 7-4 shows several of them in action.

Example 7-4. Different kinds of comparisons
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
#include "utils.h"

using namespace std;
using namespace utils;

int main( ) {

   vector<string> vec1, vec2;

   vec1.push_back("Charles");
   vec1.push_back("in");
   vec1.push_back("Charge");

   vec2.push_back("Charles");
   vec2.push_back("in");
   vec2.push_back("charge");  // Note the small "c"

   if (

equal

(vec1.begin( ), vec1.end( ), vec2.begin( ))) {
      cout << "The two ranges are equal!" << endl;
   } else {
      cout << "The two ranges are NOT equal!" << endl;
   }

   string s1 = "abcde";
   string s2 = "abcdf";
   string s3 = "abc";

   cout << boolalpha  // Show bools as "true" or "false"
        <<

lexicographical_compare

(s1.begin( ), s1.end( ),
                                   s1.begin( ), s1.end( )) << endl;
   cout <<

lexicographical_compare

(s1.begin( ), s1.end( ),
                                   s2.begin( ), s2.end( )) << endl;
   cout <<

lexicographical_compare

(s2.begin( ), s2.end( ),
                                   s1.begin( ), s1.end( )) << endl;
   cout <<

lexicographical_compare

(s1.begin( ), s1.end( ),
                                   s3.begin( ), s3.end( )) << endl;
   cout <<

lexicographical_compare

(s3.begin( ), s3.end( ),
                                   s1.begin( ), s1.end( )) << endl;

   pair<string::iterator, string::iterator> iters =

mismatch

(s1.begin( ), s1.end( ), s2.begin( ));

   cout << "first mismatch  = " << *(iters.first) << endl;
   cout << "second mismatch = " << *(iters.second) << endl;
}

The output of Example 7-4 looks like this:

The two sequences are NOT equal!
false
true
false
false
true
first mismatch  = e
second mismatch = f

Discussion

Use equal to compare two sequences for equality. It takes three or four arguments, depending on the version you use. Here's how equal is declared:

bool equal(In1 first1, In1 last1, In2 first2);
bool equal(In1 first1, In1 last1, In2 first2, BinPred pred);

equal compares each element between first1 and last1 with each element starting at first2 using operator== . If you supply pred , equal uses that to test equality instead. Ensure that the sequences each have the same length before calling equal ; it assumes the second range is at least as long as the first, and if it isn't, the behavior is undefined.

If you want to know more about how or where two sequences differ , you can use lexicographical_compare or mismatch . lexicographical_compare compares two sequences and returns true if the first is lexicographically less than the second, which means that each pair of elements in the two sequences is compared using the < operator. The declaration of lexicographical_compare looks like this:

bool lexicographical_compare(In1 first1, In1 last1,
                             In2 first2, In2 last2);
bool lexicographical_compare(In1 first1, In1 last1,
                             In2 first2, In2 last2,
                             Compare comp);

As soon as operator< returns true, or the first sequence ends before the second, true is returned. Otherwise, false is returned. Consider the character sequences in Example 7-4:

string s1 = "abcde";
string s2 = "abcdf";
string s3 = "abc";

lexicographical_compare(s1.begin( ), s1.end( ),  // abcde < abcde
                        s1.begin( ), s1.end( )); // = false
lexicographical_compare(s1.begin( ), s1.end( ),  // abcde < abcdf
                        s2.begin( ), s2.end( )); // = true
lexicographical_compare(s2.begin( ), s2.end( ),  // abcdf < abcde
                        s1.begin( ), s1.end( )); // = false
lexicographical_compare(s1.begin( ), s1.end( ),  // abcde < abc
                        s3.begin( ), s3.end( )); // = false
lexicographical_compare(s3.begin( ), s3.end( ),  // abc < abcde
                        s1.begin( ), s1.end( )); // = true

The complexity of lexicographical_compare is linear and will do a number of comparisons equal to the minimum of the two sequence lengths, or until the first time an element in one of the sequences is less than the corresponding element in the other. The comparisons are implemented entirely using operator< , so if iter1 and iter2 are iterator s into the two sequences, the comparison stops as soon as *iter1 < *iter2 or *iter2 < *iter1 .

mismatch will tell you where two sequences differ. Its declaration is a little different than equal and lexicographical_compare , though, because it returns a pair<> of iterator s instead of a bool . Here it is:

pair<In1, In2> mismatch(In1 first1, In1 last1, In2 first2);
pair<In1, In2> mismatch(In1 first1, In1 last1, In2 first2, BinPred);

The two iterator s returned point to the differing elements in each of the sequences. Consider Example 7-4:

string s1 = "abcde";
string s2 = "abcdf";
pair<string::iterator, string::iterator> iters =
   mismatch(s1.begin( ), s1.end( ), s2.begin( ));

cout << "first mismatch  = " << *(iters.first) << '\n'; // 'e'
cout << "second mismatch = " << *(iters.second) << '\n';// 'f'

You have to ensure that the second range is at least as long as the first. If the second sequence is shorter than the first, mismatch has no way to know it and will continue making comparisons to elements past the end of the second sequence, which has undefined behavior if it extends past the end of the second sequence. Additionally, if there is no mismatch, the first iterator will be pointing to last1 , which may not be valid (e.g., if you passed in end( ) as last1 ).

You may have noticed from the declarations of each of these algorithms that the types of the iterator s for each of the two sequences are different. This means that the two sequences can be containers of different types, so long as the type of the element those iterator s refer to have operator< defined for them. For example, you may want to compare a string to a vector<char> :

string s = "Coke";
vector<char> v;

v.push_back('c');
v.push_back('o');
v.push_back('k');
v.push_back('e');

std::cout << std::lexicographical_compare(s.begin( ), s.end( ),
                                          v.begin( ), v.end( )) << '\n';

This compares each of the characters in the two sequences without regard for the type of container that holds them.

The C++ standard library provides several different ways to compare sequences. If none of these suits your needs, look at the source code for them; it will provide a good example of how you can write your own efficient, generic algorithm.

See Also

Recipe 7.1


C++ Cookbook
Authors: Cogswell J. Diggins C. Stephens R.
Published year: 2006
Pages: 105-106/241
Buy this book on amazon.com >>

Similar books on Amazon