C++ code - problems with сode execution time - c++

there is code.
#include "pch.h"
#include <algorithm>
#include <iostream>
#include <vector>
#include <stdlib.h>
using namespace std;
vector<int> SearchInt(vector<int> vec, int num) {
vector<int> temp(2);
sort(begin(vec), end(vec));
int j = 0;
for (int i : vec) {
if (i > num) {
temp[0] = i;
temp[1] = j;
return { temp };
}
//cout << i << " !>= " << num << endl ;
j++;
}
cout << "NO";
exit(0);
}
int main()
{
int n;
cin >> n;
vector<int> nums(n, 0);
vector<int> NewNums(n, 0);
for (int i = 0; i < n; i++) {
cin >> nums[i];
}
if (n != nums.size()) {
cout << "://";
return 0;
}
sort(begin(nums), end(nums));
NewNums[1] = nums[nums.size() - 1];
nums.erase(nums.begin() + nums.size() - 1);
NewNums[0] = nums[nums.size() - 1];
nums.erase(nums.begin() + nums.size() - 1);
for (int j = 2; j <= NewNums.size() - 1; j++) {
NewNums[j] = SearchInt(nums, NewNums[j-1]- NewNums[j-2])[0];
nums.erase(nums.begin() + SearchInt(nums, NewNums[j] - NewNums[j - 1])[1]);
}
if (NewNums[NewNums.size()-1] < NewNums[NewNums.size() - 2] + NewNums[0]) {
cout << "YES" << endl;
for (int i : NewNums) {
cout << i << " ";
}
return 0;
}
else {
cout << "NO";
return 0;
}
}
His task is to check whether it is possible from the given Each number is less than the sum of the two adjacent ones.
(each number is less than both of two adjacent ones)
But there is a problem - with a large number of numbers, the code takes too long. Please help me to optimize it, or just give some advice.
numbers cаn not be null.
time limit: 3.0 s
n <= 500000
You are given n numbers a1, a2,…, an. Is it possible to arrange them in a circle so that each number is strictly less than the sum of its neighbors?
For example, for the array [1,4,5,6,7,8], the left array satisfies the condition, while the right array does not, since 5≥4 + 1 and 8> 1 + 6.
Input data
The first line contains one integer n (3≤n≤105) - the number of numbers.
The second line contains n integers a1, a2,…, an (1≤ai≤109) - the numbers themselves. The given numbers are not necessarily different.
Output
If there is no solution, print "NO" on the first line.
If it exists, print "YES" on the first line. After that, on the second line print n numbers - the elements of the array in the order in which they will stand on the circle. The first and last elements you print are considered neighbors on the circle. If there are multiple solutions, output any of them. You can print a circle starting with any of the numbers.

