Even-Odd sorting

  • Thread starter Ney André de Mello Zunino
  • Start date
N

Ney André de Mello Zunino

Hello.

It seems a year is all it takes for one's proficiency in C++ to become
too rusty. Professionally, I've been away from the language (and from
programming in general), but I still preserve an appreciation for it.

So I decided to toy with some idea, just for fun and for evaluating how
rusty I'd become. "Let me write a simple functor for sorting the
elements of a collection! I will start with a simple collection of
integers.", I thought. I then chose an unusual criterion for the
sorting: "I'd like the elements to be ordered so that the even members
should appear before the odd ones." The following is what I came up
with. The program is not producing the expected behavior and I suspect
it has to do with my predicate. It is probably not fully complying to
the requirements for such a function.

#include <iostream>
#include <set>
#include <functional>
#include <algorithm>
#include <iterator>

class EvenOddSorting {
public:
bool operator()(const int i, const int j) const {
return i % 2 == 0 && j % 2 != 0;
}
};

int main() {
std::set<int, EvenOddSorting> intSet;
intSet.insert(10);
intSet.insert(7);
intSet.insert(5);
intSet.insert(18);
intSet.insert(3);
std::cout << "Even-Odd ordered set: ";
std::copy(intSet.begin(),
intSet.end(),
std::eek:stream_iterator<int>(std::cout, " "));
std::cout << "\n";
}

Given the above program, the expected output should read:

Even-Odd ordered set: 10 18 3 5 7

Notice how my proposed output implies a hidden requirement of ordering
among even and odd numbers. That would be ideal, but I'd be satisfied
(at first) with an output like '18 10 5 7 3'. Would anybody care to give
me a hand?

Thank you!
 
J

jason.cipriani

On May 23, 3:41 pm, Ney André de Mello Zunino <[email protected]>
wrote:
[snip]
I then chose an unusual criterion for the
sorting: "I'd like the elements to be ordered so that the even members
should appear before the odd ones." [snip]
The program is not producing the expected behavior and I suspect
it has to do with my predicate. [snip]
class EvenOddSorting {
public:
bool operator()(const int i, const int j) const {
return i % 2 == 0 && j % 2 != 0;
}

}; [snip]
std::set<int, EvenOddSorting> intSet;
[snip]


Your main problem is your predicate along with your use of an
std::set. Note that a set can only hold unique items. That is, no two
elements a and b exist in a set such that a == b.

Your predicate tests if a < b. If a < b OR b < a then a != b. If (NOT
a < b) AND NOT (b < a) then a == b. The set uses your predicate to
determine uniqueness. Because your predicate only compares based on
parity and not values, in effect you have defined all even numbers to
be equivalent, and all odd numbers to be equivalent. Therefore your
set, which must only contain unique values, will contain one even
number, and one odd number.

If you want the value of the integers to be a criteria for uniqueness
your predicate must take that into account. You can also, then, have
sorting "within" the even and odd numbers, if you do something like
this instead:

class EvenOddValueSorting {
public:
bool operator()(const int i, const int j) const {
bool i_is_even = !(i % 2);
bool j_is_even = !(j % 2);
if (i_is_even == j_is_even) // if parity is same...
return i < j; // ... compare by value only
else
return i_is_even; // ... otherwise even always < odd.
}
};

Alternatively, you can use the predicate you have now for sorting, but
do not use a container that requires value uniqueness. For example,
use a vector with your current predicate:

std::vector<int> v;
v.push_back(10);
v.push_back(7);
v.push_back(5);
v.push_back(18);
v.push_back(3);
std::sort(v.begin(), v.end(), EvenOddSorting());

Doing that with EvenOddSorting will place all the even numbers before
all the odd ones. Doing it with EvenOddValueSorting will sort by
numeric value also. Note that std::sort is not stable; but you can use
std::stable_sort if that is a requirement. See:

http://www.sgi.com/tech/stl/table_of_contents.html

Jason
 
N

Ney André de Mello Zunino

Victor said:
What if they are both odd? What if they are both even? Does the
predicate satisfy the requirement of strict weak ordering?

The predicate returns 'true' if the first is even and the second is odd.
For the rest it would return 'false', right? That means that when
comparing 5 and 7 it would determine that 5 is not less than 7. And
when comparing 7 and 5 it would determine that 7 is not less than 5.
That seems to me that 5 is the same as 7. That is, 5 and 7 cannot
coexist in the set that has your 'EvenOddSorting' as the predicate.

Do the same with two evens.

Yes, I see what you mean and it makes perfect sense now.

