You are here:

C++/problem of random character

Advertisement


Question
I am a beginner and I want to generate a name of five random characters from 'a' to 'z'. How can i do it?

Answer
In many ways. You say you are a beginner but not exactly what you have learnt already.

Please note that code shown in this answer consists of incomplete fragments used for example and demonstration purposes only. They have not been tested so may contain typos and mistakes, for which I apologise. Specifically if you see:

   // ...

It means that there is assumed to be code not shown, however this is often implied by the context only. For example:

   #include <some_header>

   // ...

   int some_variable = some_value;

   // ...

The above would imply that at least following code is present to create a compilable program:

   #include <some_header>

   int main()
   {
       int some_variable = some_value;
   }

I also do not tend to show all included headers, only ones not mentioned before that would be required.

OK the first problem is generating any random numbers.

The current standard way to generate random numbers in C++ is to use a function called rand, which it inherited from the C standard library. The rand function takes no parameters and returns an integer value between 0 and a value given by the constant RAND_MAX, which varies between C++ implementations. To use rand we have to include the <cstdlib> header (or the <stdlib.h> header if using C):

   #include <cstdlib>

   // ...

   int random_value = rand();  // random_value between 0 and RAND_MAX

   // ...


However you require a value that represents one of the letters. There are 26 letters in the range a - z so a value between 0 and 25 inclusive would seem appropriate. There are several ways to scale the raw value returned from rand to a range of our choosing. The simplest is to take the modulus (remainder) of one past the highest value we require, 26 in this case:

   random_value = random_value % 26;

Or more concisely:

   random_value %= 26;

Or even:

   int random_value = rand()%26;  // random_value between 0 and 25

Or better C++ style:

   int random_value( rand()%26 );  // random_value between 0 and 25

Now if you wrote a program whose main function did the above and then wrote out random_value to the console you would find that when run repeatedly the random value is the same each time. This is because the random number generator is really a pseudo random number generator and unless told otherwise it always generates the same sequence of random numbers, which can be good for testing purposes, but not for cases like this! The solution is to seed the pseudo random number generator with a value so that it starts elsewhere each time. To do so we call a function srand passing it a seed value (an unsigned int). We only need to call srand once in the program:

   srand(1234);

However this just pushes the problem back to srand - now we require a 'random' value to use to pass to srand to seed the pseudo random number generator!

The usual solution is to use some random quantity such as the value in an un-initialised memory location or other variable value. My usual solution is to use a time value as a seed value as time changes over, err, well, time. The simplest way to get such a value is to call the time function (include <ctime> or <time.h> for C).

The time function returns a value representing the number of seconds since some point in the past, called the epoch, such as midnight January 1 1970 Coordinated Universal Time (UTC) (a.k.a. GMT).

   #include <ctime>

   // ...

   srand( time(0) );

   // ...

However time returns its value as some implementation specific integer type aliased to the type name time_t, so you may find it necessary to cast the returned value from time to an unsigned int as required by srand to suppress compiler warnings:

   srand( static_cast<unsigned int>(time(0)) );

The 0 value passed to time is a null pointer value as time can take a time_t pointer as a parameter, which we are not interested in here.

OK so we have the following:

   #include <cstdlib>
   #include <ctime>

   // ...

   srand( time(0) );

   int random_value( rand()%26 );  // random_value between 0 and 25

   // ...

The above code allows us to seed the pseudo random number generator and then generate random numbers scaled to a useful range. The next problem is how to turn these values into characters. An obvious solution is to test the number for each of the values and output the equivalent character, 0 giving 'a', 1 giving 'b' etc.:

   #include <iostream>

   // ...

   if ( 0 == random_value )
   {
       std::cout << 'a';
   }
   else if ( 1 == random_value )
   {
       std::cout << 'b';
   }
   // ...

This is very long winded and tedious for what we are trying to achieve, and not that efficient. A better way would be to use a switch statement:

   switch ( random_value )
   {
   case 0: std::cout << 'a'; break;
   case 1: std::cout << 'b'; break;
   case 2: std::cout << 'c'; break;

   // ...

   default:
       std::cerr << "\nError! Character value out of range."
         << std::endl
         ;
       break;
   }

   // ...


However there are more direct ways of achieving what is required.

Now I should note that characters are represented as numbers, specifically integers. Which number represents which character, or in some case which sequence of numbers represent which character depends on the character set in question. In some character sets, including ones popular on PCs, the letters 'a' - 'z' are assigned contiguous ranges of numbers (there are in fact two ranges for the letters, one for lowercase letters and one for uppercase letters). This allows us to write expressions such as:

   'a' + random_value

Which gives 'a' + 0 ( i.e. 'a') to 'a' + 25 (i.e. 'z'). This can be used as below:

   std::cout << char('a' + random_value);

