Tracking iterations of number in a list

Hello all,

This is the answer I got for the following question.
"Write a program that will read a file of numbers of type int and output the frequency of each number in the file. The file contains only whole numbers, positive and negative, separated by spaces, tabs, or line breaks."

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
#include <iostream>
#include <fstream>

std::ifstream fin;

void scanner(std::ifstream& fin, int& LargeNum, int& SmallNum);
void counter(std::ifstream& fin, int LargeNum, int SmallNum);

int main()
{
	//open numbers file, check for error
	fin.open("numbers.txt");
	if (fin.fail())
	{
		std::cout << "The input file has failed to open";
		exit(1);
	}

	//Obtain first number in list using fin
	//Set first number as base point for both high and low numbers
	int SmallNum;
	fin >> SmallNum;
	int LargeNum = SmallNum;

	//Run scanner using initial number as base comparison
	scanner(fin, LargeNum, SmallNum);
	//Run counter based on range of high and low number
	counter(fin, LargeNum, SmallNum);
}

void scanner(std::ifstream& fin, int& LargeNum, int& SmallNum)
{

	//Take a number, compare it to SmallNum and LargeNum
	//If less than SmallNum, store as SmallNum
	//If greater than LargeNum, store as LargeNum
	while (!fin.eof())
	{
		int number;
		fin >> number;

		if (number < SmallNum)
		{
			SmallNum = number;
		}
		if (number > LargeNum)
		{
			LargeNum = number;
		}
	}
	//close input file, output results
	fin.close();
	std::cout << "The largest number in the range is " << LargeNum << std::endl;
	std::cout << "The smallest number in the range is " << SmallNum << std::endl;
}

void counter(std::ifstream& fin, int LargeNum, int SmallNum)
{
	//run a loop for all possible integers between SmallNum and LargeNum
	for (int x = SmallNum; x <= LargeNum; x++)
	{
		
		//Open and reopen input file for each iteration of loop
		fin.open("numbers.txt");
		if (fin.fail())
		{
			std::cout << "The input file has failed to open";
			exit(1);
		}
		
		int counter = 0;
		
		//Check all numbers in file, if number in file matches number of iteration of loop, counter++
		while (!fin.eof())
		{
			
			int number;
			fin >> number;
			if (number == x)
			{
				counter++;
			}

		}
		if (counter > 0)
		{
			std::cout << "The number " << x << " appears " << counter << "  times" << std::endl;
		}
		fin.close();
	}
}


my question is can I move the following line of code into the function definition of scanner. The issue I believe I am having is the scope of SmallNum and LargeNum. Also, any comments or tips on the code would be greatly appreciated, I had some trouble with this one. Thanks!
1
2
3
4
5
6
7
8
9
10
11
12
13
//open numbers file, check for error
	fin.open("numbers.txt");
	if (fin.fail())
	{
		std::cout << "The input file has failed to open";
		exit(1);
	}

	//Obtain first number in list using fin
	//Set first number as base point for both high and low numbers
	int SmallNum;
	fin >> SmallNum;
	int LargeNum = SmallNum;
What are the constraints? I.e., What is the max number of ints that the file can contain and what is the range of those ints?

Can you just do this:

1
2
3
4
5
6
7
8
9
10
11
#include <iostream>
#include <fstream>
#include <map>

int main()
{
    std::ifstream& fin("numbers.txt");
    std::map<int,int> m;
    for (int n; fin >> n; ) ++m[n];
    for (auto& p: m) std::cout << p.first << ": " << p.second << '\n';
}

Last edited on
Are you allowed to use a map?
@dutch
@Thomas1965
Thanks for the responses. Unfortunately, I do not know what a map is, I haven't learned that yet. Also @dutch I'm not given a constraint on the number of items, just that they are all integers and spaced uniformly.
Last edited on
Your program will have trouble with input like this:
1
400000000000
18


The problem is that you'll try to check all numbers between 1 and 400 billion to see if they're in the file.

Have you learned about any of the std collections? vector? set?

@dhayden
Thanks for the response. I haven't gone over any of the topics you mentioned, I am working through " Problem Solving with C++ " and I'm only on chapter 6 so the program I wrote for this problem pretty much contains the scope of my knowledge.
Are you sure it's from the right chapter? It seems you don't have the tools to solve it properly (unless perhaps the file is already sorted).

What edition of Problem Solving with C++ is it and what is the question number in Chapter 6?
@dutch

"Problem Solving with C++, Tenth Edition, Walter Savitch"
Pg.402
Chapter 6
Practice Programs question 1.
Last edited on
I can say, from personal experience having been in a introductory college course that uses that exact same textbook, that it is a flaming pile of garbage.
@TheToaster
Thanks for the input bro, good to know from someone who was in the same boat. I'm only using it to get ready for the course, and we are going to be using this textbook.
If you can't use an array or other data structure then I guess you do have to go through the file multiple times.

The problem with your current algorithm is that if the input file contains the two numbers -1000000000 and 1000000000 then your program will open the file two billion times. That seems excessive.

Why not go through the file and find the largest value and how many times it occurs. Then find the next largest, etc.
Here's a possibility:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <iostream>
#include <fstream>
#include <limits>

const int IntMin = std::numeric_limits<int>::min();
const int IntMax = std::numeric_limits<int>::max();

int find_next(int limit) {
    std::ifstream in("numbers.txt");
    int x, cur = IntMin, cnt = 0;
    while (in >> x) {
        if (x <= limit && x >= cur) {
            if (x > cur) { cur = x; cnt = 1; }
            else         ++cnt;
        }
    }
    if (cnt == 0) return IntMin;
    std::cout << cur << ": " << cnt << '\n';
    return cur;
}

int main() {
    for (int n = IntMax; (n = find_next(n)) != IntMin; --n) ;
}

Last edited on
Thanks for the input bro, good to know from someone who was in the same boat. I'm only using it to get ready for the course, and we are going to be using this textbook.


I would recommend a text such as Professional C++ by Gregoire. It is surprisingly thorough and can give you a much more advanced understanding of modern C++, along with design patterns, extensive standard library coverage, etc. It also covers C++17 so that's a huge plus.
Last edited on
I would think twice about taking a course that uses such a textbook.
Why don't you look for some online course where they teach modern C++ ?
@TheToaster
Thanks for the recommendation, I will definitely check out that book once the first class is over.

@Thomas1965
Unfortunately I'm trying to get a CS degree through the local University and the course I am taking is required for that but I do appreciate the advice.


I'm just worried about getting an A in the class and I wanted to use this book so I would know pretty much what we were going to cover.
Last edited on
even without the c++ toys, you can do it with a better algorithm. The issue isn't lack of advanced c++ shortcuts (those help, but these problems are solved in languages that lack built in algorithms and tools), its that his algorithm is brute force.

you need some way to organize the input so that you can quickly find if a number is there or not and if its there, increment its count, if not there, add it with a count of 1.

That is the key to this problem, is being able to store/find/modify the data efficiently; otherwise it will take all day. Anything is better than brute force; grabbing all the data into a container that is sorted, which is pretty efficient, and then counting the numbers that are back to back (eg a sorted list looks like {1,1,1,1,2,2,2,3,5,5,5,1000,1000,1001,..} so you can count them until the data changes, eg got a 1, count it, got another 1, count it, ... got a 2, not working on ones anymore... start over counting the 2s... and so on. There are better ways, but this is pretty good and fairly simple to do.
Last edited on
Topic archived. No new replies allowed.