Displaying chart as below

Hi,
I have found the letter frequencies percentage of the string user inputs and have stored them in an array in which index 0 holds the frequency of 'a' and ... index 25 holds the frequency of 'z'.
Now I need to print the frequency like the chart below. Note that numbers to the right must go to the highest frequency percentage (in this case 10) and the asterisks basically represent the percentage.

http://i.imgur.com/UnnJBCH.png
1
2
3
4
5
6
7
8
9
for (int i=1; i<=10;i++)
    {
    for (int lett=0; lett<=25; lett++)
       if (freq[lett]>=i/10.0)
          cout << "* ";
       else
          cout << "  ";
    cout << endl;
    }


... although from your query I can't figure out what exacty is the highest frequency percentage. Perhaps you should find max percentage before doing this printing (and then replace 10.0 with max).
Last edited on
Thank you Kevin for your reply, but it didn't work as intended. Please look at my code below:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147

#include <iostream>
#include <string>
using namespace std;

void setCharCountArrayToZero( );
int getStringLength( );
void findLargestValue( );
void findLargestValueIndex( );
void freqCalculator( );
void printFrequency( );

int length = 0;
int maxValueIndex = 0;
int maxValue = 0;
unsigned int index = 0;
int charCountArray[26];
double charFreqArray[26];
string str;

int main()
{
	cout << "Enter text at the prompt to calculate letter frequencies. Enter DONE when finished with text input." << endl;
	cout << "Enter a line of text: ";
	getline( cin, str );
	setCharCountArrayToZero( );
	while( str != "DONE" )						//continues to get string until "DONE" is entered
	{
		for( unsigned int i = 0; i < str.length(); i++ )		//checks the entered string and deposits the number of occurance of a letter to the corresponding index in charCountArray (e.g. 'a' and 'A' fo to index 0)
		{
			if( str[i] >= 'a' && str[i] <= 'z' )
			{
				while( index < str.length() )
				{
					// The character at the index.
					int ch = str[index];
					// Increment the charCountArray for the letter.
					charCountArray[ch-'a']++;
					break;
				}
				index++;
			}
			else if( str[i] >= 'A' && str[i] <= 'Z' )
				{
					while( index < str.length() )
					{
						// The character at the index.
						int ch = str[index] + 32;
						// Increment the charCountArray for the letter.
						charCountArray[ch-'a']++;
						break;
					}
					index++;
				}
			else
				index++;
		}
		
		cout << "Enter a line of text: ";
		getline( cin, str );
		index = 0;
	}

	getStringLength( );
	freqCalculator( );
	findLargestValue( );
	findLargestValueIndex( );
	for( int p = 0; p < 26; p++ )
	{
		cout << endl << charFreqArray[p] << endl;
	}
	printFrequency( );
	return 0;
}

void setCharCountArrayToZero( )						//initializes all elements of the countArray to 0
{
	for( int i = 0; i < 26; i++ )
		charCountArray[i] = 0;
}

int getStringLength( )							//calculates and returns the total number of alphabets entered
{
	for( int i = 0; i < 26; i++ )
		length += charCountArray[i];
	return length;
}

void findLargestValue( )						//finds the largest frequency percentage
{
     maxValue = charFreqArray[0];		    // start with max = first element
     for( int i = 0; i < 26; i++ )
     {
          if( charFreqArray[i] > maxValue )
			  maxValue = charFreqArray[i];
     }
	 cout << "max value " << maxValue << endl;
}

void findLargestValueIndex( )					//finds the index # for the largest frequency percentage
{
	for( int i = 0; i < 26; i++ )
	{
		if( charFreqArray[i] == maxValue )
		{
			maxValueIndex = i;
			i = 26;						//to get out of the loop
		}	
	}
	cout << "max value index " << maxValueIndex;
}

void freqCalculator()
{
	for( int i = 0; i < 26; i++ )						//calculates the frequency percentage
		charFreqArray[i] = charCountArray[i]/(double) length * 100;

	for( int i = 0; i < 26; i++ )						//rounds the frquency
	{
		charFreqArray[i] += 0.5;
		charFreqArray[i] = (int) charFreqArray[i];
	}
}