[...]
OK, that's what it *should* read (you believe). What *does* it read?

Sorry for not posting the results. You were right, only 10 and 7 were
being accepted in the collection.
Not sure what kind of help you are seeking. You've not specified all
the requirements for your predicate to implement it correctly. Have I
given you enough hints to go on?

Yes, sir, you have. Here's the modified functor class (which can
probably be further simplified):

class EvenOddSorting {
public:
bool operator()(const int i, const int j) const {
if (i % 2 == 0) {
if (j % 2 != 0) { // i is even and j is odd
return true;
} else { // both i and j are even
return i < j;
}
} else {
if (j % 2 == 0) { // i is odd and j is even
return false;
} else { // both i and j are odd
return i < j;
}
}
}
};

The program now outputs my desired results:

Even-Odd ordered set: 10 18 3 5 7

Do you see any remaining issue?

Thank you,
 
N

Ney André de Mello Zunino

(e-mail address removed) wrote:

[...]
class EvenOddValueSorting {
public:
bool operator()(const int i, const int j) const {
bool i_is_even = !(i % 2);
bool j_is_even = !(j % 2);
if (i_is_even == j_is_even) // if parity is same...
return i < j; // ... compare by value only
else
return i_is_even; // ... otherwise even always < odd.
}
};

Alternatively, you can use the predicate you have now for sorting, but
do not use a container that requires value uniqueness. For example,
use a vector with your current predicate:

std::vector<int> v;
v.push_back(10);
v.push_back(7);
v.push_back(5);
v.push_back(18);
v.push_back(3);
std::sort(v.begin(), v.end(), EvenOddSorting());

Doing that with EvenOddSorting will place all the even numbers before
all the odd ones. Doing it with EvenOddValueSorting will sort by
numeric value also. Note that std::sort is not stable; but you can use
std::stable_sort if that is a requirement. See:

http://www.sgi.com/tech/stl/table_of_contents.html

Thank you, Jason, for your insights and suggestions. I also appreciated
how simpler your predicate looks compared to mine. Before seeing your
reply, I had further simplified my own version to the following:

bool operator()(const int i, const int j) const {
if (i % 2 == 0 && j % 2 == 0 || i % 2 != 0 && j % 2 != 0) {
return i < j;
} else {
return i % 2 == 0;
}
}

But your version seems a bit easier to follow.

Regards,
 
D

Daniel Pitts

Ney said:
Hello.

It seems a year is all it takes for one's proficiency in C++ to become
too rusty. Professionally, I've been away from the language (and from
programming in general), but I still preserve an appreciation for it.

So I decided to toy with some idea, just for fun and for evaluating how
rusty I'd become. "Let me write a simple functor for sorting the
elements of a collection! I will start with a simple collection of
integers.", I thought. I then chose an unusual criterion for the
sorting: "I'd like the elements to be ordered so that the even members
should appear before the odd ones." The following is what I came up
with. The program is not producing the expected behavior and I suspect
it has to do with my predicate. It is probably not fully complying to
the requirements for such a function.

#include <iostream>
#include <set>
#include <functional>
#include <algorithm>
#include <iterator>

class EvenOddSorting {
public:
bool operator()(const int i, const int j) const {
return i % 2 == 0 && j % 2 != 0;
}
};

int main() {
std::set<int, EvenOddSorting> intSet;
intSet.insert(10);
intSet.insert(7);
intSet.insert(5);
intSet.insert(18);
intSet.insert(3);
std::cout << "Even-Odd ordered set: ";
std::copy(intSet.begin(),
intSet.end(),
std::eek:stream_iterator<int>(std::cout, " "));
std::cout << "\n";
}

Given the above program, the expected output should read:

Even-Odd ordered set: 10 18 3 5 7

Notice how my proposed output implies a hidden requirement of ordering
among even and odd numbers. That would be ideal, but I'd be satisfied
(at first) with an output like '18 10 5 7 3'. Would anybody care to give
me a hand?

Thank you!

Along with the other advice you got, I've re-written parts of your
original to make it what I consider cleaner.

#include <iostream>
#include <set>
#include <algorithm>
#include <iterator>

class EvenOddSorting {
private:
inline bool isEven(const int i) const {
return 0 == i % 2;
}
public:
inline bool operator()(const int i, const int j) const {
return isEven(i+j) ? i < j : isEven(i);
}
};

