2

I'm studying C++ and I had the task to create array[n][m], to fill it with integer numbers, then
"Characteristic of matrix rows is called the sum of its positive even elements. You need to sort the rows of the matrix in accordance with the growth of characteristics."

It's my code

#include "stdafx.h"
using namespace std;

int _tmain(int argc, _TCHAR* argv[])
{
srand((unsigned)time(NULL));

int n, m;
cout << "n = ";
cin >> n;
cout << "m = ";
cin >> m;

int ** mas = new int * [n];
for (int i = 0; i < n; ++i)
{
    mas[i] = new int[m];
}

cout << "Array:\n";
for (int i = 0; i < n; ++i)
{
    for (int j = 0; j < m; ++j)
    {
        mas[i][j] = rand()%41-20;
        cout << mas[i][j] << "\t";
    }
    cout << "\n";
}

double * characteristic = new double[n];
for (int i = 0; i < n; ++i)
{
    characteristic[i] = 0;
}

for (int i = 0; i < n; ++i)
{
    for (int j = 0; j < m; ++j)
    {
        if((j%2 == 0) && (mas[i][j] >= 0)) 
        {
                characteristic[i] += mas[i][j];
        }
    }
}

cout << "Characteristics:\n";
for (int i = 0; i < n; ++i)
{
    cout << characteristic[i] << " ";
}
cout << "\n";

for (int i = 0; i < n - 1; ++i)
{
    int min = i;
    for (int j = i + 1; j < n; ++j)
    {
        if (characteristic[min] <= characteristic[j]) continue;
        min = j;
    }
    if (min != i)
    {
        double temp = characteristic[i];
        characteristic[i] = characteristic[min];
        characteristic[min] = temp;

        for (int k = 0; k < m; ++k)
        {
            int temp1 = mas[i][k];
            mas[i][k] = mas[min][k];
            mas[min][k] = temp1;
        }

    }
}

cout << "\nSorted characteristics:\n";
for (int i = 0; i < n; ++i)
{
    cout << characteristic[i] << " ";
}
cout << "\n";

cout << "Sorted array:\n";
for (int i = 0; i < n; ++i)
{
    for (int j = 0; j < m; ++j)
    {
        cout << mas[i][j] << "\t";
    }
    cout << "\n";
}

for (int i = 0; i < n; ++i)
{
    delete [] mas[i];
}
delete [] mas;

delete [] characteristic;

system("PAUSE");
return 0;
}

I created another one array for characteristics and sorted it and the first array at the same time, but it seems I used too difficult way to accomplish a given task. Maybe are there other ways?

Heidel
  • 3,174
  • 16
  • 52
  • 84
  • 2
    try std::vector of std::vectors and then std::sort using a custom predicate, then it'll look like c++ – Dmitry Ledentsov Oct 11 '13 at 06:38
  • Why do you use `double` for characteristic, if the matrix only contains `int`s? – SingerOfTheFall Oct 11 '13 at 06:38
  • The worst thing about your code is that you use an `n^2` sorting algorithm. This should be useful for how to use `std::sort`: http://stackoverflow.com/questions/4523220/sorting-a-vector-of-double-precision-reals-and-obtain-their-order – Adam Oct 11 '13 at 06:46
  • 1
    If you are free to choose your own matrix structure then instead of a 2D array use what Dmetry suggests, or an array of pointers to 1D arrays. That way you can swap entire rows by simply swapping pointers (or `vector.swap()`). That takes your row swaps from `O(n^2)` to `O(n)`. – Adam Oct 11 '13 at 06:49
  • Are they expecting you to write your own sorting algorithm? – sarat Oct 11 '13 at 06:59
  • @Adam can you explain me please how to create an array of pointers and sort it? – Heidel Oct 11 '13 at 07:03
  • @Dmitry Ledentsov I have not studied `std::vector` of `std::vectors` and `std::sort` yet. – Heidel Oct 11 '13 at 07:05
  • @Adam: you seem to assume you can sort a 2D asymptotically better than `std::sort` can do with a vector of vectors. Be rest assured that this assumption is wrong and arrogant. The C++ standard library is created by some of the very best programmers and computer scientists of this planet. If you think you can outdo them single-handedly, think again. – n. m. could be an AI Oct 11 '13 at 07:07
  • @Heidel: you should start with studying these things and not with the builtin arrays if you have any choice. – n. m. could be an AI Oct 11 '13 at 07:13
  • @n.m. Where do I assume that? – Adam Oct 11 '13 at 07:18
  • @Adam I agree, minimizing copies is unrelated to what algorithm is used. `std::sort` still uses the chosen data structure and indeed, copying can hurt. Now, it's worth checking whether `vector(vector&&)` optimizes this case (I don't think so becuase the move constructor isn't `nothrow`) – sehe Oct 11 '13 at 07:25
  • @n.m. I suggested to use `std::sort`! Chill out. `std::array` has only been available since TR1, and it's unclear what c++ OP needs/has. It also doesn't claim anything about performing better than C arrays, it's there to make arrays that act like other containers (type safety, iterators, etc). I don't see your violent opposition to using something OP is familiar with. Clearly he's learning. – Adam Oct 11 '13 at 07:44
  • @n.m. Besides, I suggested using `std::vector` by referencing Dmitry. Why else would I mention `vector.swap()`? – Adam Oct 11 '13 at 07:45
  • 2
    @n.m. Actually, I'm going to call you out on the `std::array` thing: `n` and `m` are dynamic. YOU tell me how to make a dynamically sized `std::array`. – Adam Oct 11 '13 at 07:47
  • 1
    @Adam again, agreed :) My answer would have been using `std::array<>` if it weren't for that – sehe Oct 11 '13 at 07:53
  • What kind of a course allows `#include `, `_tmain` and `_TCHAR`? And if you're dealing with matrices, the first thing to do is to create a matrix class, and use it. (And of course, if the course presents things like `double [][]` before `std::vector`, it should be avoided like the plague.) – James Kanze Oct 11 '13 at 08:12
  • Note that this is extremely tricky if you have to use C style arrays for the matrix, because the rows (which you will need to swap) are C style arrays, which don't have normal copy semantics. – James Kanze Oct 11 '13 at 08:14
  • Note that to do this efficiently is fairly advanced C++. You don't want to have to recalculate the characteristic for each row, so you'll need some sort of intermediate data structure. I'd probably use something along the lines of `struct Row { double characteristic; double const* row; }`, put that in a `std::vector`, sort the `vector`, and then rearrange the actual data afterwards. – James Kanze Oct 11 '13 at 08:18
  • @Adam sorry I have misread your comments, made wwrong conclusions, and I apologize for that. I will now delete my comments. – n. m. could be an AI Oct 11 '13 at 08:42
  • don't forget there are other benefits of using standard algorithms, i.e. you can easily replace them with parallel versions, for example: [gcc](http://gcc.gnu.org/onlinedocs/libstdc++/manual/parallel_mode_using.html#parallel_mode.using.parallel_mode), [tbb](http://software.intel.com/en-us/blogs/2011/06/03/generic-parallel-algorithms-for-intel-tbb-theyre-already-in-there-part-2), [ppl](http://blogs.msdn.com/b/nativeconcurrency/archive/2011/01/15/sorting-in-ppl.aspx). You might want to sort [on the gpu](https://code.google.com/p/thrust/) some day too... – Dmitry Ledentsov Oct 11 '13 at 11:14