void printFrequency( )
{
	cout << endl << "A total of " << length << " letters of the alphabet were processed." << endl << endl;

	for( char i = 'a'; i <= 'z'; i++ )
	{
		cout << i << " ";
	}
	cout << '%' << endl << endl;

	for ( int i = 1; i <= maxValue; i++ )
    {
		for( char lett = 0; lett < 26; lett++ )
		{
			if( charFreqArray[lett] >= i / (double) maxValue )
				cout << "* ";
			else
				cout << "  ";
		}
		cout << endl;
    }
	cout << endl << "The most common letter is " << "'" << (char) ( maxValueIndex + 97 ) << "'" << " with a frequency of " << charFreqArray[maxValueIndex] << "%." << endl;
}
Last edited on
Integer division discards remainder. i/max is 0 for all i<max.
[EDIT]
It doesn't work correctly when I enter 3 lines of string and then enter "DONE"


Enter text at the prompt to calculate letter frequencies. Enter DONE when finish
ed with text input.
Enter a line of text: Frequency analysis is cool.
Enter a line of text: If you use it on long text you can discover some interesti
ng things.
Enter a line of text: Try it out!
Enter a line of text: DONE

A total of 86 letters of the alphabet were processed.

a b c d e f g h i j k l m n o p q r s t u v w x y z %

*   * * * * * * *     * * * *   * * * * * *   * *
*   * * * * * * *     * * * *   * * * * * *   * *
*   * * * * * * *     * * * *   * * * * * *   * *
*   * * * * * * *     * * * *   * * * * * *   * *
*   * * * * * * *     * * * *   * * * * * *   * *
*   * * * * * * *     * * * *   * * * * * *   * *
*   * * * * * * *     * * * *   * * * * * *   * *
*   * * * * * * *     * * * *   * * * * * *   * *
*   * * * * * * *     * * * *   * * * * * *   * *
*   * * * * * * *     * * * *   * * * * * *   * *


-Percentages aren't shown below %.
-Also, maxValueIndex holds a wrong number in this case.
Last edited on
-Percentages aren't shown below %.

Your program does not even attempt to print them.

-Also, maxValueIndex holds a wrong number in this case.

Hard to tell. Print the frequencies just to see that the numeric values make sense.


Notes:

* You do use global variables. That is a bad habit that you should not learn. Pass data via function arguments. There are situations, when global variables are necessary, but that is an advanced topic.

* You do compare equality of floating point values. That is an another tricky area. There are many texts about its weirdness.
@keskiverto

Thank you very much. I was looking for a way to put the percentages below % somehow using the code Kevin wrote, but couldn't do it.
Regarding the frequencies, I printed all of them as you said and they are all correct. It is the index that catches a wrong value. (In this example with 3 lines of input, the first most frequent letter is 'l' at 10%, but it shows 'a' at 3% as the most frequent)

[UPDATE 1] Apparently maxIndexValue remains 0 for all inputs. As you said this is caused by comparing two floating point values. I changed maxValue to int and now it works fine. BUT the number of printed asterisks is still not correct for each letter if the 1st input is "Frequency analysis is cool." and the 2nd input is "If you use it on long text you can discover some interesting things." and the 3rd one is "Try it out" and then "DONE". The chart must be like this: http://i.imgur.com/CpMnXHo.png
BUT IT ISN'T! WEIRD!
Last edited on
Try this:

1
2
3
4
5
6
7
8
9
10
11
for ( int i = 1; i <= 10; i++ )
    {
		for( int lett = 0; lett < 26; lett++ )
		{
			if( charFreqArray[lett] >= (double) maxValue*i/10 )
				cout << "* ";
			else
				cout << "  ";
		}
		cout << endl;
    }
Last edited on
@Kevin C: Why the division? What do you try to achieve with it?
(My psychic powers already do know, but you should explain to backslashV.)

@backslashV: study the code below. Rather than blindly copy-pasting, try to figure out how it differs, and which of the differences are merely cosmetic and which affect the logic.
1
2
3
4
5
6
7
8
9
10
11
for ( int row = 1; row <= maxValue; ++row )
{
  for ( char lett = 0; lett < 26; ++lett )
  {
    if ( row <= charFreqArray[lett] )
      cout << "* ";
    else
      cout << "  ";
  }
  cout << row << '\n';
}

I'm trying to calculate a fraction of maxValue. It would probably be more clear if I wrote it like this:

if( charFreqArray[lett] >= maxValue * (i / 10.0) )

In other words you print exactly 10 rows of histogram whether the max value is, for example 5% or 100%. The difference is on how much one row covers (0.5% or 10% fractions, respectively) in each case.
1
2
3
4
5
6
7
8
9
10
11
12
13
const int Fractions { 10 };
for ( int row = 1; row <= Fractions; ++row )
{
  const double Treshold { static_cast<double>( row * maxValue ) / Fractions };
  for ( char lett = 0; lett < 26; ++lett )
  {
    if ( Treshold <= charFreqArray[lett] )
      cout << "* ";
    else
      cout << "  ";
  }
  cout << Treshold << '\n';
}


