1

I want to sort an array of rational numbers of type integers. I have used the Bubble Sort algorithm. I am dividing numerator with denominator and then comparing two rational numbers on the basis of their float quotient value. But, I am not getting the right result.

Code for sorting:

void sort(rational arr[], int n) {
    int i, j;
    for (i = 0; i < n-1; i++)
        for (j = 0; j < n-i-1; j++)
            if (arr[j] > arr[j+1])
                swap(arr[j], arr[j+1]);
}

Code for Swapping:

void swap(rational &r1, rational &r2) {
       rational temp;
       temp.set(r1.getNum(),r1.getDenom());
       r1.set(r2.getNum(),r2.getDenom());
       r2.set(temp.getNum(),temp.getDenom());
}

Code for Comparing:

int operator>(const rational&r2) const {
        float n1 = (float)this->num/(float)this->denom;
        float n2 = (float)r2.num/(float)r2.denom;

        if(n1>n2) return 1;
        if(n1==n2) return 0;
        if(n1<n2) return -1;
}

Main

    int main(int argc, char** argv) {

    rational *arr;
    int n = 10;
    cout<<"\You may enter upto 10 relational numbers. How many?"<<endl;
    cin>> n;
    arr = new rational[n];

    fillArray(arr,n);
    cout<<"\nBefore sorting Array contains: "<<endl;
    displayArray(arr,n);
    cout<<"\nAfter sorting Array contains: "<<endl;
    sort(arr,n);
    displayArray(arr,n);
    return 0;
}

Current output: enter image description here

Expected Output:

enter image description here

It can be in ascending or descending order.

Thanks in advance.

Junaid
  • 941
  • 2
  • 14
  • 38
  • 2
    Include your input, your output, and your expected output, as part of your driver `main` running this code, which you should also include (as part of a [mcve]). And fyi, I'm certain `(float)r2.num/(float)r2.num;` is a typo either in your posted code, or your actual code, and should be `(float)r2.num/(float)r2.denom;` in that comparative instead. What you have now is near-pointless for `n2`, as it will always be `1`. Finally, lose the `==` condition in your if-chain. just return `0` if the other two don't pan out. – WhozCraig Nov 16 '19 at 09:19
  • Show us what you expect vs what you actually get. – schaiba Nov 16 '19 at 09:19
  • 2
    Don't divide, cross multiply, i.e. `a/b > c/d` iff `a*d > b*c`. That way everything remains in the integer domain, and all results are exact. – user3386109 Nov 16 '19 at 09:20
  • 1
    There is no `operator>` in C, please do not tag questions with both C and C++ unless it's absolutely necessary (or portable between the two). – paxdiablo Nov 16 '19 at 09:23
  • 1
    Now that I look at it closer, that code is all kinds of broken anyway. An `operator >` overload should return a `bool`. Your sort certainly expects that. As-written your sort will get a "true" state anytime the values don't match, whether the lhs is greater or lesser than the rhs. – WhozCraig Nov 16 '19 at 09:25
  • @user3386109: "all results are exact" - or junk due to integer overflow. – einpoklum Nov 16 '19 at 09:43
  • 1
    Junaid, just a bit of friendly advice. If you're going to change the question in a way that makes the answers incorrect in some way (such as fixing something that was mentioned as a problem), it's good community spirit to just leave a note on the answer stating so. That gives the answerer a chance to adjust their answer to suit. I've already done that re the `n2 = numerator / numerator` problem that you fixed so no big deal on this one, just something to keep in mind going forward. Cheers, and hope my answer helped. – paxdiablo Nov 17 '19 at 01:47
  • @paxdiablo thanks for the suggestion. I will follow it from now on. – Junaid Nov 18 '19 at 07:58

1 Answers1

3

The operator> method is supposed to return a boolean rather than an integer. It would be better written as something like:

bool operator> (const rational &r2) const {
    return (float)num / denom > (float)r2.num / r2.denom;
}

By way of example, here's a complete program that shows your current behaviour:

#include <iostream>

class rational {
    int num, denom;
public:
    void set(int n, int d) { num = n; denom = d; }
    int getNum() { return num; }
    int getDenom() { return denom; }

    int operator>(const rational &r2) const {
        float n1 = (float)num/(float)denom;
        float n2 = (float)r2.num/(float)r2.denom;

        if(n1>n2) return 1;
        if(n1==n2) return 0;
        if(n1<n2) return -1;
    }

};

void swap(rational &r1, rational &r2) {
    rational temp;
    temp.set(r1.getNum(),r1.getDenom());
    r1.set(r2.getNum(),r2.getDenom());
    r2.set(temp.getNum(),temp.getDenom());
}

void sort(rational arr[], int n) {
    int i, j;
    for (i = 0; i < n-1; i++)
        for (j = 0; j < n-i-1; j++)
            if (arr[j] > arr[j+1])
                swap(arr[j], arr[j+1]);
}

void displayArray(const char *desc, rational *arr, size_t sz) {
    std::cout << desc;
    for (size_t idx = 0; idx < sz; ++idx) {
        std::cout << "  " << arr[idx].getNum() << "/" << arr[idx].getDenom();
    }
    std::cout << '\n';
}

If you compile and run that, you'll see:

Before:  1/3  2/7  -1/4  3/11  5/8  1/2
 After:  1/2  5/8  3/11  -1/4  2/7  1/3

which is basically the same as your output. However, using my suggested comparison operator produces the output:

Before:  1/3  2/7  -1/4  3/11  5/8  1/2
 After:  -1/4  3/11  2/7  1/3  1/2  5/8

which is correctly sorted, albeit in the opposite order from your expected output. Since you stated in the question that "it can be in ascending or descending order", I assume that's not a problem.


You should also be aware that the range and/or precision of your rationals is likely to be different to standard floating point values.

If you have to revert to floats for comparison, I'm not sure you gain that much from having rationals in the first place.

Of course, it may be that it's okay to use floats just for sorting but rationals everywhere else. But it's something to keep in mind.

paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953
  • This will fail if the rational class has greater effective precision than `float`, and the input has many values which translate to the same float. Of course, that's a problem with OP's original approach, not your suggestion per se. – einpoklum Nov 16 '19 at 09:43
  • @einpoklum-reinstateMonica: good point, have adjusted the answer. And why is this Monica so damn good? You're the second bod I've seen today with that entreaty in your name :-) – paxdiablo Nov 16 '19 at 10:00
  • Monica Cellio is a vetern SE user and moderator, which was terminated for with no apparent cause, as part of a series of problematic actions by StackExchange Inc. See [here](https://meta.stackexchange.com/questions/333965/firing-mods-and-forced-relicensing-is-stack-exchange-still-interested-in-cooper) for the context. If you want to learn about Monica's undue termination specifically, there's [this audio interview](https://www.youtube.com/watch?v=tFiQPkdb5Qs). Fires are running pretty high on meta.stackexchange.com these days. – einpoklum Nov 16 '19 at 10:05
  • Interesting reads. I'm not *too* fussed by the licence change since my code is explicitly marked as usable for any purpose (see https://stackoverflow.com/users/14860/paxdiablo?tab=profile) but I can certainly see possible legal issues with only one party changing the contract. The lack of consultation thing is a little worrying but it is their site after all (they risk losing the community but they'll have to deal with that, it may be that they assume there's no better alternative). Maybe we'll end up with a GitHub/GitLab situation :-) – paxdiablo Nov 17 '19 at 01:43