3 Answers3

3

Did you want to sort the matrix too, using the same ordering as the 'characteristic's?

Let's say you had C++ style code to calculate the characteristics:

std::vector<double> characteristic(n, 0.0);
std::transform(begin(mas), end(mas), begin(characteristic), sum_);

You could then sort them:

std::sort(begin(characteristic), end(characteristic));

Or you could, indeed sort the matrix immediately:

std::sort(begin(mas), end(mas), [&sum_](int_vec const& a, int_vec const& b) 
         { return sum_(a)<sum_(b); });

Edit Fixed all versions to use the correct "characteristic sum" (kept the name though), thanks @Adam

Here's a full program that demonstrates this: See it Live on Coliru

#include <random>
#include <iostream>
#include <string>
#include <vector>

#include <cstdlib>
#include <algorithm>
#include <iterator>

using namespace std;

int main()
{
    typedef std::vector<int> int_vec;

    srand((unsigned)time(NULL));
    int n, m;
    cout << "n = ";
    cin  >> n;
    cout << "m = ";
    cin  >> m;

    std::vector<int_vec> mas(n, int_vec(m));

    for (auto& v : mas)
        std::for_each(begin(v), end(v), [](int& i) { i = rand()%41-20; });

    cout << "Array:\n";
    for (auto const& v : mas)
    {
        std::copy(begin(v), end(v), ostream_iterator<int>(cout, "\t"));
        cout << "\n";
    }

    auto sum_ = [m](int_vec const& v) { 
        double vchar = 0;
        for (auto j = 0; j < m; j+=2)
            if(v[j] >= 0) vchar += v[j];
        return vchar;
    };

    std::vector<double> characteristic(n, 0.0);
    std::transform(begin(mas), end(mas), begin(characteristic), sum_);

    cout << "Characteristics:\n";
    std::copy(begin(characteristic), end(characteristic), ostream_iterator<double>(cout, " "));
    cout << "\n";

    std::sort(begin(characteristic), end(characteristic));

    cout << "\nSorted characteristics:\n";
    std::copy(begin(characteristic), end(characteristic), ostream_iterator<double>(cout, " "));
    cout << "\n";

    std::sort(begin(mas), end(mas), [&sum_](int_vec const& a, int_vec const& b) { return sum_(a)<sum_(b); });

    cout << "Sorted Array:\n";
    for (auto const& v : mas)
    {
        std::copy(begin(v), end(v), ostream_iterator<int>(cout, "\t"));
        cout << "\n";
    }

}

Sample output:

n = m = Array:
11  15  19  18  
-20 -16 2   -11 
8   2   19  8   
Characteristics:
30 2 27 