It does, however, seem that the task demands 1% fractions and the height of the graph depends on max value.


Based on what is actually stored in 'charFreqArray' (now), the type of that array should be integral, not double.
Last edited on
Well, it's hard to guess just by looking at the graph. But I counted 96 stars. So probably, the scale on the right is in 1% and does not depend on max value.

In that case:

if( charFreqArray[lett] >= (double) i )
Based on what is actually stored in 'charFreqArray' (now), the type of that array should be integral, not double.


I think those are actually rounded frequencies, in percentages. I would keep it as double in case the author changes his mind about rounding. Also, making it type int would necessitate too many changes in the program.
@keskiverto
@Kevin C

Thank you both very much for your time and help.
The code keskiverto wrote makes sense to me and works well. Regarding Kevin's code, I don't get the division by 10.
Your code that counts letters is very obscure. For example, this loop
executes only once because of the break statement:
1
2
3
4
5
6
7
8
while( index < str.length() )
{
    // The character at the index.
    int ch = str[index];
    // Increment the charCountArray for the letter.
    charCountArray[ch-'a']++;
    break;
}


Simplify the code to:
1
2
3
4
5
6
7
8
9
10
11
12
for( unsigned int i = 0; i < str.length(); i++ )
{
    int ch = str[i];
    if( ch >= 'a' && ch <= 'z' )
    {
	// Increment the charCountArray for the letter.
	charCountArray[ch-'a']++;
    } else if( ch >= 'A' && ch <= 'Z' )
    {
	charCountArray[ch-'A']++;
    }
}


Now lets start with the printing code:
for ( int i = 1; i <= maxValue; i++ ) // If maxValue=87, you'll print 87 lines?
You're always printing 10 lines so that should be:
for ( int i = 0; i <10; i++ )
Now the code to print the frequency needs to change slightly:
1
2
3
4
if( charFreqArray[lett] >= maxValue * i / 10 )
    cout << "* ";
else
    cout << "  ";


getStringLength() sets a global variable. You use this global instead
of the return value. That's bad form, but the worse thing about this
code is that it doesn't initialize length. If you call
getStringLength() twice, you'll get different answers. Use a local
variable to get the string length As a habbit, I always call the
return value of a function "result":

1
2
3
4
5
6
7
int getStringLength( )							//calculates and returns the total number of alphabets entered
{
    int result = 0;
    for( int i = 0; i < 26; i++ )
	result += charCountArray[i];
    return result;
}


Then inside main, make length local and assign it to the return value
of getStringLength
int length = getStringLength( );

freqCalculator should take the total number of letters as a
parameter. You're currently modifying the frequency values to make
display easier. Don't do that. Data should represent the actual data:
1
2
3
4
5
void freqCalculator(int length)
{
    for( int i = 0; i < 26; i++ )						//calculates the frequency percentage
	charFreqArray[i] = charCountArray[i]/(double) length * 100;
}

You're finding the largest value and then using that to find the index
of the largest value. It's must better to just find the index and then
the largest value is charFreqArray[index]. Again, use local variables
as much as possible and return the actual index:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
int findLargestValueIndex( )		//finds the index # for the most frequent letter
{
    int maxValue = charCountArray[0];
    int result = 0;
    for( int i = 0; i < 26; i++ )
    {
	if( charCountArray[i] >= maxValue )
	{
	    maxValue = charCountArray[i];
	    result = i;
	}	
    }
    return result;
}





1
2
3
4
5
6
7
for each character on a line
    if the character is a letter
        for EVERY character on the line
             increment the charCountArray[character-'a']
        }
    }
}


Here's the code with all of these changes and things like prototypes fixed to match
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
#include <iostream>
#include <iostream>
#include <string>
using namespace std;

void setCharCountArrayToZero( );
int getStringLength( );
int findLargestValueIndex( );
void freqCalculator(int length );
void printFrequency(double maxValue );

int charCountArray[26];
double charFreqArray[26];