int main() {
std::set<int, EvenOddSorting> intSet;
const int values[] = {10, 7, 5, 18, 3};
std::copy(values,
values + 5,
std::inserter(intSet, intSet.begin()));
std::cout << "Even-Odd ordered set: ";
std::copy(intSet.begin(),
intSet.end(),
std::eek:stream_iterator<int>(std::cout, " "));
std::cout << "\n";
return 0;
}
 
J

Jerry Coffin

[email protected] says... said:
class EvenOddSorting {
public:
bool operator()(const int i, const int j) const {
return i % 2 == 0 && j % 2 != 0;
}

Just FWIW, the least significant bit of a number is 1 if the number is
odd, and 0 if the number is even. As such, 'i&1' will be 1 iff i is odd.
IOW, the even/odd ordering can be written as: (i&1) < (j&1).

As has already been mentioned, for a set you also need to provide
ordering within the odd and even numbers, so you'll need to add a normal
comparison iff the LSBs are equal.

class EvenOddSorting {
// feel free to return 'i%2' if you prefer
int lsb(int i) const { return i&1; }
public:
bool operator()(int i, int j) const {
return (lsb(i) < lsb(j)) || (lsb(i) == lsb(j) && i<j);
//
// A couple of alterative possibilities that may be more understanable
// to those who don't like boolean expressions.
//
// if (lsb(i) < lsb(j))
// return true;
// return lsb(i)==lsb(j) && i<j;
//
// Possibly even more explicit alternative:
//
// if (lsb(i) < lsb(j))
// return true;
// if (lsb(j) < lsb(i))
// return false;
// return i < j;
}
};
 
V

Vincent Jacques

Hello,

Jerry Coffin a écrit :
Just FWIW, the least significant bit of a number is 1 if the number is
odd, and 0 if the number is even. As such, 'i&1' will be 1 iff i is odd.
IOW, the even/odd ordering can be written as: (i&1) < (j&1).

I've often been told that a good compiler would generate exactly the
same assembly code int those cases (i is an int):
i % 2 and i & 1
i * 2 and i << 1
i / 2 and i >> 1
and for the next powers of 2. (i * 8 and i << 3 etc.)

*Question*: has anybody really looked the assembly code generated by
your compiler in those cases? And what is your conclusion on this point?

If your compiler generates the same assembly, I do think that i % 2 is
more readable than i & 1, because the 'modulo 2' is the mathematic
operation for 'odd or even'.

If your compiler does not generate the same code, then you will have to
profile your code to choose between the two possible solutions. Anyway,
I would stick to i % 2 until there is a proof that this part of code is
an execution speed bottleneck.

Bon week-end,
 
D

Dario Saccavino

Some people in this thread have suggested comparison operators that
rely on the result of (i%2) being either 0 or 1, but this may be false
in case the number is negative. Many compilers implement the division
operators so that the quotient is rounded towards zero, which imply
that i%2==-1 when i<0.

Indeed, the following comparison operators have implementation-defined
behaviour (and give a wrong result on my machine when the numbers are
{10, -7, -5, 18, 3} ):

Victor Bazarov's:
class EvenOddSorting {
public:
bool operator()(int i, int j) const {
if (i % 2 == j % 2)
return i < j; // for the same oddness - just compare them
else
return i % 2 == 0; // for different, 'i' is smaller if it's even
}
};

and an alternative suggested by Jerry Coffin:
class EvenOddSorting {
        // feel free to return 'i%2' if you prefer
        int lsb(int i) const { return i%2; }
public:
        bool operator()(int i, int j) const {
                return (lsb(i) < lsb(j)) || (lsb(i) == lsb(j) && i<j);
};

Cheers,

Dario
 
S

Stefan Ram

Vincent Jacques said:
If your compiler generates the same assembly, I do think that
i % 2 is more readable than i & 1, because the 'modulo 2' is
the mathematic operation for 'odd or even'.

I do not see how the readability of »i % 2« depends on the
code my compiler generates.

»%« is /not/ the mathematical operation »modulo«.

The binary / operator yields the quotient, and the binary %
operator yields the remainder from the division of the first
expression by the second. If the second operand of / or % is
zero the behavior is undefined; otherwise (a/b)*b + a%b is
equal to a. If both operands are nonnegative then the
remainder is nonnegative; if not, the sign of the remainder
is implementation-defined.
If your compiler does not generate the same code, then you will have to
profile your code to choose between the two possible solutions.

I do not have to profile, because I usually do not write C++
source code for a specific compiler and execution environment,
I usually do not invest time in such non-portable
microoptimizations.
 
V

Vincent Jacques

Stefan Ram a écrit :
I do not see how the readability of »i % 2« depends on the
code my compiler generates.

Sorry for not being clear:

I meant:
(1) there are two cases: (a) the generated code is the same or (b) it is
different.
(2) In general, I think that i % 2 is more readable than i & 1 when you
mean 'odd or even'
(3) I prefer to write readable code over micro-optimized code.

Then I go on the two cases of (1):
case (a): from (2), I write i % 2
case (b): from (3) and (2), I write i % 2 until I'm told (by my
customer, by my hierarchy, etc.) that I *have* to write micro-optimized
code.
»%« is /not/ the mathematical operation »modulo«.
[snip the exact meaning of %]

You're right. Yet, the reminder of the division of i by 2 carries more
about 'odd or even' than the bitwise and between i and 1. (motivation
for (2))
I do not have to profile, because I usually do not write C++
source code for a specific compiler and execution environment,
I usually do not invest time in such non-portable
microoptimizations.

So do I, usually. But, from (2), I assume that the comment from Jerry
Coffin is motivated by an hypothetical win of performance. That's why I
talked about optimization.

Cheers,
 
S

Stefan Ram

Vincent Jacques said:
So do I, usually. But, from (2), I assume that the comment from
Jerry Coffin is motivated by an hypothetical win of
performance. That's why I talked about optimization.

Sorry for not taking this into account, I had not read all of
the thread and was just responding to your post.

A function to tell whether a number is odd, might be written
as follows.

inline odd( int const i ){ return( i >= 0 ? i : -i )% 2; }

or

inline odd( int const i ){ return( i >= 0 ? i : -i )& 1; }

As you wrote, whether one of them is faster can only be
determined relative to a specific environment by a
measurement.
 
J

Jerry Coffin

[ ... ]
I've often been told that a good compiler would generate exactly the
same assembly code int those cases (i is an int):
i % 2 and i & 1
i * 2 and i << 1
i / 2 and i >> 1
and for the next powers of 2. (i * 8 and i << 3 etc.)

*Question*: has anybody really looked the assembly code generated by
your compiler in those cases? And what is your conclusion on this point?

Yes. Given test code like this:

int lsb1(int i) { return i&1; }
int lsb2(int i) { return i%2; }

Microsoft (VC++ 7.1) produces code like this (after cleaning up a lot of
cruft):

lsb1:
mov eax, DWORD PTR _i$[esp-4]
and eax, 1
ret 0

lsb2:
mov eax, DWORD PTR _i$[esp-4]
and eax, -2147483647 ; 80000001H
jns SHORT $L286
dec eax
or eax, -2 ; fffffffeH
inc eax
$L286:
ret 0

Using g++ (4.3.0) the result is marginally different (again, after
cleaning things up a bit):

lsb1:
movl 8(%ebp), %eax
popl %ebp
andl $1, %eax
ret

lsb2:
movl 8(%ebp), %eax
popl %ebp
movl %eax, %edx
shrl $31, %edx
addl %edx, %eax
andl $1, %eax
subl %edx, %eax
ret

Even if you don't read assembly language, it's pretty easy to see that
the version using '%' is substantially longer in both cases. More
importantly than the difference in length is the difference in effect --
the first is simple and always works. The extra complexity in the second
not only slows it down, but _stops it from working correctly_!

[ ... ]
If your compiler does not generate the same code, then you will have to
profile your code to choose between the two possible solutions. Anyway,
I would stick to i % 2 until there is a proof that this part of code is
an execution speed bottleneck.

Dario's point was NOT related to speed -- it was related to correct
operation. The code was based on an assumption that 'i%2' would always
yield 0 or 1. That's NOT the case -- when i is negative, it's allowed to
yield -1.

Now, it's certainly true that C and C++ make allowances for some
ancient, oddball hardware. It's fairly safe for a lot of people to
assume their code will never deal with such things, so they can safely
make some assumptions even though they aren't guaranteed to always be
true. Assuming that 'i%2' will always yield 0 or 1 isn't really one of
those safe assumptions though -- yielding -1 isn't some strange
possibility that will only ever happen with a strange compiler on a
weird machine almost nobody's ever seen or heard of. Quite the contrary,
it's a common occurrence with average, everyday compilers running on
extremely common hardware (the Intel/AMD x86/x64).

At least with the tested compilers, the code using 'i&1' will almost
certainly be faster -- but the fact that it works correctly is FAR more
important.
 
J

Jerry Coffin

[ ... ]
A function to tell whether a number is odd, might be written
as follows.

inline odd( int const i ){ return( i >= 0 ? i : -i )% 2; }

or

inline odd( int const i ){ return( i >= 0 ? i : -i )& 1; }

As you wrote, whether one of them is faster can only be
determined relative to a specific environment by a
measurement.

Yes, but it might also be written as:

inline odd(int i) { return i & 1; }

IMO, this is simpler and more readable than either of the previous
possibilities -- and given the requirements of both C and C++, it's just
as dependable and portable as the alternatives.

As an aside, I'd note that all of these need a return type added --
either bool or int would work, though given the name you've chosen, bool
would be the obvious choice.
 
S

Stefan Ram

Jerry Coffin said:
inline odd(int i) { return i & 1; }
IMO, this is simpler and more readable than either of the previous

C++ permits 2's complement, 1's complement, signed magnitude
and possibly other representations for integral types. It is
not obvious to me, that the above definition is correct for
all these representations when i is negative. (It also is not
obvious to me that it is not correct for all these representations.)
 
J

Jerry Coffin

C++ permits 2's complement, 1's complement, signed magnitude
and possibly other representations for integral types. It is
not obvious to me, that the above definition is correct for
all these representations when i is negative. (It also is not
obvious to me that it is not correct for all these representations.)

It's always true in those three representations. In theory C++ (but not
C, anymore) allows other "pure binary" representations, but 1) only
those three actually exist, and 2) being a pure binary representation
means that it would continue to work anyway, even on some representation
that doesn't exist.
 
