I am trying to generate all arrangements of strings in a vector. For example, for
vector<string> vs = { "a", "b", "c"};
I wrote the following code:
do{
for (string s : vs)
cout << s << " ";
cout << endl;
} while (std::next_permutation(vs.begin(), vs.end()));
My output is:
a b c
a c b
b a c
b c a
c a b
c b a
but, I am missing the combinations like
a
a b
b a
c
etc..
I would like to modify my code so that includes these arrangements as well. How to do it? Thanks!
Your example shows that you want to not only output each subset of your input (the Power set) but also all permutations of each set.
I am not aware of a particular term used for this, but OEIS A000522 calls these "arrangements".
To get what you need, you have to essentially combine your code with Jarod's partial answer (or any of the other power set implementations you can find around here):
void outputAllPermutations(std::vector<std::string> input)
{
// assert(std::is_sorted(input.begin(), input.end()));
do
{
for (std::string s : input)
std::cout << s << " ";
std::cout << std::endl;
} while (std::next_permutation(input.begin(), input.end()));
}
bool isBitSet(unsigned bitset, std::size_t i)
{
return (bitset & (1 << i)) != 0;
}
void outputAllArrangements(const std::vector<std::string>& input)
{
// assert(std::is_sorted(input.begin(), input.end()));
// assert(input.size() < std::sizeof(unsigned) * 8);
unsigned bitset = 0;
std::vector<std::string> subset{};
subset.reserve(input.size());
for (unsigned bitset = 0; bitset < (1 << input.size()); ++bitset)
{
subset.clear();
for (std::size_t i = 0; i != input.size(); ++i)
if (isBitSet(bitset, i))
subset.push_back(input[i]);
outputAllPermutations(subset);
}
}
Demo including example output
I used an unsigned instead of std::vector<bool> as I find the overall incrementation logic much easier to reason about that way. This theoretically "limits" the code to inputs smaller than 32 strings (or 64, depending on platform), but seeing as input length 22 would already take thousands of years to output at 1 CPU cycle per output I am comfortable with that.
You might implement Power set with:
bool increase(std::vector<bool>& bs)
{
for (std::size_t i = 0; i != bs.size(); ++i) {
bs[i] = !bs[i];
if (bs[i] == true) {
return true;
}
}
return false; // overflow
}
template <typename T>
void PowerSet(const std::vector<T>& v)
{
std::vector<bool> bitset(v.size());
do {
for (std::size_t i = 0; i != v.size(); ++i) {
if (bitset[i]) {
std::cout << v[i] << " ";
}
}
std::cout << std::endl;
} while (increase(bitset));
}
Demo
Then do permutation of each set, something like:
bool increase(std::vector<bool>& bs)
{
for (std::size_t i = 0; i != bs.size(); ++i) {
bs[i] = !bs[i];
if (bs[i] == true) {
return true;
}
}
return false; // overflow
}
template <typename T, typename F>
void PowerSet(const std::vector<T>& v, F f)
{
std::vector<bool> bitset(v.size());
do {
f(v, bitset);
} while (increase(bitset));
}
template <typename T, typename F>
void AllArrangements(const std::vector<T>& v, F f)
{
PowerSet(v, [f](const std::vector<T>& v, const std::vector<bool>& bitset){
std::vector<T> toPermute;
for (std::size_t i = 0; i != v.size(); ++i) {
if (bitset[i]) {
toPermute.push_back(v[i]);
}
}
do {
f(toPermute);
} while (std::next_permutation(toPermute.begin(), toPermute.end()));
});
}
Demo
Related
Is it possible in C++ to split a flat vector (or C style array) into multiple vectors of equal size without copying any of its containing data? That is, disassembling the original vector by moving its content to a new vector, which invalidates the original vector. The following code example should illustrate this:
#include <cassert>
#include <vector>
void f(int* v) {
for (int i = 0; i < 100; i++) {
v[i] = i;
}
}
/**
* Split v into n vectors of equal size without copy its data (assert v.size() % n == 0)
*/
std::vector<std::vector<int>> g(std::vector<int> v, int n) {
std::vector<std::vector<int>> vs(n);
int vec_size = v.size() / n;
for (int i = 0; i < n; i++) {
vs[i].assign(v.begin() + i * vec_size, v.begin() + (i + 1) * vec_size); // copies?
// how to let vs[i] point to v.begin() + i * vec_size?
}
return vs;
}
int main() {
std::vector<int> v(100);
f(v.data());
std::vector<std::vector<int>> vs = g(std::move(v), 10);
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 10; j++) {
assert(vs[i][j] == i * 10 + j);
}
}
return 0;
}
Yes, in my opinion this is possible. Moving the elements, but not copying the elements.
C++ offers std::make_move_iterator. Please read here about that.
To check that, I created a small class to output, to see, if we copy or move something.
So, if your data can "move", then it will work, otherwise of course a copy will be made. With the following we see the result.
struct Test {
int data{};
Test(int d) : data(d) { std::cout << "Construct and init\n"; }
Test() { std::cout << "Default construct\n"; };
~Test() { std::cout << "Destruct\n"; };
Test(const Test& other) { std::cout << "Construct\n"; data = other.data; }
Test(const Test&& other) noexcept { std::cout << "Move Construct\n"; data = other.data; }
Test& operator =(const Test& other) noexcept { std::cout << "Assign\n"; data = other.data; return *this; }
Test& operator =(const Test&& other) noexcept { std::cout << "Move Assign\n"; data = other.data; return *this; }
};
We will additionally add a small function, which calculates the offsets of the chunks that will be moved.
And then, we can come up with a small function to implement that.
#include <iostream>
#include <vector>
#include <numeric>
#include <iterator>
#include <iomanip>
// Calculate start and end index for all chunks
std::vector<std::pair<size_t, size_t>> calculatePairs(const size_t low, const size_t high, const size_t numberOfGroups) {
// Here we will store the resulting pairs with start and end values
std::vector<std::pair<size_t, size_t>> pairs{};
// Calculate chung size and remainder
const size_t delta = high - low;
const size_t chunk = delta / numberOfGroups;
size_t remainder = delta % numberOfGroups;
// Calculate the chunks start and end addresses for all chunks
size_t startIndex{}, endIndex{};
for (size_t i = 0; i < numberOfGroups; ++i) {
// Calculate end address and distribute remainder equally
endIndex = startIndex + chunk + (remainder ? 1 : 0);
// Store a new pair of start and end indices
pairs.emplace_back(startIndex, endIndex);
// Next start index
startIndex = endIndex;
// We now consumed 1 remainder
if (remainder) --remainder;
}
//--pairs.back().second;
return pairs;
}
struct Test {
int data{};
Test(int d) : data(d) { std::cout << "Construct and init\n"; }
Test() { std::cout << "Default construct\n"; };
~Test() { std::cout << "Destruct\n"; };
Test(const Test& other) { std::cout << "Construct\n"; data = other.data; }
Test(const Test&& other) noexcept { std::cout << "Move Construct\n"; data = other.data; }
Test& operator =(const Test& other) noexcept { std::cout << "Assign\n"; data = other.data; return *this; }
Test& operator =(const Test&& other) noexcept { std::cout << "Move Assign\n"; data = other.data; return *this; }
};
std::vector<std::vector<Test>> split(std::vector<Test>& v, unsigned int n) {
std::vector<std::vector<Test>> result{};
if (v.size() > n) {
result.resize(n);
std::vector<std::pair<size_t, size_t>> offset = calculatePairs(0u, v.size(), n);
for (size_t i{}; i < n; ++i) {
result[i].insert(result[i].end(), std::make_move_iterator(v.begin() + offset[i].first),
std::make_move_iterator(v.begin() + offset[i].second));
}
}
return result;
}
constexpr size_t NumberOfElements = 30u;
constexpr unsigned int NumberOfGroups = 3;
static_assert (NumberOfElements >= NumberOfGroups, "Number of elements must be greater/equal then number of elements\n");
int main() {
std::cout << "\n\n\nCreate vector with " << NumberOfElements << " elements\n\n";
std::vector<Test> v1(NumberOfElements);
std::cout << "\n\n\nFill vector with std::iota\n\n";
std::iota(v1.begin(), v1.end(), 1);
std::cout << "\n\n\nSplit in " << NumberOfGroups<< "\n\n";
std::vector<std::vector<Test>> s = split(v1, NumberOfGroups);
std::cout << "\n\n\nOutput\n\n";
for (const std::vector<Test>& vt : s) {
for (const Test& d : vt) std::cout << std::setw(3) << d.data << ' ';
std::cout << "\n\n";
}
}
But my strong guess is that you want to splice the data. The underlying elements fo the std::vector which you can get with the data() function.
You can access the data easily with pointer arithmetic on data().
But if you want to have the data in a new container, then this is difficult with a std::vector. It can for example be done with a std::list that has a splice function and does, what you want.
Or, you need to implement your own dynamic array and implement a splice function . . .
Checksum:
;23M#eTo1?:B#r7C8#wtJ'Z'..uIvLT.j;bld$Bvgjd.qm=8;B/`dHM%D#wyv:\5YI:WVGwJL00%IsKQ9O+&#g,/gzkPg^cg::LX?6dL3;Fs3GOOAmQmCIW?&skWxZXsElyn6S3#fi:0DSKJ/A^r#*'k#a#e8!XDpjAUtu?K5iu+e=P"M7a2BWdFdA.5NP:Y"l,,h+Y/PxhVfP/m0ceS=Nxol2vOZwM2+!H\^a=douX%fhqcr4'0eXiEZeKvTf0^%CTNY^WB6fc#IpK^GQgxTXQo0ikr0+/OxXlc1B$5jc1r,GQj+fwEdoCPrz6,j:SO6L3QU#7lT:f#Y^V!Au\P'a5amR$NCU?\WspBOuy#RH3tJimka#rdyNN56.$;DtRCHN*YeWlrG=',XNSrzEK:Cw;#A%.#/:c,a2W24IIIdecc7O"EnKQn;nXmUemX4kclDsYci+izmr#vlGAQ.w2!cuf;6n2UvJM,CeSyRj1,:2\9#i8GLwtux!uEHUp7X*5SC%nld956CHsy&/n73/90cRP'Me"1PW+##FH8mH4Rf^o=ZP/Rm\X&1syUdUh+.N/jtoO:,OBBAmq,jW69Fu%jJukBa$g4hIrIPcxx17i;XU,FCbQGd8v*AyKGSML\JN#jte*F";Zh7fqhvCXobE&SapX90r"Z$.CN,1R^aj.=5L6^tUB2UPJH^eb'*B!v5=D.9PFI#Pt*KjK+yS*tV6f.5kgPOzBE$uK0MA/\l9U"63LUR6k3#'cub?u&xILMXP%#:lx2TbKhFOjBpMN!+%F16jrgv&AoFhuf%P!==8?x,NsSd%hVo"BJhVv3rjrhvM"WLE3%y#N7g37Re^XiS9lpyKA9E7ow6U=I"tlv",&#+fZoIR4KM58!NTm978wCI?9wo.ocS!9i5k#ler47J.G0yXjZVSdr=G"uRodC06k\V%8;oUwV&z!W5:+ZvE:nyO#+lO+Hn0&tnH&^tNC?'PmERxs/B+KW4O6&oWDED9?MqxmYgVKoT.a%iw
I'm trying to display a vector contents named l_anMarking, but I'm getting this error message:
error : expression must have class type while trying to match the argument list '(std::ostream, std::vector<long,std::allocator<_Ty>>)
I don't understand why I'm having this error. This is my code:
Header file:
class SPSIM_EXPORT ParaStochSimulator : public StochasticSimulator
{
private:
protected:
VectorLong m_anCurrentMarking;
long m_nMinTransPos;
public:
void first_reacsimulator();
void ParaStochSimulator::broad_cast(long);
}
cpp:
void ParaStochSimulator::first_reacsimulator()
{
if (mnprocess_id==0)
{
broad_cast(m_anCurrentMarking);
}
}
void ParaStochSimulator::broad_cast(long j)
{
std::cout << "i'm broad_casting" << std::endl;
double val;
//Get manipulated places
VectorLong l_nMinplacesPos = (*m_pcTransitionsInfo)[j]->GetManipulatedPlaces();
double* l_anMarking=new double [l_nMinplacesPos.size()];
//l_anMarking.clear();
//double var = l_nMinplacesPos.size();
int i = 0;
for (auto lnpos : l_nMinplacesPos)
{
val = m_anCurrentMarking[lnpos];
l_anMarking[i++] = val;
}
std::vector<VectorLong>::iterator it;
for (it = l_anMarking.begin(); it < l_anMarking.end(); it++) //here
{
std::cout << *it << std::endl;
}
MPI_Bcast(&l_anMarking, sizeof l_nMinplacesPos, MPI_DOUBLE, 0, MPI_COMM_WORLD);
delete[] l_anMarking;
}
int main()
{
((spsim::ParaStochSimulator*)l_pcStochSolver)->first_reacsimulator();
}
Your l_anMarking variable is declared as a raw double* pointer, but you are trying to treat it as an STL container by calling begin() and end() on it. Thus the compiler error.
You have two choices:
Change the second for loop to use indexes instead of iterators.
for (int k = 0; k < i; ++k)
{
std::cout << l_anMarking[k] << std::endl;
}
Change l_anMarking to be a std::vector<double> instead (and adjust MPI_Bcast()) accordingly):
std::vector<double> l_anMarking;
l_anMarking.reserve(l_nMinplacesPos.size());
for (auto lnpos : l_nMinplacesPos)
{
val = m_anCurrentMarking[lnpos];
l_anMarking.push_back(val);
}
for (auto marking : l_anMarking)
{
std::cout << marking << std::endl;
}
The compiler error is referring to not being able to print the VectorLong to std::cout
std::vector<VectorLong>::iterator it;
for (it = l_anMarking.begin(); it < l_anMarking.end(); it++) //here
{
std::cout << *it << std::endl; // need an operator<< for your VectorLong
}
You should add something like this:
std::ostream & operator<<(std::ostream &os, const VectorLong& v)
{
os << "VectorLong: ";
for (const auto &l : v)
os << l << " ";
return os;
}
I cannot figure out why the algorithm results in an infinite loop. I am trying to sort the vector according to the final price. The pivot always stays the same. Maybe the problem is with the swapping of the objects
Motorbike findPivotPricee(vector<Motorbike*>& available, int left, int right)
{
int center = (left + right) / 2;
if (available[center]->finalPrice() < available[left]->finalPrice())
{
swap(*available[left], *available[center]);
}
if (available[right]->finalPrice()< available[left]->finalPrice())
{
swap(*available[left], *available[right]);
}
if (available[right]->finalPrice() < available[center]->finalPrice())
{
swap(*available[center], *available[right]);
}
Motorbike pivot = *available[center];
swap(*available[center], *available[right - 1]);
return pivot;
}
void quickSortMotorbikeByPrice(vector<Motorbike*>& available, int left, int right)
{
int i = left;
int j = right-1;
Motorbike pivot = findPivotPricee(available, left, right);
cout << pivot.finalPrice() << endl;
while (i < j)
{
while (available[i]->finalPrice() < pivot.finalPrice())
{
i++;
}
while (available[j]->finalPrice() > pivot.finalPrice())
{
j--;
}
if (i <= j)
{
swap(*available[i], *available[j]);
i++;
j--;
}
else {
break;
}
}
swap(*available[i], *available[right - 1]);//restore the pivot
if (left < right) {
if (left<j) quickSortMotorbikeByPrice(available, left, j);
if (right>i) quickSortMotorbikeByPrice(available, i, right);
}
}
void quickSortMotorbikeByPrice(vector<Motorbike*>& available)
{
quickSortMotorbikeByPrice(available, 0, available.size() - 1);
}
Often, algorithms from wikipedia, e.g. the quicksort algorithms are hard to transform into a working implementation because they fail to point out the assumed programming model implied by the given algorithm. For example, if it is a 0 based or a 1 based array and that they assume that the indices used are signed not unsigned integers.
Then, people start trying to use those algorithms, here, for example in C++ and run into all sorts of problems. Quite a waste of time... And from looking at the code given in the question, I assume the author of the question tried to use info from wikipedia...
Since this is obviously homework, impress your teacher and use the code below. Quicksort is tricky to get right and it can take quite a while to find out if you should write lo = left + 1 or lo = left etc.
#include <cstdint>
#include <memory>
#include <vector>
#include <iostream>
#include <cassert>
template <class X>
void vswap(std::vector<X>& v, size_t i1, size_t i2)
{
X temp = v[i1];
v[i1] = v[i2];
v[i2] = temp;
}
template <typename X>
std::ostream& operator<<(std::ostream& stm, const std::vector<X>& v)
{
stm << "[|";
size_t i = 0;
for (auto& x : v)
{
if (0 == i)
stm << x;
else
stm << "; " << x;
i++;
}
stm << "|]";
return stm;
}
template <typename X>
std::ostream& operator<<(std::ostream& stm, const std::vector<X*>& v)
{
stm << "[|";
size_t i = 0;
for (auto& x : v)
{
if (0 == i)
if (nullptr == x) stm << "nullptr"; else stm << *x;
else
if (nullptr == x) stm << "; nullptr"; else stm << "; " << *x;
i++;
}
stm << "|]";
return stm;
}
template <class X, class Predicate>
size_t partition(std::vector<X> & v, Predicate p, size_t left, size_t right)
{
size_t boundary = left;
X x = v[boundary];
for (size_t i = left; i < right; i++)
{
if (p(v[i], x))
{
vswap(v, boundary, i);
boundary++;
}
}
return boundary;
}
template<class X, class Predicate>
void mysort(std::vector<X> & v, Predicate p, size_t left, size_t right)
{
//std::cout << "mysort: " << v << " " << left << " " << right << std::endl;
if ((right - left) > 1)
{
size_t boundary = partition(v, p, left, right);
//std::cout << "boundary = " << boundary << std::endl;
mysort(v, p, left, boundary);
mysort(v, p, boundary == left ? boundary + 1 : boundary, right);
}
}
class Motorbike
{
size_t m_id;
int32_t m_finalPrice;
public:
Motorbike()
: m_id(0)
, m_finalPrice(0)
{}
Motorbike(size_t id, int32_t finalPrice)
: m_id(id)
, m_finalPrice(finalPrice)
{}
void Id(size_t id)
{
m_id = id;
}
size_t Id() const
{
return m_id;
}
void Price(int32_t price)
{
m_finalPrice = price;
}
int32_t Price() const
{
return m_finalPrice;
}
};
std::ostream& operator<< (std::ostream& stm, const Motorbike& bike)
{
stm << "(" << bike.Id() << ", " << bike.Price() << ")";
return stm;
}
std::vector<Motorbike> randomBikes(size_t count, int32_t lowPrice = 100, int32_t highPrice = 1000)
{
std::vector<Motorbike> result;
result.resize(count);
for (size_t i = 0; i < count; i++)
{
result[i].Id(i);
result[i].Price(lowPrice + rand() * (highPrice - lowPrice) / RAND_MAX);
}
return result;
}
std::vector<Motorbike*> bikePointers(std::vector<Motorbike> & bikes)
{
std::vector<Motorbike*> result;
result.resize(bikes.size());
for (size_t i = 0; i < bikes.size(); i++)
{
result[i] = &bikes[i];
}
return result;
}
int main()
{
//_CrtSetDbgFlag(_CRTDBG_CHECK_ALWAYS_DF);
//_CrtDumpMemoryLeaks();
//{
//{
// std::vector<int32_t> data = { 3, 5, 1, 4, 2, 0 };
// std::cout << "original: " << data << std::endl;
// mysort(data, [](int32_t a, int32_t b) -> bool {return a < b;}, 0, data.size());
// std::cout << "sorted? " << data << std::endl;
//}
//std::cout << "--------------------------------------------------------" << std::endl;
//{
// std::vector<int32_t> data = { 3, 6, 1, 4, 2, 0, 5 };
// std::cout << "original: " << data << std::endl;
// mysort(data, [](int32_t a, int32_t b) -> bool {return a < b;}, 0, data.size());
// std::cout << "sorted? " << data << std::endl;
//}
for(size_t run = 0; run < 10; run++)
{
auto bikes = randomBikes(5+run%2);
auto bikes_p = bikePointers(bikes);
std::cout << "original: " << bikes_p << std::endl;
mysort(bikes_p, [](const Motorbike* m1, const Motorbike* m2)-> bool { return m1->Price() < m2->Price(); }, 0, bikes_p.size());
std::cout << "sorted? " << bikes_p << std::endl;
std::cout << "--------------------------------------------------------" << std::endl;
}
//}
//_CrtDumpMemoryLeaks();
return 0;
}
I am having trouble getting a file to read and then print a list to another file, line by line of the number of occurrences of each.
I works, however it prints the numbers: 1, 2, 3, 4, and 5 to the output file which are not in the read file
The struct:
struct entry {
string word;
string word_uppercase;
int number_occurences;
};
//for array
entry myEntryArray[MAX_WORDS];
int addNewEntryHere=0; //next empty slot
My main calls extractTokensFromLine to read and put in an array:
void extractTokensFromLine(std::string &myString) {
const char CHAR_TO_SEARCH_FOR = ' ';
stringstream ss(myString);
string tempToken;
//Extracts characters from is and stores them into str until the delimitation character delim is found
while (getline(ss, tempToken, CHAR_TO_SEARCH_FOR)) {
processTokenArray(tempToken);
}
}
It goes through each line word by word to put in an array:
void processTokenArray(string &token) {
//temp uppercase for compare
string strUpper = token;
toUpper(strUpper);
//see if its already there
for (int i = 0; i < addNewEntryHere; ++i) {
if (strUpper == myEntryArray[i].word_uppercase) {
//yep increment count
myEntryArray[i].number_occurences++;
return;
}
}
//nope add it
myEntryArray[addNewEntryHere].word = token;
myEntryArray[addNewEntryHere].number_occurences = 1;
myEntryArray[addNewEntryHere].word_uppercase = strUpper;
//where next place to add is
addNewEntryHere++;
}
It then writes the Array to a file (each word and its number of occurrence):
bool writeArraytoFile(const std::string &OUTPUTFILENAME) {
fstream outfile;
if (!openFile(outfile,OUTPUTFILENAME,ios_base::out))
return false;
int var;
for (var = 0; var < addNewEntryHere; ++var) {
string word = myEntryArray[var].word;
if(word != " " && word != "")
outfile<<myEntryArray[var].word << " " <<IntToString(myEntryArray[var].number_occurences)<<std::endl;
}
closeFile(outfile);
return true;
}
The Read File is TestData.txt:
I think I should like a bit of butter
And If its not
too much trouble some toast as well. And while you are in the kitchen a brace of expressos for me and my man here.
My Output file (sorted using the following method):
void sortVector(sortOrder so = NUMBER_OCCURRENCES) {
bool shouldSwap = false;
for (int var = 0; var < addNewEntryHereV; ++var) {
for (int var1 = var+1; var1 < addNewEntryHereV; ++var1) {
switch (so) {
case ASCENDING:
shouldSwap =!compareAscendingV(myEntryVector, var, var1);
break;
//TODO handle the following cases appropriately
case DESCENDING:
shouldSwap =!compareDescendingV(myEntryVector, var, var1);
break;
case NUMBER_OCCURRENCES:
shouldSwap =!sortbyOccurrenceV(myEntryVector, var, var1);
break;
default:
break;
}
if (shouldSwap){
std::string tmp = myEntryVector._V.at(var);
myEntryVector._V.at(var) = myEntryVector._V.at(var1);
myEntryVector._V.at(var1) = tmp;
}
}
}
}
Actual Output:
And 3
4 2
of 2
a 2
I 2
here. 1
man 1
my 1
me 1
for 1
expressos 1
brace 1
kitchen 1
the 1
in 1
are 1
you 1
while 1
well. 1
as 1
toast 1
some 1
trouble 1
much 1
too 1
5 1
3 1
2 1
1 1
not 1
its 1
If 1
butter 1
bit 1
like 1
should 1
think 1
Any sort of suggestion would be greatly appreciated,
thanks
Your specification was not clear to me in some cases so I guessed. This should be pretty close to what you are trying to do.
gcc 4.7.3: g++ -Wall -Wextra -std=c++0x word-freq.cpp
#include <algorithm>
#include <cctype>
#include <iostream>
#include <map>
typedef std::map<std::string, int> histogram_t;
std::string to_lower(const std::string& s) {
std::string r(s);
std::transform(std::begin(r), std::end(r), std::begin(r), ::tolower);
return r; }
histogram_t word_freq(std::istream& is) {
histogram_t m;
std::string s;
while (is >> s) { ++m[to_lower(s)]; }
return m; }
void outAscWord(std::ostream& os, const histogram_t& m) {
for (const auto& e : m) {
os << e.first << " " << e.second << "\n"; } }
void outDescWord(std::ostream& os, const histogram_t& m) {
for (auto i = m.crbegin(); i != m.crend(); ++i) {
os << i->first << " " << i->second << "\n"; } }
template <class A, class B>
std::pair<B, A> flip_pair(const std::pair<A, B>& p) {
return std::pair<B, A>(p.second, p.first); }
template <class A, class B>
std::multimap<B, A> flip_map(const std::map<A, B>& m) {
std::multimap<B, A> r;
std::transform(m.begin(), m.end(), std::inserter(r, r.begin()), flip_pair<A,B>);
return r; }
void outAscCount(std::ostream& os, const histogram_t& m) {
auto mm = flip_map(m);
for (const auto& e : mm) {
os << e.first << " " << e.second << "\n"; } }
int main() {
// Can pass fstreams instead of iostreams if desired.
auto m = word_freq(std::cin);
outAscWord(std::cout, m);
outDescWord(std::cout, m);
outAscCount(std::cout, m);
}
Comparing version numbers as strings is not so easy...
"1.0.0.9" > "1.0.0.10", but it's not correct.
The obvious way to do it properly is to parse these strings, convert to numbers and compare as numbers.
Is there another way to do it more "elegantly"? For example, boost::string_algo...
I don't see what could be more elegant than just parsing -- but please make use of standard library facilities already in place. Assuming you don't need error checking:
void Parse(int result[4], const std::string& input)
{
std::istringstream parser(input);
parser >> result[0];
for(int idx = 1; idx < 4; idx++)
{
parser.get(); //Skip period
parser >> result[idx];
}
}
bool LessThanVersion(const std::string& a,const std::string& b)
{
int parsedA[4], parsedB[4];
Parse(parsedA, a);
Parse(parsedB, b);
return std::lexicographical_compare(parsedA, parsedA + 4, parsedB, parsedB + 4);
}
Anything more complicated is going to be harder to maintain and isn't worth your time.
I would create a version class.
Then it is simple to define the comparison operator for the version class.
#include <iostream>
#include <sstream>
#include <vector>
#include <iterator>
class Version
{
// An internal utility structure just used to make the std::copy in the constructor easy to write.
struct VersionDigit
{
int value;
operator int() const {return value;}
};
friend std::istream& operator>>(std::istream& str, Version::VersionDigit& digit);
public:
Version(std::string const& versionStr)
{
// To Make processing easier in VersionDigit prepend a '.'
std::stringstream versionStream(std::string(".") + versionStr);
// Copy all parts of the version number into the version Info vector.
std::copy( std::istream_iterator<VersionDigit>(versionStream),
std::istream_iterator<VersionDigit>(),
std::back_inserter(versionInfo)
);
}
// Test if two version numbers are the same.
bool operator<(Version const& rhs) const
{
return std::lexicographical_compare(versionInfo.begin(), versionInfo.end(), rhs.versionInfo.begin(), rhs.versionInfo.end());
}
private:
std::vector<int> versionInfo;
};
// Read a single digit from the version.
std::istream& operator>>(std::istream& str, Version::VersionDigit& digit)
{
str.get();
str >> digit.value;
return str;
}
int main()
{
Version v1("10.0.0.9");
Version v2("10.0.0.10");
if (v1 < v2)
{
std::cout << "Version 1 Smaller\n";
}
else
{
std::cout << "Fail\n";
}
}
First the test code:
int main()
{
std::cout << ! ( Version("1.2") > Version("1.3") );
std::cout << ( Version("1.2") < Version("1.2.3") );
std::cout << ( Version("1.2") >= Version("1") );
std::cout << ! ( Version("1") <= Version("0.9") );
std::cout << ! ( Version("1.2.3") == Version("1.2.4") );
std::cout << ( Version("1.2.3") == Version("1.2.3") );
}
// output is 111111
Implementation:
#include <string>
#include <iostream>
// Method to compare two version strings
// v1 < v2 -> -1
// v1 == v2 -> 0
// v1 > v2 -> +1
int version_compare(std::string v1, std::string v2)
{
size_t i=0, j=0;
while( i < v1.length() || j < v2.length() )
{
int acc1=0, acc2=0;
while (i < v1.length() && v1[i] != '.') { acc1 = acc1 * 10 + (v1[i] - '0'); i++; }
while (j < v2.length() && v2[j] != '.') { acc2 = acc2 * 10 + (v2[j] - '0'); j++; }
if (acc1 < acc2) return -1;
if (acc1 > acc2) return +1;
++i;
++j;
}
return 0;
}
struct Version
{
std::string version_string;
Version( std::string v ) : version_string(v)
{ }
};
bool operator < (Version u, Version v) { return version_compare(u.version_string, v.version_string) == -1; }
bool operator > (Version u, Version v) { return version_compare(u.version_string, v.version_string) == +1; }
bool operator <= (Version u, Version v) { return version_compare(u.version_string, v.version_string) != +1; }
bool operator >= (Version u, Version v) { return version_compare(u.version_string, v.version_string) != -1; }
bool operator == (Version u, Version v) { return version_compare(u.version_string, v.version_string) == 0; }
https://coliru.stacked-crooked.com/a/7c74ad2cc4dca888
Here's a clean, compact C++20 solution, using the new spaceship operator <=>, and Boost's string split algorithm.
This constructs and holds a version string as a vector of numbers - useful for further processing, or can be disposed of as a temporary. This also handles version strings of different lengths, and accepts multiple separators.
The spaceship operator lets us provide results for <, > and == operators in a single function definition (although the equality has to be separately defined).
#include <compare>
#include <boost/algorithm/string.hpp>
struct version {
std::vector<size_t> data;
version() {};
version(std::string_view from_string) {
/// Construct from a string
std::vector<std::string> data_str;
boost::split(data_str, from_string, boost::is_any_of("._-"), boost::token_compress_on);
for(auto const &it : data_str) {
data.emplace_back(std::stol(it));
}
};
std::strong_ordering operator<=>(version const& rhs) const noexcept {
/// Three-way comparison operator
size_t const fields = std::min(data.size(), rhs.data.size());
// first compare all common fields
for(size_t i = 0; i != fields; ++i) {
if(data[i] == rhs.data[i]) continue;
else if(data[i] < rhs.data[i]) return std::strong_ordering::less;
else return std::strong_ordering::greater;
}
// if we're here, all common fields are equal - check for extra fields
if(data.size() == rhs.data.size()) return std::strong_ordering::equal; // no extra fields, so both versions equal
else if(data.size() > rhs.data.size()) return std::strong_ordering::greater; // lhs has more fields - we assume it to be greater
else return std::strong_ordering::less; // rhs has more fields - we assume it to be greater
}
bool operator==(version const& rhs) const noexcept {
return std::is_eq(*this <=> rhs);
}
};
Example usage:
std::cout << (version{"1.2.3.4"} < version{"1.2.3.5"}) << std::endl; // true
std::cout << (version{"1.2.3.4"} > version{"1.2.3.5"}) << std::endl; // false
std::cout << (version{"1.2.3.4"} == version{"1.2.3.5"}) << std::endl; // false
std::cout << (version{"1.2.3.4"} > version{"1.2.3"}) << std::endl; // true
std::cout << (version{"1.2.3.4"} < version{"1.2.3.4.5"}) << std::endl; // true
int VersionParser(char* version1, char* version2) {
int a1,b1, ret;
int a = strlen(version1);
int b = strlen(version2);
if (b>a) a=b;
for (int i=0;i<a;i++) {
a1 += version1[i];
b1 += version2[i];
}
if (b1>a1) ret = 1 ; // second version is fresher
else if (b1==a1) ret=-1; // versions is equal
else ret = 0; // first version is fresher
return ret;
}