First I'll only briefly analyze technical shortcomings of your code - without analyzing its meaning. After that I'll write my solution of the problem you defined.
Performance problems of your code are due to some strange decisions:
(1) passing std::vector<int> by value and not by reference to SearchInt function - this implies allocating and copying of the whole array on each function invocation,
(2) call SearchInt two times per loop iteration in function main instead of only one,
(3) sort array within each invocation of SearchInt - it is already sorted before the loop.
To be honest your code feels ridiculously time-consuming. I'm only wondering if that was your intention to make it as slow as you possibly can...
I will not analyze correctness of your code according to problem description. To be honest even after fixing technical shortcomings your code seems to me utterly sub-optimal and quite incomprehensible - so it is just easier to solve the problem from scratch to me.
The answer to the problem as defined is YES if the biggest number is smaller than the sum of the second big and the third big and NO otherwise - this follows from the fact that all numbers are positive (in range 1 - 109 according to newly found problem description). If the answer is YES then to make a circle that satisfies the problem description you just need in a sorted sequence of input numbers switch places of the biggest number and the next big one - that's all.
Here is my code for that (for slightly relaxed input format - I'm not checking if number of items is on a separate line and that all items are on the same line - but all correct inputs will be parsed just fine):
#include <set>
#include <iostream>
int main()
{
std::multiset<unsigned> input_set;
unsigned n;
if( !( std::cin >> n ) )
{
std::cerr << "Input error - failed to read number of items." << std::endl;
return 2;
}
if( n - 3U > 105U - 3U )
{
std::cerr << "Wrong number of items value - " << n << " (must be 3 to 105)" << std::endl;
return 2;
}
for( unsigned j = 0; j < n; ++j )
{
unsigned x;
if( !( std::cin >> x ) )
{
std::cerr << "Input error - failed to read item #" << j << std::endl;
return 2;
}
if( x - 1U > 109U - 1U )
{
std::cerr << "Wrong item #" << j << " value - " << x << " (must be 1 to 109)" << std::endl;
return 2;
}
input_set.insert(x);
}
std::multiset<unsigned>::const_reverse_iterator it = input_set.rbegin();
std::multiset<unsigned>::const_reverse_iterator it0 = it;
std::multiset<unsigned>::const_reverse_iterator it1 = ++it;
if( *it0 >= *it1 + *++it )
{
std::cout << "NO (the biggest number is bigger than the sum of the second big and the third big numbers)" << std::endl;
return 1;
}
std::cout << "YES" << std::endl;
std::cout << "Circle: " << *it1 << ' ' << *it0;
do
{
std::cout << ' ' << *it;
}
while( ++it != input_set.rend() );
std::cout << std::endl;
return 0;
}

Related

Mean and Mode of vector array - How can I make a smaller improvement in the function

Doing an exercise to find the mean and mode of a list of numbers input by a user. I have written the program and it works, but I'm wondering if my function 'calcMode' is too large for this program. I've just started looking into functions which is a first attempt. Would it be better to write smaller functions? and if so what parts can I split? Im pretty new to C++ and also looking if I can improve this code. Is there any changes I can make to make this run more efficient?
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
int calcMean(vector<int> numberList)
{
int originNumber = numberList[0];
int nextNumber;
int count = 0;
int highestCount = 0;
int mean = 0;
for (unsigned int i = 0; i <= numberList.size() - 1; i++)
{
nextNumber = numberList[i];
if (nextNumber == originNumber)
count++;
else
{
cout << "The Number " << originNumber << " appears " << count << " times." << endl;
count = 1;
originNumber = nextNumber;
}
}
if (count > highestCount)
{
highestCount = count;
mean = originNumber;
}
cout << "The Number " << originNumber << " appears " << count << " times." << endl;
return mean;
}
int main()
{
vector<int> v;
int userNumber;
cout << "Please type a list of numbers so we can arrange them and find the mean: "<<endl;
while (cin >> userNumber) v.push_back(userNumber);
sort(v.begin(), v.end());
for (int x : v) cout << x << " | ";
cout << endl;
cout<<calcMean(v)<<" is the mean"<<endl;
return 0;
}
One thing to watch out for is copying vectors when you don't need to.
The function signature
int calcMode(vector<int> numberList)
means the numberList will get copied.
int calcMode(const & vector<int> numberList)
will avoid the copy. Scott Meyer's Effective C++ talks about this.
As an aside, calling is a numberList is misleading - it isn't a list.
There are a couple of points that are worth being aware of in the for loop:
for (unsigned int i = 0; i <= numberList.size()-1; i++)
First, this might calculate the size() every time. An optimiser might get rid of this for you, but some people will write
for (unsigned int i = 0, size=numberList.size(); i <= size-1; i++)
The size is found once this way, instead of potentially each time.
They might even change the i++ to ++i. There used to a potential overhead here, since the post-increment might involve an extra temporary value
One question - are you *sure this gives the right answer?
The comparison nextNumber == originNumber is looking at the first number to begin with.
Try it with 1, 2, 2.
One final point. If this is general purpose, what happens if the list is empty?
Would it be better to write smaller functions?
Yes, you can make do the same job using std::map<>; which could be
a much appropriate way to count the repetition of the array elements.
Secondly, it would be much safer to know, what is the size of the
array. Therefore I suggest the following:
std::cout << "Enter the size of the array: " << std::endl;
std::cin >> arraySize;
In the calcMode(), you can easily const reference, so that array
will not be copied to the function.
Here is the updated code with above mentioned manner which you can refer:
#include <iostream>
#include <algorithm>
#include <map>
int calcMode(const std::map<int,int>& Map)
{
int currentRepetition = 0;
int mode = 0;
for(const auto& number: Map)
{
std::cout << "The Number " << number.first << " appears " << number.second << " times." << std::endl;
if(currentRepetition < number.second )
{
mode = number.first; // the number
currentRepetition = number.second; // the repetition of the that number
}
}
return mode;
}
int main()
{
int arraySize;
int userNumber;
std::map<int,int> Map;
std::cout << "Enter the size of the array: " << std::endl;
std::cin >> arraySize;
std::cout << "Please type a list of numbers so we can arrange them and find the mean: " << std::endl;
while (arraySize--)
{
std::cin >> userNumber;
Map[userNumber]++;
}
std::cout << calcMode(Map)<<" is the mode" << std::endl;
return 0;
}
Update: After posting this answer, I have found that you have edited your function with mean instead of mode. I really didn't get it.
Regarding mean & mode: I recommend you to read more. Because in general, a data set can have multiple modes and only one mean.
I personally wouldn't split this code up in smaller blocks, only if i'd want to reuse some code in other methods. But just for this method it's more readable like this.
The order of excecution is aroun O(n) for calc which is quite oke if you ask me

vector element compare c++

This program takes a word from text and puts it in a vector; after this it compares every element with the next one.
So I'm trying to compare element of a vector like this:
sort(words.begin(), words.end());
int cc = 1;
int compte = 1;
int i;
//browse the vector
for (i = 0; i <= words.size(); i++) { // comparison
if (words[i] == words[cc]) {
compte = compte + 1;
}
else { // displaying the word with comparison
cout << words[i] << " Repeated : " << compte; printf("\n");
compte = 1; cc = i;
}
}
My problem in the bounds: i+1 may exceed the vector borders. How to I handle this case?
You need to pay more attention on the initial conditions and bounds when you do iteration and comparing at the same time. It is usually a good idea to execute your code using pen and paper at first.
sort(words.begin(), words.end()); // make sure !words.empty()
int cc = 0; // index of the word we need to compare.
int compte = 1; // counting of the number of occurrence.
for( size_t i = 1; i < words.size(); ++i ){
// since you already count the first word, now we are at i=1
if( words[i] == words[cc] ){
compte += 1;
}else{
// words[i] is going to be different from words[cc].
cout << words[cc] << " Repeated : " << compte << '\n';
compte = 1;
cc = i;
}
}
// to output the last word with its repeat
cout << words[cc] << " Repeated : " << compte << '\n';
Just for some additional information.
There are better ways to count the number of word appearances.
For example, one can use unordered_map<string,int>.
Hope this help.
C++ uses zero-based indexing, e.g., an array of length 5 has indices: {0, 1, 2, 3, 4}. This means that index 5 is outside of the range.
Similarly, given an array arr of characters:
char arr[] = {'a', 'b', 'c', 'd', 'e'};
The loop for (int i = 0; i <= std::size(arr); ++i) { arr[i]; } will cause a read from outside of the range when i is equal to the length of arr, which causes undefined behaviour. To avoid this the loop must stop before i is equal to the length of the array.
for (std::size_t i = 0; i < std::size(arr); ++i) { arr[i]; }
Also note the use of std::size_t as type of the index counter. This is common practice in C++.
Now, let's finish with an example of how much easier this can be done using the standard library.
std::sort(std::begin(words), std::end(words));
std::map<std::string, std::size_t> counts;
std::for_each(std::begin(words), std::end(words), [&] (const auto& w) { ++counts[w]; });
Output using:
for (auto&& [word, count] : counts) {
std::cout << word << ": " << count << std::endl;
}
My problem in the bounds: i+1 may exceed the vector borders. How to I
handle this case?
In modern C++ coding, the problem of an index going past vector bounds can be avoided. Use the STL containers and avoid using indices. With a little effort devoted to learning how to use containers this way, you should never see these kind of 'off-by-one' errors again! As a benefit, the code becomes more easily understood and maintained.
#include <iostream>
#include <vector>
#include <map>
using namespace std;
int main() {
// a test vector of words
vector< string > words { "alpha", "gamma", "beta", "gamma" };
// map unique words to their appearance count
map< string, int > mapwordcount;
// loop over words
for( auto& w : words )
{
// insert word into map
auto ret = mapwordcount.insert( pair<string,int>( w, 1 ) );
if( ! ret.second )
{
// word already present
// so increment count
ret.first->second++;
}
}
// loop over map
for( auto& m : mapwordcount )
{
cout << "word '" << m.first << "' appears " << m.second << " times\n";
}
return 0;
}
Produces
word 'alpha' appears 1 times
word 'beta' appears 1 times
word 'gamma' appears 2 times
https://ideone.com/L9VZt6
If some book or person is teaching you to write code full of
for (i = 0; i < ...
then you should run away quickly and learn modern coding elsewhere.
Same repeated words counting using some C++ STL goodies via multiset and upper_bound:
#include <iostream>
#include <vector>
#include <string>
#include <set>
int main()
{
std::vector<std::string> words{ "one", "two", "three", "two", "one" };
std::multiset<std::string> ms(words.begin(), words.end());
for (auto it = ms.begin(), end = ms.end(); it != end; it = ms.upper_bound(*it))
std::cout << *it << " is repeated: " << ms.count(*it) << " times" << std::endl;
return 0;
}
https://ideone.com/tPYw4a

Using an iterator in a recursive function results in a segmentation fault

I wrote a program that takes in N test cases of integers representing skill levels of students and attempts to find the total number of the smallest group possible if the only restriction is that there can be no to skill levels that are equal on a team and there is no skill gap greater than 1. So the following test case:
4 5 2 3 -4 -3 -5
would output:
3
Because the teams possible are {-4,-3,-5} and {4,5,2,3}, since the first group is only three members the output is 3.
I decided to use a linked list and a recursive function to solve the problem. One recursive function would go left and right of an integer looking for an integer that was higher by one size, is one is found then the element is removed from the list and 1 is returned. The same is done with another function looking for an integer smaller by 1. This should result in the sum of a group and I could compare the different sums to find the smallest. Unfortunately when I tried to implement this not only do I get a segmentation fault but the number that comes out after a few iterations are not even a part of the list and really large.
#include <cmath>
#include <cstdio>
#include <list>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
int findHigherSkillLevel(int skillLevel, list<int>::iterator *it, list<int> &list) {
if (it == NULL) return 0;
if (**it == (skillLevel + 1)) {
//cout << "test3" << endl;
skillLevel++;
list.erase(*it);
*it = list.begin();
//cout << "Iterator in the higher skill level function if it finds a skill level higher by 1: " << **it << endl;
//cout << "The skill level is: " << skillLevel << endl;
return 1 + findHigherSkillLevel(skillLevel, it, list);
} else {
//cout << "Iterator in the higher skill level function if it doesn't find one: " << **it << endl;
return findHigherSkillLevel(skillLevel, ++it, list);
}
return 0;
}
int findLowerSkillLevel(int skillLevel, list<int>::iterator *it, list<int> &list) {
if (it == NULL) return 0;
if (**it == (skillLevel - 1)) {
skillLevel--;
list.erase(*it);
*it = list.begin();
return 1 + findLowerSkillLevel(skillLevel, ++it, list);
} else {
//cout << "test2" << endl;
return findLowerSkillLevel(skillLevel, ++it, list);
}
return 0;
}
int findGroupsSizes(list<int>::iterator *it, list<int> &list) {
if (it == NULL) return 0;
int groupSize = 1;
int skillLevel = **it;
*it = list.erase(*it);
//cout << "Iterator value in the first function: " << **it << endl;
groupSize += findHigherSkillLevel(skillLevel, it, list) + findLowerSkillLevel(skillLevel, it, list);
return groupSize;
}
If I were to use the test case mentioned then it would iterate through 4, then 5, then 2, and then some weird numbers pop out and finally a seg fault. Is it impossible to use iterators on recursive functions if you pop them from the list in those recursions?
main() actually takes in t total test cases followed by t lines of N separated integers. I used the following as a test case:
4
7 4 5 2 3 -4 -3 -5
1 -4
4 3 2 3 1
7 1 -2 -3 -4 2 0 -1
Here is main if it matters:
int main() {
int t; // the number of test cases
cin >> t;
vector<list<int> > skillLevels(t, list<int>());
// input for each test case
for (int i = 0; i < t; i++) {
int n; // number of students for this test case
cin >> n;
// initialize the list for this test case
for (int j = 0; j < n; j++) {
int skillLevel;
cin >> skillLevel;
skillLevels[i].push_back(skillLevel);
}
}
// recursively scan lists for smallest teams
for (int i = 0; i < t; i++) {
int minGroupNumber = skillLevels[i].size();
list<int>::iterator iterator = skillLevels[i].begin();
int skillLevel = skillLevels[i].front();
while (!skillLevels[i].empty()) {
iterator = skillLevels[i].begin();
int currentGroupSize = findGroupsSizes(&iterator, skillLevels[i]);
cout << currentGroupSize << endl;
if (currentGroupSize < minGroupNumber)
minGroupNumber = currentGroupSize;
//cout << minGroupNumber << endl;
if (!skillLevels[i].empty()) skillLevels[i].pop_front();
}
cout << minGroupNumber << endl;
}
return 0;
}
++it is incrementing the pointer (which makes it invalid) not the iterator. You probably want ++*it.
But that might also take you beyond the end of the list.

Increment double by smallest possible valueTest

I want to increment a double value from the smallest possible (negative) value it can take to the largest possible value it can take.
I've started off with this:
int main()
{
double min(numeric_limits<double>::min());
double i(min);
while(i < 0);
{
cout << i << endl;
i += min ;
}
}
Unfortunately, this doesn't produce the desired result - the while loop is skipped after one iteration.
Is there a better way to accomplish my goal?
I'm guessing at what you want from your code: You want to start with largest possible negative value and increment it toward positive infinity in the smallest possible steps until the value is no longer negative.
I think the function you want is nextafter().
int main() {
double value(-std::numeric_limits<double>::max());
while(value < 0) {
std::cout << value << '\n';
value = std::nextafter(value,std::numeric_limits<double>::infinity());
}
}
Firstly,
while(i < 0); // <--- remove this semicolon
{
cout << i << endl;
i += min ;
}
Then, std::numeric_limits<double>::min() is a positive value, so i < 0 will never be true. If you need the most negative value, you'll need
double min = -std::numeric_limits<double>::max();
but I don't know what your i += min line is supposed to do. Adding two most negative number will just yield −∞, and the loop will never finish. If you want to add a number, you'll need another variable, like
double most_negative = -std::numeric_limits<double>::max();
double most_positive = std::numeric_limits<double>::max();
double i = most_negative;
while (i < 0)
{
std::cout << i << std::endl;
i += most_positive;
}
Of course this will just print the most negative number (-1.8e+308), and then i becomes 0 and the loop will exit.
The following runs through all float-values 'in order'. The steps between successive values become smaller as u.mind increases.
No guarantee this is correct and it will take a long time to complete and this isn't portable and it will take even longer for doubles and... etc. etc.
#include <cassert>
#include <iostream>
#include <limits>
union umin {
float mind;
int mini;
} u;
int main()
{
u.mind = std::numeric_limits<float>::max();
std::cout << -u.mind << " " << u.mini << std::endl;
while ( u.mind > 0 ) {
float previous = u.mind;
u.mini -= 1;
std::cout << -u.mind << " " << u.mini << " " << previous - u.mind << std::endl;
assert( previous > u.mind );
}
}

Handling an 'else' type situation within a loop

I have a homework problem for my C++ class and the problem wants us to have the user input a wavelength and then output the correct type of radiation. The point to notice is that there are more Wave Name values than there are Wave Lengths.
My solution is listed below:
const double WAVE_LENGTH[] = { 1e-11, 1e-8, 4e-7, 7e-7, 1e-3, 1e-2 };
const char* WAVE_NAME[] = { "Gamma Rays", "X Rays", "Ultraviolet", "Visible Light", "Infrared", "Microwaves", "Radio Waves" };
double waveLength;
std::cout << "Enter a wavelength in decimal or scientific notation\nWavelength: ";
std::cin >> waveLength;
for (unsigned short i = 0U; i < 6U; ++i)
{
if (waveLength < WAVE_LENGTH[i])
{
std::cout << "The type of radiation is " << WAVE_NAME[i] << std::endl;
break;
}
if (i == 5U) // Last iteration
std::cout << "The type of radiation is " << WAVE_NAME[i + 1] << std::endl;
}
My question is regarding my approach at solving the problem, specifically within the loop. I can't seem to find a way to handle all the situations without creating two conditions inside the loop which seems like it is a poor design. I realize I could use a series of if/else if statements, but I figured a loop is cleaner. Is my approach the best way or is there a cleaner way of coding this?
Thanks!
I think you can simplify your loop to this:
unsigned short i;
for (i = 0U; i < 6U; ++i)
{
if (waveLength < WAVE_LENGTH[i])
{
break;
}
}
std::cout << "The type of radiation is " << WAVE_NAME[i] << std::endl;
In my view a somewhat cleaner design is to add positive infinity as the last element of WAVE_LENGTH. This way your corner case will require no special handling:
#include <iostream>
#include <limits>
...
const double WAVE_LENGTH[] = { 1e-11, 1e-8, 4e-7, 7e-7, 1e-3, 1e-2,
std::numeric_limits<double>::infinity() };
const char* WAVE_NAME[] = { "Gamma Rays", "X Rays", "Ultraviolet", "Visible Light",
"Infrared", "Microwaves", "Radio Waves" };
double waveLength;
std::cout << "Enter a wavelength in decimal or scientific notation\nWavelength: ";
std::cin >> waveLength;
for (int i = 0; i < sizeof(WAVE_LENGTH) / sizeof(WAVE_LENGTH[0]); ++i)
{
if (waveLength < WAVE_LENGTH[i])
{
std::cout << "The type of radiation is " << WAVE_NAME[i] << std::endl;
break;
}
}
Also note how I've avoided having to hard-code the length of the array (6U in your code) in the loop's terminal condition.
You can test the last iteration in the same if. Notice there is no test anymore itn for.
for (unsigned short i = 0U; ; ++i)
{
if (i == 6 || waveLength < WAVE_LENGTH[i])
{
std::cout << "The type of radiation is " << WAVE_NAME[i] << std::endl;
break;
}
}
Alternatively, you can add a extra wavelength set to MAX_FLOAT (or whatever is called in C++) or set the last one to zero and exit if wave_length[i] == 0.0. That way you don't need to "know" the number of wave lengths.