Sorted characteristics:
2 27 30 
Sorted Array:
-20 -16 2   -11 
8   2   19  8   
11  15  19  18  
sehe
  • 374,641
  • 47
  • 450
  • 633
  • 1
    Using [c++11's `` header](http://en.cppreference.com/w/cpp/numeric/random) is left as an exercise to the reader for now. Do read [How do I scale down numbers from rand()?](http://stackoverflow.com/a/4196775/85371) and [`rand()` Considered Harmful](http://channel9.msdn.com/Events/GoingNative/2013/rand-Considered-Harmful) by Stephan Lavavej, though. – sehe Oct 11 '13 at 07:11
  • thanks a lot for your explanation, but I have not studied `vector` yet. – Heidel Oct 11 '13 at 08:23
  • @sehe am I missing something or does your `mas` sort recompute the characteristics for every comparison? Also it seems `sum_` makes the characteristic the sum of *all* elements, not just the *positive even* ones. – Adam Oct 11 '13 at 09:19
  • @Adam you're right on both. I never even noticed the check for positive even ones. Oh and now I see: you meant positive ones _at even indices_ :). Will fix – sehe Oct 11 '13 at 09:31
  • do you know the C++ish way to fix the former? All I can think of is to sort the characteristics vector while keeping track of the permutation, then permute `mas` using the same permutation. It's what I do in my own codes but it's messy and I couldn't find a standard permutation function. – Adam Oct 11 '13 at 09:37
  • Here's a version without vector (using a custom `my_array` to avoid having to do manual memory management): http://coliru.stacked-crooked.com/a/1842b65464940390 and here's a version with raw C-arrays only: http://coliru.stacked-crooked.com/a/8c5679b14b84df34 – sehe Oct 11 '13 at 09:39
  • @Adam I've fixed all versions now with regards to correctness. I'm not really inclined to start optimizing (premature optimization anyone?) but of course you could use some kind of precalculated index, or you could avoid sorting the matrix and just traverse it in index order. It all depends on the real use case :) – sehe Oct 11 '13 at 09:45
  • @Adam As to permutation functions, they exist. But you're probably looking for [Boost's `permutation_iterator`](http://www.boost.org/doc/libs/1_54_0/libs/iterator/doc/permutation_iterator.html) or MultiIndex library? – sehe Oct 11 '13 at 09:47
  • `permutation_iterator` is close, but it only gives a permuted view. It doesn't permute the data. Anyway, that's off topic I guess. – Adam Oct 11 '13 at 09:52
1

@sehe gives you great advice, but I suspect a lot of that stuff won't make sense until you know more C++.

Here's a simple improvement to eliminate a slow loop:

When doing your row swaps swap the row pointers instead of copying every value that they point to. Replace this:

    for (int k = 0; k < m; ++k)
    {
        int temp1 = mas[i][k];
        mas[i][k] = mas[min][k];
        mas[min][k] = temp1;
    }

With:

    int* temp1 = mas[i];
    mas[i] = mas[min];
    mas[min] = temp1;

If you can figure out how to use a built-in sort algorithm that would be another improvement on top of this, but even this small change will gain you a lot.

Adam
  • 16,808
  • 7
  • 52
  • 98
  • 2
    @Heidel no problem. After your final exams come back to look at sehe's code. It will make you a better C++ programmer. It also highlights some differences between C and C++ and why they really are different languages that only resemble each other. – Adam Oct 11 '13 at 09:41
0

Since the sizes n,m are known in compile time, you can use the qsort function from the C library.

#include <stdlib.h>

void qsort(void *base, size_t nmemb, size_t size,
           int (*compar)(const void *, const void *));

Where is compar is a function you write, which should treat both its arguments as pointers to a row of the matrix. Then it can calculate the characteristic of both rows, and return -1, 0 or 1 depending on which row's characteristic is greater.

SzG
  • 12,333
  • 4
  • 28
  • 41
  • 1
    Honestly? qsort is C and not type safe. I'd recommend std::sort. – cdoubleplusgood Oct 11 '13 at 07:02
  • @cdoubleplusgood See my answer (although this is likely homework, and my answer is going to be of little use unless the teacher is surprisingly open-minded about using post-1990 C++ in class :)) – sehe Oct 11 '13 at 07:08
  • @SzG `n` and `m` are not known at compile time. Obviously you can still write a comparator function, but I think you'd have to resort to global variables. `std::sort` doesn't have that problem, you can use a comparator object instead. – Adam Oct 11 '13 at 09:23
  • @cdou Yes I know C is evil, not type-safe and totally incompatible with C++. That's why e.g the Linux kernel is using C++. Oh wait... – SzG Oct 11 '13 at 12:58
  • @SzG: The question is about C++, not C. – cdoubleplusgood Oct 11 '13 at 13:07
  • @cdou Honestly? Introduce the concept of templates and iterators just to sort something with a known size and type? std::sort is not even able to differentiate between lower/equal/greater like the evil qsort(). Give me a break... – SzG Oct 11 '13 at 13:09