I'm new to C++. I am trying to create a program that reads a file named symbols.txt, fill a vector with the symbols, then take chars that are in the range [d;p] and move them to the second vector while erasing them from the first vector.
int main(){
vector<char> sym1;
vector<char> sym2;
int p = 100, d = 80;
char x;
ifstream symbols("symbols.txt");
while (symbols >> x )
{
sym1.push_back(x);
}
for(int i = 0; i < sym1.size(); i++){
if (sym1[i] < p && sym1[i] > d){
sym2.push_back(sym1[i]);
sym1.erase(sym1.begin()+i);
}
}
}
When I do this, the program works if there are no characters the same, otherwise it only moves half of the characters and leaves the other half untouched.
In your implementation, the first thing you need to notice is that in your for loop, sym1.size() changes if you call sym1.erase(). Second, since your variable i increments every time, you skip some elements in the vector.
For example, the element at index 5 is erased, then the element at index 6 will move down to replace it. In the mean time, your loops does i++, so you skip the element that moved to index 5.
I think you should use iterators with C++ containers, below is an example:
#include <iostream>
#include <vector>
using namespace std;
int main() {
vector<char> sym1;
vector<char> sym2;
int p = 100, d = 80;
char x;
// ifstream symbols("symbols.txt");
// while (symbols >> x)
// {
// sym1.push_back(x);
// }
for(int i = 0; i < 200; i++) {
sym1.push_back(i);
}
int size = sym1.size();
for(vector<char>::iterator it = sym1.begin(); it != sym1.end(); ){
if (*it < p && *it > d){
sym2.push_back(*it);
it = sym1.erase(it);
}
else
it++;
}
for(vector<char>::iterator it = sym2.begin(); it != sym2.end(); it++) {
cout << (int)*it << " ";
}
}
std::vector is good for a lot of things. However, it's terribly inefficient when it comes to insertion or erasing at any other position than the end.
When an element is inserted somewhere (not at end), then the following happens:
The internally managed size is increased by 1. (This may cause a re-allocation of the internal buffer.)
The last element is moved to next position, the second last element is moved to the now "free" position, and so on until the element at insertion index is moved.
Similar happens when an element is erased:
The successor of the element to erase is moved to its previous position, the next is moved to its previous position, and so on until the last element is moved.
Finally, the internally managed size is decreased by 1. (No re-allocation. – The buffer is kept as reserved.)
In OPs case, it's the repeated erasing which makes me concerns. And, when I had this problem in production I used a way to reduce all the moves to the absolutely minimal required.
For this, I use two indices:
one to read and check the characters
one to write the characters which have to be kept in the vector (sym1 in OPs sample).
While the read index is simply incremented for each loop iteration (as usual) the write index is incremented only after an element has been moved (because it should be kept).
As long as nothing is erased, both indices will have identical values. In this case, it's a bit annoying that elements are moved to the position where they already are. However, I consider this as the lesser evil. (Checking whether indices are still identical will probably buy nothing if the move is simple enough.) As soon as something is erased the write index starts to lag behind the read index.
So finally, for a vector of length N there are exactly N write operations to perform (and this for granted). This sounds reliable and not too terrible.
(Erasing elements individually one by one could be in worst case result in
N - 1 + N - 2 + … + 1 moves of elements which are roughly N * (N - 1) / 2 or (N² - N) / 2.)
For the demonstration, the modified sample of OP:
#include <fstream>
#include <iostream>
#include <vector>
int main()
{
std::vector<char> sym1, sym2;
const char p = 'z', d = 'a';
// read sample data
std::ifstream symbols("symbols.txt");
for (char x; symbols >> x;) sym1.push_back(x);
// move all characters in [d, p] to sym2
size_t iWrite = 0;
for (size_t iRead = 0, n = sym1.size(); iRead < n; ++iRead) {
const char c = sym1[iRead];
if (c >= d && c <= p) sym2.push_back(c);
else sym1[iWrite++] = sym1[iRead];
}
// The difference between iWrite - iRead is the number of obsolete elements.
// (It must be equal to sym2.size().)
sym1.resize(iWrite);
// could've been altermatively: sym1.erase(sym1.begin() + iWrite, sym1.end());
// show results:
std::cout << "sym1: '";
for (const char c : sym1) std::cout << c;
std::cout << "'\nsym2: '";
for (const char c : sym2) std::cout << c;
std::cout << "'\n";
}
Input:
I used the above source code as input (as the OP didn't expose the symbols.txt file).
Output:
sym1: '#<>#<>#<>(){::<>1,2;='',='';//::(".");(;>>;)1._();//[,]2_W=0;(_R=0,=1.();R<;++R){=1[R];(>=&&<=)2._();1[W++]=1[R];}//TW-R.//(I2.().)1.(1.()+W,1.());//:::<<"1:'";(:1)::<<;::<<"'\2:'";(:2)::<<;::<<"'\";}'
sym2: 'includefstreamincludeiostreamincludevectorintmainstdvectorcharsymsymconstcharpadzreadsampledatastdifstreamsymbolssymbolstxtforcharxsymbolsxsympushbackxmoveallcharactersindptosymsizetiriteforsizetieadnsymsizeieadnieadconstcharcsymieadifcpcdsympushbackcelsesymiritesymieadhedifferencebetweeniriteieadisthenumberofobsoleteelementstmustbeequaltosymsizesymerasesymbeginiritesymendshowresultsstdcoutsymforconstcharcsymstdcoutcstdcoutnsymforconstcharcsymstdcoutcstdcoutn'
Live Demo on coliru
So, for the range ['a', 'z'], (I used in my demo for better illustration) sym2 ends up containing all lower case characters while sym1 keeps everything else (except the white spaces).
Concerning the missing white spaces I wondered a bit until I realized that the white spaces are filtered out by the read loop:
for (char x; symbols >> x;) sym1.push_back(x);
which used formatted input.
This could be changed using symbols.get(x) instead of symbols >> x but the filtering of white space might be even intended.
Nitpicking:
OP described the range [d;p] (for the characters to move) as requirement.
AFAIK, it's usual that square brackets (or angle brackets) describe ranges with closed ends (i.e. with inclusive border values) while for open ends (exclusive border values) round brackets are used.
Thus, either the requirement has to be (d;p)
or the implementation of the range check
if (sym1[i] < p && sym1[i] > d){ // check for range (d;p)
is not according to the requirement but should be
if (sym1[i] <= p && sym1[i] >= d){ // check for range [d;p]
Related
I've been assigned this question for my lab (and yes I understand there will be backlash because it's homework). I've been working on this question for a couple of days to no avail and I feel like I'm missing something glaringly obvious.
My code:
int processSuitors(vector<int>& currentSuitors, list<int>& rekt)
{
int sizeSuitors = currentSuitors.size();
int eliminated = 2;
while(sizeSuitors != 1)
{
rekt.push_back(currentSuitors[eliminated]);
currentSuitors.erase(currentSuitors.begin() + eliminated);
sizeSuitors--;
if(eliminated > sizeSuitors)
{
eliminated -= sizeSuitors;
}
}
return currentSuitors[0];
}
Prompt:
In an ancient land, the beautiful princess Eve had many suitors. She decided on the following procedure to determine which suitor she would marry. First, all of the suitors would be lined up one after the other and be assigned numbers. The first suitor would be number 1, the second number 2, and so on up to the last suitor, number n. Starting at the first suitor she would then count three suitors down the line (because of the three letters in her name) and the third suitor would be eliminated from winning her hand and he would be removed from the line. Eve would then continue, counting three more suitors and eliminating every third suitor. When she reached the end of the line she would continue counting from the beginning.
Write a function named processSuitors that takes as arguments an STL vector of type int containing the suitors, and an STL list of type int that will collect all the suitors that are eliminated. The function returns an int storing the position a suitor should stand in to marry the princess if there are n suitors. The function that calls processSuitors will send the vector already filled with n suitors (1, 2, 3... n), and an empty list that needs to be filled with the position number of the suitors that were eliminated, in the order they were eliminated.
Restrictions: You may not create any containers (no arrays, no vectors, etc.); you need to use the vector and the list that are passed as parameters.
Use ONLY the following STL functions:
vector::size
vector::erase
vector::begin
ist::push_back
vector::operator[ ]
The adjacent files are hidden since we are to rely on what is given. Any clean-up of my code would be extremely appreciated as well.
What do you think of this solution.
Keep another vector that marks whether an index in your currentSuitors vector has been removed. Then have a helper function that will always find the next free index.
Instead of trying to reduce currentSuitors, you just keep marking elements in the taken list.
size_t findNextFreeSlot(const vector<bool>& taken, size_t pos)
{
// increment to the next candidate position
pos = (pos + 1) % taken.size();
// search for the first free slot
for (size_t i = 0; i < taken.size(); i++)
{
if (taken[pos] == false)
{
return next;
}
pos = (pos + 1) % taken.size();
}
// assert(false); // we should never get here as long as there's one free slot index in taken
return -1;
}
int processSuitors(vector<int>& currentSuitors, list<int>& rekt)
{
size_t len = currentSuitors.size();
vector<bool> taken(len); // keep a vector of eliminated indices from current
size_t index = len; // initialize one past the last valid element
size_t eliminated = 0;
if (len == 0)
{
return -1;
}
while (eliminated < (len-1))
{
// advance the index three times to the next "untaken" index
index = findNextFreeSlot(taken, index);
index = findNextFreeSlot(taken, index);
index = findNextFreeSlot(taken, index);
taken[index] = true; // claim this index as taken
rekt.push_back(currentSuitors[index]); // add the value at this index to the eliminated list
eliminated++;
}
index = findNextFreeSlot(taken, index); // find the last free index
return currentSuitors[index];
}
So I was asked to write a function that changes array's values in a way that:
All of the values that are the smallest aren't changed
if, let's assume, the smallest number is 2 and there is no 3's and 4's then all 5's are changed for 3's etc.
for example, for an array = [2, 5, 7, 5] we would get [2, 3, 4, 3], which generalizes to getting a minimal value of an array which remains unchanged, and every other minimum (not including the first one) is changed depending on which minimum it is. On our example - 5 is the first minimum (besides 2), so it is 2 (first minimum) + 1 = 3, 7 is 2nd smallest after 2, so it is 2+2(as it is 2nd smallest).
I've come up with something like this:
int fillGaps(int arr[], size_t sz){
int min = *min_element(arr, arr+sz);
int w = 1;
for (int i = 0; i<sz; i++){
if (arr[i] == min) {continue;}
else{
int mini = *min_element(arr+i, arr+sz);
for (int j = 0; j<sz; j++){
if (arr[j] == mini){arr[j] = min+w;}
}
w++;}
}
return arr[sz-1];
}
However it works fine only for the 0th and 1st value, it doesnt affect any further items. Could anyone please help me with that?
I don't quite follow the logic of your function, so can't quite comment on that.
Here's how I interpret what needs to be done. Note that my example implementation is written to be as understandable as possible. There might be ways to make it faster.
Note that I'm also using an std::vector, to make things more readable and C++-like. You really shouldn't be passing raw pointers and sizes, that's super error prone. At the very least bundle them in a struct.
#include <algorithm>
#include <set>
#include <unordered_map>
#include <vector>
int fillGaps (std::vector<int> & data) {
// Make sure we don't have to worry about edge cases in the code below.
if (data.empty()) { return 0; }
/* The minimum number of times we need to loop over the data is two.
* First to check which values are in there, which lets us decide
* what each original value should be replaced with. Second to do the
* actual replacing.
*
* So let's trade some memory for speed and start by creating a lookup table.
* Each entry will map an existing value to its new value. Let's use the
* "define lambda and immediately invoke it" to make the scope of variables
* used to calculate all this as small as possible.
*/
auto const valueMapping = [&data] {
// Use an std::set so we get all unique values in sorted order.
std::set<int> values;
for (int e : data) { values.insert(e); }
std::unordered_map<int, int> result;
result.reserve(values.size());
// Map minimum value to itself, and increase replacement value by one for
// each subsequent value present in the data vector.
int replacement = *values.begin();
for (auto e : values) { result.emplace(e, replacement++); }
return result;
}();
// Now the actual algorithm is trivial: loop over the data and replace each
// element with its replacement value.
for (auto & e : data) { e = valueMapping.at(e); }
return data.back();
}
I'm trying to create this c++ program to perform the description below. I am pretty certain the issue is in the recursive, but uncertain how to fix it. I'm guessing it just keeps iterating through to infinity and crashes. I do not even get an output. I figured I could just compare the previous and current pointer and perform a 3-piece temp swap based on lexicography. I would use a pointer to iterate through the array and decrement it after each swap, then recursively call with that ptr as the parameter. Didn't work, I'm here, help me please :). If there is a simpler solution that would work too, but prefer to understand where I went wrong with this code.
#include <string>
#include <iostream>
using namespace std;
// Given an array of strictly the characters 'R', 'G', and
// 'B', segregate the values of the array so that all the
// Rs come first, the Gs come second, and the Bs come last.
// You can only swap elements of the array.
char* RGBorder(char* c_a)
{
size_t sz = sizeof(c_a)/sizeof(*c_a);
char* ptr_ca = c_a;
char* prv_ptr = ptr_ca;
ptr_ca++;
char temp;
while(*ptr_ca)
{
switch(*ptr_ca)
{
case 'R' :
if( *prv_ptr < *ptr_ca ) {
temp = *prv_ptr; *prv_ptr = *ptr_ca; *ptr_ca = temp;
} else if( *prv_ptr == *ptr_ca ) {
continue;
} else { ptr_ca--; RGBorder(ptr_ca); }
case 'G' :
if( *prv_ptr < *ptr_ca ) {
temp = *prv_ptr; *prv_ptr = *ptr_ca; *ptr_ca = temp;
} else if( *prv_ptr == *ptr_ca ) {
continue;
} else { ptr_ca--; RGBorder(ptr_ca); }
default:
ptr_ca++;
continue;
}
ptr_ca++;
cout << *ptr_ca;
}
return c_a;
}
int main()
{
char ca[] = {'G', 'B', 'R', 'R', 'B', 'R', 'G'};
char *oca =RGBorder(ca);
char *pca = oca;
while(*pca)
{
cout << *pca << endl;
pca++;
}
}
There are many issues with your code.
1) You call the function RGBorder with a character pointer, and then attempt to get the number of characters using this:
size_t sz = sizeof(c_a)/sizeof(*c_a);
This will not get you the number of characters. Instead this will simply get you the
sizeof(char *) / sizeof(char)
which is usually 4 or 8. The only way to call your function using a char array is either provide a null-terminated array (thus you can use strlen), or you have to pass the number of characters in the array as a separate argument:
char *RGBorder(char *c_a, int size)
2) I didn't go through your code, but there are easier ways to do a 3-way partition in an array. One popular algorithm to do this is one based on the Dutch National Flag problem.
Since you want the array in RGB order, you know that the series of G will always come in the middle (somewhere) of the sequence, with R on the left of the sequence, and B always on the right of the sequence.
So the goal is to simply swap R to the left of the middle, and B to the right of the middle. So basically you want a loop that incrementally changes the "middle" when needed, while swapping R's and B's to their appropriate position when they're detected.
The following code illustrates this:
#include <algorithm>
char *RGBorder(char *c_a, int num)
{
int middle = 0; // assume we only want the middle element
int low = 0; // before the G's
int high = num - 1; // after the G's
while (middle <= high)
{
if ( c_a[middle] == 'R' ) // if we see an 'R' in the middle, it needs to go before the middle
{
std::swap(c_a[middle], c_a[low]); // swap it to a place before middle
++middle; // middle has creeped up one spot
++low; // so has the point where we will swap when we do this again
}
else
if (c_a[middle] == 'B') // if we see a 'B' as the middle element, it needs to go after the middle
{
std::swap(c_a[middle], c_a[high]); // place it as far back as you can
--high; // decrease the back position for next swap that comes here
}
else
++middle; // it is a 'G', do nothing
}
return c_a;
}
Live Example
Here is another solution that uses std::partition.
#include <algorithm>
#include <iostream>
char *RGBorder(char *c_a, int num)
{
auto iter = std::partition(c_a, c_a + num, [](char ch) {return ch == 'R';});
std::partition(iter, c_a + num, [](char ch) {return ch == 'G';});
return c_a;
}
Live Example
Basically, the first call to std::partition places the R's to the front of the array. Since std::partition returns an iterator (in this case, a char *) to the end of where the partition occurs, we use that as a starting position in the second call to std::partition, where we partition the G values.
Note that std::partition also accomplishes its goal by swapping.
Given this solution, we can generalize this for an n-way partition by using a loop. Assume we want to place things in RGBA order (4 values instead of 3).
#include <algorithm>
#include <iostream>
#include <cstring>
char *RGBorder(char *c_a, int num, char *order, int num2)
{
auto iter = c_a;
for (int i = 0; i < num2 - 1; ++i)
iter = std::partition(iter, c_a + num, [&](char ch) {return ch == order[i];});
return c_a;
}
int main()
{
char ca[] = "AGBRRBARGGARRBGAGRARAA";
std::cout << RGBorder(ca, strlen(ca), "RGBA", 4);
}
Output:
RRRRRRRGGGGGBBBAAAAAAA
Sorry to put it blunt, but that code is a mess. And I don't mean the mistakes, those are forgivable for beginners. I mean the formatting. Multiple statements in one line make it super hard to read and debug the code. Short variable names that carry no immediate intrinsic meaning make it hard to understand what the code is supposed to do. using namespace std; is very bad practise as well, but I can imagine you were taught to do this by whoever gives that course.
1st problem
Your cases don't break, thus you execute all cases for R, and both G and default for G. Also your code will never reach the last 2 lines of your loop, as you continue out before in every case.
2nd problem
You have an endless loop. In both cases you have two situations where you'll end up in an endless loop:
In the else if( *prv_ptr == *ptr_ca ) branch you simply continue; without changing the pointer.
In the else branch you do ptr_ca--;, but then in default you call ptr_ca++; again.(Note that even with breaks you would still call ptr_ca++; at the end of the loop.)
In both cases the pointer doesn't change, so once you end up in any of those conditions your loop will never exit.
Possible 3rd problem
I can only guess, because it is not apparent from the name, but it seems that prv_ptr is supposed to hold whatever was the last pointer in the loop? If so, it seems wrong that you don't update that pointer, ever. Either way, proper variable names would've made it more clear what the purpose of this pointer is exactly. (On a side note, consistent usage of const can help identify such issues. If you have a variable that is not const, but never gets updated, you either forgot to add const or forgot to update it.)
How to fix
Format your code:
Don't use using namespace std;.
One statement per line.
Give your variables proper names, so it's easy to identify what is what. (This is not 1993, really, I'd rather have a thisIsThePointerHoldingTheCharacterThatDoesTheThing than ptr_xy.)
Fix the aforementioned issues (add breaks, make sure your loop actually exits).
Then debug your code. With a debugger. While it runs. With breakpoints and stepping through line by line, inspecting the values of your pointers as the code executes. Fancy stuff.
Good luck!
just count the number of 'R', 'G' and 'B' letters and fill the array from scratch.
much easier, no recursions.
(This problem is taken from IEEE Xtreme 2013 problem AS)
The problem I have to solve is as follows:
I am given a series of letters, consisting of pairs of joined crossroads, such as the below
F G
F H
H I
H J
I K
J K
G H
G I
The task is to navigate from one given letter to another, such as from F to K. In this case the optimum path would be F, H, I, K (i.e. the shortest path (or one of them)).
Part of the task is to find all possible paths. As the length of the path is variable, along with the number of crossroads and the number of crossroads joined to each crossroad, I reasoned I would need a variable level of nested loops - a new nested level of loop for each step along the path, along with the ability to step back along the path and go in a different direction.
the only way I could think of solving this was by creating a function, passing it an incomplete path, search for a joined letter, and have the function call itself until it reaches the end of the path, whereupon it steps back to a previous crossroads and goes in a different direction.
Is the recursion here a bad idea, or inefficient? is there a better/simpler/clearer way of solving this problem?
Code: (It doesn't quite run yet, I'm not finished with it)
(The recursive function is pathfind)
// Problem_AS (ice cream route finder).cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;
//typdefs
typedef vector<char> route;
//each route when found will be put into a vector of routes
//function declarations
bool checkChar(vector<char>& chars, const char& c);
vector<char> create_turnings(vector<string>& pairs);
vector<char> find_joined(const char& a, vector<string>& routes);
void pathfind(const vector< vector<char> >& joined, const char& current, const char& dest, vector<char>& non_option,
vector<route>& routes, const vector<char>& turnings, route& current_route);
int _tmain(int argc, _TCHAR* argv[])
{
char dest;
cin >> dest;
char start = 'f';
vector<string> joined;
const string a_a = "a a";
char input[4];
do
{
cin.getline(input, sizeof(input));
if (input != a_a)
joined.push_back(input);
} while (input != a_a);
vector<char> turnings = create_turnings(joined);
//alpabetic list of all the turns
/*vector<char>::iterator iter;
for (iter = turnings.begin(); iter < turnings.end(); iter++)
{
cout << *iter << endl;
}*/
//dir is a vector of string vectors - for each letter, there will be a vector, containing all the
//letters that can be reached from the current letter
vector < vector<char> > dir;
for (unsigned int i = 0; i < turnings.size(); i++)
{
dir.push_back(find_joined(turnings[i], joined));
cout << turnings[i] << ": ";
for (unsigned int j = 0; j < dir[i].size(); j++)
{
cout << dir[i][j] << ", ";
}
cout << endl;
}
vector<char> non_option;
//will be passed to functions, contains all the letters that have been used or should not be
route current_route; //contains the current path
vector< route > routes; //vector of all valid paths
pathfind(dir,start, dest, non_option, routes, turnings, current_route);
getchar();
return 0;
}
//checks if char c is present in the character array chars
bool checkChar(vector<char>& chars, const char& c)
{
vector<char>::iterator iter;
for (iter = chars.begin(); iter < chars.end(); iter++)
{
if (*iter == c)
return true;
}
return false;
}
//converts a string vector of pattern:
//"g t"
//"t y"
//"d t"
//etc into a char vector in alphabetic order (containing no repeated characters)
//the first entry in pairs is skipped, as it is empty
vector<char> create_turnings(vector<string>& pairs)
{
vector<char> a;
a.push_back(pairs[1][0]);
if (!checkChar(a, pairs[1][2]))
a.push_back(pairs[1][2]);
vector<string>::iterator iter;
//do first action so that check char in loop has something to check against
for (iter = pairs.begin() + 2; iter < pairs.end(); iter++)
{
if (!checkChar(a, (*iter)[0]))
a.push_back((*iter)[0]);
if (!checkChar(a, (*iter)[2]))
a.push_back((*iter)[2]);
}
sort(a.begin(), a.end());
return a;
}
//takes a letter in the map, and finds all the letters associated with it, and outputs them in a char vector
//(alpthabetcially)
vector<char> find_joined(const char& a, vector<string>& pairs)
{
vector<char> joined;
vector<string>::iterator iter;
for (iter = pairs.begin()+1; iter < pairs.end(); iter++) // must skip first element as it is an empty string
{
if ((*iter)[0] == a)
joined.push_back((*iter)[2]);
if ((*iter)[2] == a)
joined.push_back((*iter)[0]);
//must check is the letter we are finding associations for on the left or right of the route - the opposite
//side of the joined letters will be added
}
sort(joined.begin(), joined.end());
return joined;
}
void pathfind(const vector< vector<char> >& joined, const char& current, const char& dest, vector<char>& non_option,
vector<route>& routes, const vector<char>& turnings, route& current_route)
{
current_route.push_back(current);
non_option.push_back(current);
//find the index of current in turnings
unsigned int index;
for (index = 0; index < turnings.size(); index++)
{
if (turnings[index] == current)
break;
}
int x = joined[index].size();
for (int i = 0; i < x; i++)
{
if (checkChar(non_option, joined[index][i]))
continue;
//check if the character being checked has been makred as a non option already
if (joined[index][i] == dest)
{
current_route.push_back(joined[index][i]);
routes.push_back(current_route);
continue;
}
//check if the character being checked is the destination, in which case mark a route
pathfind(joined, joined[index][i], dest, non_option, routes, turnings, current_route);
while (non_option.size() != current_route.size())
{
non_option.pop_back();
}
}
while (non_option.size() != current_route.size())
{
non_option.pop_back();
}
current_route.pop_back();
}
In answer to the question, but without specific reference to the code given:
Is recursion a bad thing?
In general, No.
What are the trade-offs?
Often using recursion boils down to simplicity of implementation versus simpicity of debugging and flexibility of changing implementations.
General factors around recursive / non-recursive implementations:
Judging by the number of questions that crop-up on StackOverflow regarding recursion, it's pretty clear that recursive code is harder to follow for many developers, partly because the code-path isn't always obvious. A non-recursive function is often easier to debug... you can step through the code and examine your stack easily without having to track which invocation of the method you're at.
However, removing recursion can sometimes make code more complex... often you have to introduce your own stack and do a bit of housekeeping that isn't necessary with recursive code.
I frequently see posters on Stack Overflow using recursion when a simple iteration would suffice such as traversing a list (no stack needed). In these trivial cases recursion should definitely be avoided (unless its a requirement of your assignment). In more comlex cases, the performance differences between a recursive solution and a non-recursive one usually aren't significant.
And although recursion itself isn't bad, one consideration is that because the size of the stack is limited by your environment you have an arbitrary limit imposed on you for the amount of recursive calls. If you know that the termination condition for your calls is met quickly then by all means recurse. If you're working with very large data sets, and potentially deep recursion, exercise caution and favour a non-recursive implementation.
Whenever you are thinking about a recursive solution (or any algorithm for that matter), favour clarity, readability and easy debugging first, only optimise when performance profiling shows there's a problem. Try to keep code encapsulated so implementations can be easily swapped out.
The recursion in not always a bad idea, however there's a problem with using a recursion which is the stack depth. If your recursion goes too deep - you run out of stack and your program will not work properly. Stack depth can change from system to system, may or may not be adjustable, and stack overruns may or may not lead to an immediate crash.
If you're considering a recursion of bounded depth that you know is within the limits of your stack - go for it, it is the easier way to write code.
If you're considering recursion with unbounded depth (i.e.: you don't know how big the input can be and how many calls you're going to make) - you better implement it iteratively (i.e.: without using the OS stack, use your own structures to manage the state).
There are also ways to make recursions more efficient wrt the stack usage (for example - tail recursion).
In this particular case, the input doesn't seem to be too large, and I'd expect the recursion to work. If it doesn't - it is more likely to be a bug in your algorithm/implementation, rather than an inherent "recursion problem".
My question is a follow-up to How to make this code faster (learning best practices)?, which has been put on hold (bummer). The problem is to optimize a loop over an array with floats which are tested for whether they lie within a given interval. Indices of matching elements in the array are to be stored in a provided result array.
The test includes two conditions (smaller than the upper threshold and bigger than the lower one). The obvious code for the test is if( elem <= upper && elem >= lower ) .... I observed that branching (including the implicit branch involved in the short-circuiting operator&&) is much more expensive than the second comparison. What I came up with is below. It is about 20%-40% faster than a naive implementation, more than I expected. It uses the fact that bool is an integer type. The condition test result is used as an index into two result arrays. Only one of them will contain the desired data, the other one can be discarded. This replaces program structure with data structure and computation.
I am interested in more ideas for optimization. "Technical hacks" (of the kind provided here) are welcome. I'm also interested in whether modern C++ could provide means to be faster, e.g. by enabling the compiler to create parallel running code. Think visitor pattern/functor. Computations on the single srcArr elements are almost independent, except that the order of indices in the result array depends on the order of testing the source array elements. I would loosen the requirements a little so that the order of the matching indices reported in the result array is irrelevant. Can anybody come up with a fast way?
Here is the source code of the function. A supporting main is below. gcc needs -std=c++11 because of chrono. VS 2013 express was able to compile this too (and created 40% faster code than gcc -O3).
#include <cstdlib>
#include <iostream>
#include <chrono>
using namespace std;
using namespace std::chrono;
/// Check all elements in srcArr whether they lie in
/// the interval [lower, upper]. Store the indices of
/// such elements in the array pointed to by destArr[1]
/// and return the number of matching elements found.
/// This has been highly optimized, mainly to avoid branches.
int findElemsInInterval( const float srcArr[], // contains candidates
int **const destArr, // two arrays to be filled with indices
const int arrLen, // length of each array
const float lower, const float upper // interval
)
{
// Instead of branching, use the condition
// as an index into two distinct arrays. We need to keep
// separate indices for both those arrays.
int destIndices[2];
destIndices[0] = destIndices[1] = 0;
for( int srcInd=0; srcInd<arrLen; ++srcInd )
{
// If the element is inside the interval, both conditions
// are true and therefore equal. In all other cases
// exactly one condition is true so that they are not equal.
// Matching elements' indices are therefore stored in destArr[1].
// destArr[0] is a kind of a dummy (it will incidentally contain
// indices of non-matching elements).
// This used to be (with a simple int *destArr)
// if( srcArr[srcInd] <= upper && srcArr[srcInd] >= lower) destArr[destIndex++] = srcInd;
int isInInterval = (srcArr[srcInd] <= upper) == (srcArr[srcInd] >= lower);
destArr[isInInterval][destIndices[isInInterval]++] = srcInd;
}
return destIndices[1]; // the number of elements in the results array
}
int main(int argc, char *argv[])
{
int arrLen = 1000*1000*100;
if( argc > 1 ) arrLen = atol(argv[1]);
// destArr[1] will hold the indices of elements which
// are within the interval.
int *destArr[2];
// we don't check destination boundaries, so make them
// the same length as the source.
destArr[0] = new int[arrLen];
destArr[1] = new int[arrLen];
float *srcArr = new float[arrLen];
// Create always the same numbers for comparison (don't srand).
for( int srcInd=0; srcInd<arrLen; ++srcInd ) srcArr[srcInd] = rand();
// Create an interval in the middle of the rand() spectrum
float lowerLimit = RAND_MAX/3;
float upperLimit = lowerLimit*2;
cout << "lower = " << lowerLimit << ", upper = " << upperLimit << endl;
int numInterval;
auto t1 = high_resolution_clock::now(); // measure clock time as an approximation
// Call the function a few times to get a longer run time
for( int srcInd=0; srcInd<10; ++srcInd )
numInterval = findElemsInInterval( srcArr, destArr, arrLen, lowerLimit, upperLimit );
auto t2 = high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>( t2 - t1 ).count();
cout << numInterval << " elements found in " << duration << " milliseconds. " << endl;
return 0;
}
Thinking of the integer range check optimization of turning a <= x && x < b into ((unsigned)(x-a)) < b-a, a floating point variant comes to mind:
You could try something like
const float radius = (b-a)/2;
if( fabs( x-(a+radius) ) < radius )
...
to reduce the check to one conditional.
I see about a 10% speedup from this:
int destIndex = 0; // replace destIndices
int isInInterval = (srcArr[srcInd] <= upper) == (srcArr[srcInd] >= lower);
destArr[1][destIndex] = srcInd;
destIndex += isInInterval;
Eliminate the pair of output arrays. Instead only advance the 'number written' by 1 if you want to keep the result, otherwise just keep overwriting the 'one past the end' index.
Ie, retval[destIndex]=curIndex; destIndex+= isInArray; -- better coherancy and less wasted memory.
Write two versions: one that supports a fixed array length (of say 1024 or whatever) and another that supports a runtime parameter. Use a template argumemt to remove code duplication. Assume the length is less than that constant.
Have the function return size and a RVO'd std::array<unsigned, 1024>.
Write a wrapper function that merges results (create all results, then merge them). Then throw the parrallel patterns library at the problem (so the results get computed in parrallel).
If you allow yourself vectorization using the SSE (or better, AVX) instruction set, you can perform 4/8 comparisons in a go, do this twice, 'and' the results, and retrieve the 4 results (-1 or 0). At the same time, this unrolls the loop.
// Preload the bounds
__m128 lo= _mm_set_ps(lower);
__m128 up= _mm_set_ps(upper);
int srcIndex, dstIndex= 0;
for (srcInd= 0; srcInd + 3 < arrLen; )
{
__m128 src= _mm_load_ps(&srcArr[srcInd]); // Load 4 values
__m128 tst= _mm_and_ps(_mm_cmple_ps(src, lo), _mm_cmpge_ps(src, up)); // Test
// Copy the 4 indexes with conditional incrementation
dstArr[dstIndex]= srcInd++; destIndex-= tst.m128i_i32[0];
dstArr[dstIndex]= srcInd++; destIndex-= tst.m128i_i32[1];
dstArr[dstIndex]= srcInd++; destIndex-= tst.m128i_i32[2];
dstArr[dstIndex]= srcInd++; destIndex-= tst.m128i_i32[3];
}
CAUTION: unchecked code.