V

Vincent Jacques

(e-mail address removed)-berlin.de:
Jerry Coffin:
inline odd(int i) { return i & 1; }

Myself:
inline bool odd( int i ) { return i % 2; }
As an aside, I'd note that all of these need a return type added --
either bool or int would work, though given the name you've chosen, bool
would be the obvious choice.

If they return a bool, all the four versions are correct and allow us to
compare oddity
if( odd( i ) == odd( j ) ) { ... }
because we do not rely on i%2 returning only 0 or 1.

As it was pointed out in this thread, it is not true if they return int.
 
J

Jerry Coffin

[ one's complement, two's complement, sign/magnitude and 'i&1' ]
It's always true in those three representations.

Thinking about it a moment longer, that's not true. It works on
sign/magnitude and two's complement, but breaks on one's complement.

So, the original suggestion breaks on two's complement, and my version
breaks on one's complement. At least for me, one's complement machines
fall into that category that I feel fairly safe ignoring -- and I'm one
of probably fewer than a half dozen regular participants here who's
actually worked on a machine that used one's complement for integers.

In theory, I can also see the remote possibility of a biased
representation, in which the bias was odd, where it would also break. I
can't quite imagine anybody doing that though: given the requirement
that non-negative signed numbers and unsigned numbers have the same
representation, you'd need to use the same bias on unsigned numbers as
well, and making math on those work correctly seems a bit cumbersome
(about the only obvious way I can think of would be to remove the bias,
do the math, then reapply the bias). Of course, such a representation
isn't allowed under either C99 or C++ 0x (except under the as-if rule,
if the implementation made it always act like an allowed representation,
of course).
 