Note that I have had to force the result of the expression to a char type otherwise it would be an int, the type of random_value and the type returned by rand. Without this we would see the value in the character set that represents the character (i.e. instead of outputting "a", the output would be "97", the value representing 'a' in the PC character set (see http://www.asciitable.com/ for more on the basic PC character set values).

We could reduce the code even more like so:

   char random_letter( rand()%26 + 'a' );  

   std::cout << random_letter;

(again, you may require a cast to prevent the compiler from issuing warnings).

Now we have made an assumption that the letters are assigned contiguous values in the character set used on our machine. This is not so for all character sets, most famously the EBCDIC character set used by IBM mainframes and the like (see http://www.legacyj.com/cobol/ebcdic.html for example).

How can we write code that does not make assumptions about the relationship between values and the letters they represent in a character set? Well we can define out own array of characters containing the letters. Luckily for us a string literal in C and C++ is just such an array of characters with an added '\0' character added to the end to indicate the end of the string. So we can create an array of letters as a string literal thus:

   const char Letters[] = "abcdefghijklmnopqrstuvwxyz";

Then use the original random_value in the range 0 to 25 as an index into this array:

   std::cout << Letters[random_value];

To generate 5 random characters you just repeat the expression that calls rand and the output statement or other code that makes use of the random letter by wrapping the code in a loop, using the previous method to obtain a random letter you might write something like:

   for ( int i(0); i != 5; ++i )
   {
       int random_value( rand()%26 );
       std::cout << Letters[random_value];
   }

Or even just:

   for ( int i(0); i != 5; ++i )
   {
       std::cout << Letters[rand()%26];
   }

Or maybe you are building a string at this point rather than outputting the values directly:

   #include <string>

   // ...

   std::string name;
   for ( int i(0); i != 5; ++i )
   {
       name += Letters[rand()%26];
   }

Now you will find that the names generated are probably not name like. For example you would probably not consider the sequence "qdmkw" a name. Unfortunately fixing this is a lot more complex and would first require a good understanding of what sequences and combinations of letters you would consider would make a good name, and this would depend to some extent on your cultural background or other requirements (e.g. maybe you are creating fictional names for aliens or planets for some sci-fi game).

One approach could be to use several sequences of letters and select from different sequences depending on the previous letter or letters. Note however that you will have to modify the %26 calculation, changing the 26 to suit the number of letters in the sequence being used. Here is a simple example:

   #include <cstring> // for strlen
   // ...

   const char Vowels[] = "aeiou";
   const char Consonants[] = "bcdfghjklmnpqrstvwxyz";

   const int NumberOfVowels( strlen(Vowels) );
   const int NumberOfConsonants( strlen(Consonants) );

   std::string name;
   for ( int i(0); i != 5; ++i )
   {
       if ( 0==i%2 ) // If i is even...
       {
         name += Consonants[rand()%NumberOfConsonants];
       }
       else // i is odd...
       {
         name += Vowels [rand()%NumberOfVowels];
       }
   }

In the above fragment I have separated the letters into 2 groups: vowels and consonants. Each group has a constant literal string character array from which we can randomly select characters and a constant value of the number of characters in the group.

The loop selecting the random letters uses if..else statements to create a rule for the generation of a name. That rule is that a name should start with a consonant and each consonant is separated by a vowel. This translates to the first and every second character is a consonant and the rest are vowels. For the first character i, the loop variable, is 0, for the second character from this i is 2 and then 4. So consonants are in positions where i is even, and the other locations have vowels (i.e. when i is odd).

Hope this helps.  

C++

All Answers


Answers by Expert:


Ask Experts

Volunteer


Ralph McArdell

Expertise

I am a software developer with more than 15 years C++ experience and over 25 years experience developing a wide variety of applications for Windows NT/2000/XP, UNIX, Linux and other platforms. I can help with basic to advanced C++, C (although I do not write just-C much if at all these days so maybe ask in the C section about purely C matters), software development and many platform specific and system development problems.

Experience

My career started in the mid 1980s working as a batch process operator for the now defunct Inner London Education Authority, working on Prime mini computers. I then moved into the role of Programmer / Analyst, also on the Primes, then into technical support and finally into the micro computing section, using a variety of 16 and 8 bit machines. Following the demise of the ILEA I worked for a small company, now gone, called Hodos. I worked on a part task train simulator using C and the Intel DVI (Digital Video Interactive) - the hardware based predecessor to Indeo. Other projects included a CGI based train simulator (different goals to the first), and various other projects in C and Visual Basic (er, version 1 that is). When Hodos went into receivership I went freelance and finally managed to start working in C++. I initially had contracts working on train simulators (surprise) and multimedia - I worked on many of the Dorling Kindersley CD-ROM titles and wrote the screensaver games for the Wallace and Gromit Cracking Animator CD. My more recent contracts have been more traditionally IT based, working predominately in C++ on MS Windows NT, 2000. XP, Linux and UN*X. These projects have had wide ranging additional skill sets including system analysis and design, databases and SQL in various guises, C#, client server and remoting, cross porting applications between platforms and various client development processes. I have an interest in the development of the C++ core language and libraries and try to keep up with at least some of the papers on the ISO C++ Standard Committee site at http://www.open-std.org/jtc1/sc22/wg21/.

Education/Credentials

©2016 About.com. All rights reserved.