int main()
{
    string str;
    cout << "Enter text at the prompt to calculate letter frequencies. Enter DONE when finished with text input." << endl;
    cout << "Enter a line of text: ";
    getline( cin, str );
    setCharCountArrayToZero( );
    while( str != "DONE" )						//continues to get string until "DONE" is entered
	{
	    for( unsigned int i = 0; i < str.length(); i++ )		//checks the entered string and deposits the number of occurance of a letter to the corresponding index in charCountArray (e.g. 'a' and 'A' fo to index 0)
		{
		    int ch = str[i];
		    if( ch >= 'a' && ch <= 'z' )
		    {
			// Increment the charCountArray for the letter.
			charCountArray[ch-'a']++;
		    } else if( ch >= 'A' && ch <= 'Z' )
		    {
			charCountArray[ch-'A']++;
		    }
		}
		
	    cout << "Enter a line of text: ";
	    getline( cin, str );
	}
    int length = getStringLength( );
    freqCalculator(length );
    int maxValueIndex = findLargestValueIndex( );
    for( int p = 0; p < 26; p++ )
	{
	    cout << charFreqArray[p] << endl;
	}
    cout << endl << "A total of " << length << " letters of the alphabet were processed." << endl << endl;
    printFrequency(charFreqArray[maxValueIndex]);
    cout << endl << "The most common letter is "
	 << "'" << (char) ( maxValueIndex + 'a' ) << "'"
	 << " with a frequency of " << charFreqArray[maxValueIndex] << "%." << endl;
    return 0;
}

void setCharCountArrayToZero( )						//initializes all elements of the countArray to 0
{
    for( int i = 0; i < 26; i++ )
	charCountArray[i] = 0;
}

int getStringLength( )							//calculates and returns the total number of alphabets entered
{
    int result = 0;
    for( int i = 0; i < 26; i++ )
	result += charCountArray[i];
    return result;
}

int findLargestValueIndex( )		//finds the index # for the most frequent letter
{
    int maxValue = charCountArray[0];
    int result = 0;
    for( int i = 0; i < 26; i++ )
    {
	if( charCountArray[i] >= maxValue )
	{
	    maxValue = charCountArray[i];
	    result = i;
	}	
    }
    return result;
}

void freqCalculator(int length)
{
    for( int i = 0; i < 26; i++ )						//calculates the frequency percentage
	charFreqArray[i] = charCountArray[i]/(double) length * 100;
}

void printFrequency(double maxValue)
{

    for( char i = 'a'; i <= 'z'; i++ )
	{
	    cout << i << " ";
	}
    cout << '%' << endl << endl;

    for ( int i = 0; i < 10; i++ )
	{
	    for( char lett = 0; lett < 26; lett++ )
		{
		    if( charFreqArray[lett] > maxValue * i / 10 )
			cout << "* ";
		    else
			cout << "  ";
		}
	    cout << i+1;
	    cout << endl;
	}
}
@dhayden
I can't thank you enough. You are being very helpful.
1. Just so you know I need the number of lines to be equal to maxValue, so if maxValue=87 I want to print 87 lines.
2. Also we are required to round the results, that's why I had to alter the actual values.
3. The reason I wrote a different function to find maxValueIndex is that I want to find the FIRST letter that is the most frequent. Now I just merged that into your version of findLargestValueIndex( ) like this:
4. Why is it not good to have global variables?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
int findLargestValueIndex( )					//finds and returns the index # for the largest frequency percentage
{
	int maxValue = charFreqArray[0];		    // start with max = first element
	int result = 0;
	
	for( int i = 0; i < 26; i++ )
	{
		if( charFreqArray[i] >= maxValue )
		{
			maxValue = charFreqArray[i];	
		}	
	}

	for( int i = 0; i < 26; i++ )
	{
		if( charFreqArray[i] == maxValue )
		{
			result = i;
			i = 26;								//to get out of the loop
		}	
	}
	return result;
}
Last edited on
2. Also we are required to round the results, that's why I had to alter the actual values.

You can round the values when you print them. Do not change the actual values.

An example. Lets say that the input contains N 'a' and N+1 'b' and both N and N+1 round to same percentage. If you do round them, then the findLargest will point to 'a', which is a wrong answer.

3. The reason I wrote a different function to find maxValueIndex is that I want to find the FIRST letter that is the most frequent. Now I just merged that into your version of findLargestValueIndex( ) like this:

Good intent, but there is an easier way:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
int findLargestValueIndex( )
{
    int maxValue = charCountArray[0];
    int result = 0;
    for( int i = 0; i < 26; i++ )
    {
	if( charCountArray[i] > maxValue ) // the magic is here
	{
	    maxValue = charCountArray[i];
	    result = i;
	}	
    }
    return result;
}



4.
http://bytes.com/topic/c/insights/737451-case-against-global-variables
http://www.learncpp.com/cpp-tutorial/42-global-variables/
http://stackoverflow.com/questions/484635/are-global-variables-bad
Thanks a lot. You guys were very helpful unlike people at stackoverflow.
Topic archived. No new replies allowed.