A

Aitch

What am I, chopped liver? :_(

<g>

Jason

try this
class EvenOddSorting {
public:
bool operator()(const int i, const int j) const {
return (i % 2 == 0 && j % 2 != 0) || ((i + j) % 2 == 0 && (i
< j));
}

};

btw:
the original code have some problem when gcc compile it. output is 10,
7, the size of intSet is 2.
I guess that the gcc's set implementation compare number i and j by
if(!( compare(i,j) && compare(j,i) ) return; so 10 == 18, and 3 == 5
== 7 under the comparator.
 
D

Daniel Pitts

Jerry said:
[ ... ]
A function to tell whether a number is odd, might be written
as follows.

inline odd( int const i ){ return( i >= 0 ? i : -i )% 2; }

or

inline odd( int const i ){ return( i >= 0 ? i : -i )& 1; }

As you wrote, whether one of them is faster can only be
determined relative to a specific environment by a
measurement.

Yes, but it might also be written as:

inline odd(int i) { return i & 1; }

IMO, this is simpler and more readable than either of the previous
possibilities -- and given the requirements of both C and C++, it's just
as dependable and portable as the alternatives.

As an aside, I'd note that all of these need a return type added --
either bool or int would work, though given the name you've chosen, bool
would be the obvious choice.
It might also be more correctly written as
template<typename T>
inline bool even(const T t) { return (t % 2) == 0; }

template<typename T>
inline bool odd(const T t) { return !even(t); }
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,065
Latest member
OrderGreenAcreCBD

Latest Threads

Top