Método de ordenamento: Qual usar? std::sort ou for aninhado? [RESOLVIDO]

1. Método de ordenamento: Qual usar? std::sort ou for aninhado? [RESOLVIDO]

rob
robgeek

(usa Debian)

Enviado em 09/02/2016 - 13:51h

Boa tarde!

Tenho uma matriz de inteiros com milhares de linhas, porém com 5 colunas cada linha. Cada linha precisa ser ordenada crescentemente. Então fiquei em dúvida sobre qual forma de fazer isso é mais eficiente. Se é com dois for aninhados para ordenar cada linha ou se deveria usar o std::sort.

A dúvida surgiu porque vi que preciso implementar outros métodos para poder usar std::sort a quantidade de implementação(leia-se linhas de código) é muito maior que usando dois for aninhados. Acredito que std::sort seja realmente mais eficiente se o vetor for bem grande, mas no meu caso ele só tem cinco posições, apesar de ter milhares de linhas. Bem é essa minha dúvida.

O que vocês acham?


  


2. MELHOR RESPOSTA

Paulo
paulo1205

(usa Ubuntu)

Enviado em 10/02/2016 - 13:55h

Seria interessante saber um pouco mais sobre a forma desses vetores de linhas. É um std::vector<std::vector<int>>, std::vector<std::array<int, 5>> ou um int(*)[5], ou alguma outra coisa?

De todo modo, ordenar as colunas de cada linha com std::sort() deveria funcionar com as alternativas que eu perguntei sem que você precise criar código.

#include <algorithm>
#include <array>
#include <vector>

using namespace std;

void f(){
const size_t rows=5000, cols=5;
vector<vector<int>> vv_mat(rows, vector<int>(cols));
vector<array<int, cols>> va_mat(rows);
array<array<int, cols>, rows> aa_mat;
int native_aa_mat[rows][cols];
int (*pa_mat)[cols]=new int[rows][cols];
int **pp_mat=new int *[rows];
for(size_t r=0; r<rows; r++) pp_mat[r]=new int[cols];

/* ... */

// Ordena a milésima linha de cada um dos tipos de matriz declarados acima
sort(vv_mat[999].begin(), vv_mat[999].end());
sort(va_mat[999].begin(), va_mat[999].end());
sort(aa_mat[999].begin(), aa_mat[999].end());
sort(native_aa_mat[999], native_aa_mat[999]+cols);
// também conhecido como “sort(&native_aa_mat[999][0], &native_aa_mat[999][cols])”, ou ainda
// “sort(native_aa_mat[999], native_aa_mat[999]+(sizeof *native_aa_mat/sizeof **native_aa_mat))”.
sort(pa_mat[999], pa_mat[999]+cols);
sort(pp_mat[999], pp_mat[999]+cols);

/* ... */

// Antes do fim da função, não se pode esquecer de desalocar quem precisa de desalocação.
for(size_t r=0; i<rows; r++)
delete[] pp_mat[r];
delete[] pp_mat;
delete[] pa_mat;
}


Se, contudo, o problema for ordenar as linhas da matriz, então você não poderá usar std::sort() diretamente sobre todos os tipos de dados acima, pois arrays nativos só podem ser destino da operação de atribuição no momento em que são declarados. Isso implica que std::sort() não pode ser usada para ordenar as linhas native_aa_mat e pa_mat. Já para as demais, você pode usar lambdas, funções ou objetos-função (functors), junto com a versão de std::sort() que recebe três argumentos.

// Comparador a primeira coluna, versão função.
// (Fiz como template para poder aplicar a qualquer dos tipos
// de matriz cujas linhas possam ser ordenadas.)
template <class linetype> bool cmp_1_col(const linetype &l1, const linetype &l2){
return l1[0]<l2[0];
}

// Comparador da primeira coluna, versão functor.
struct cmp_1_col_t {
template <class linetype> bool operator()(const linetype &l1, const linetype &l2){
return l1[0]<l2[0];
}
};

void f(){
const size_t rows=5000, cols=5;
vector<vector<int>> vv_mat(rows, vector<int>(cols));
vector<array<int, cols>> va_mat(rows);
array<array<int, cols>, rows> aa_mat;
int **pp_mat=new int *[rows];
for(size_t i=0; i<rows; i++) pp_mat[i]=new int[cols];

using vv_line_t=decltype(vv_mat)::value_type;
using va_line_t=decltype(va_mat)::value_type;
using aa_line_t=decltype(aa_mat)::value_type;
using pp_line_t=decltype(*pp_mat);

/* ... */

/*
Ordena linhas da matriz usando a função que compara apenas
valores na 1ª coluna.

Note que é preciso ser explícito com relação ao tipo dos argumentos
(tipo de cada linha) porque estamos passando um ponteiro de
função; se fosse uma chamada direta (por exemplo:
“cmp_1_col(vv_mat[0], vv_mat[1])”), o compilador conseguiria
identificar o tipo dos argumentos e instanciar a função correta.
*/
sort(vv_mat.begin(), vv_mat.end(), cmp_1_col<vv_line_t>);
sort(va_mat.begin(), va_mat.end(), cmp_1_col<va_line_t>);
sort(aa_mat.begin(), aa_mat.end(), cmp_1_col<aa_line_t>);
sort(pp_mat, pp_mat+rows, cmp_1_col<pp_line_t>);

/*
Mesma coisa, mas usando functors.

Note que, agora, não é preciso dizer o tipo de cada linha pois,
como não há ponteiros envolvidos, o compilador consegue
saber os tipos dos argumentos. Por outro lado, note que
estamos criando objetos temporários distintos em cada chamada.
Se fosse um objeto mais complexo, isso envolveria uma chamada
de construtor no início e uma de destrutor para cada operação.
Além disso, mesmo que você tivesse duas matrizes do mesmo tipo,
ainda teria objetos distintos, e o compilador teria de ser bem
esperto também para não instanciar duas vezes a função que
faz a comparação para o mesmo tipo de argumento.
*/
sort(vv_mat.begin(), vv_mat.end(), cmp_1_col_t());
sort(va_mat.begin(), va_mat.end(), cmp_1_col_t());
sort(aa_mat.begin(), aa_mat.end(), cmp_1_col_t());
sort(pp_mat, pp_mat+rows, cmp_1_col_t());

/*
Functors novamente, mas com um objeto só.

Note que, apesar de ser um só objeto, a função de comparação
será instanciada de modo diferenciado para cada tipo de matriz.
Por outro lado, podemos ter certeza de que não haverá mais de
uma instância da função de comparação para o mesmo tipo de
linhas comparadas. Além disso, todas as operações de ordenação
são feitas com apenas uma chamada ao construtor e uma ao
destrutor deste único objeto.
*/
cmp_1_col first_col_comparator;
sort(vv_mat.begin(), vv_mat.end(), first_col_comparator);
sort(va_mat.begin(), va_mat.end(), first_col_comparator);
sort(aa_mat.begin(), aa_mat.end(), first_col_comparator);
sort(pp_mat, pp_mat+rows, first_col_comparator);

/*
Finalmente, a versão usando lambdas.

Pessoalmente, só gosto de lambdas quando as funções são
bem curtas. É o caso aqui. Por outro lado, eles acabam
deixando a coisa meio poluída visualmente, na minha opinião,
e eu acabei quebrando as linhas para que eu mesmo tivesse
clareza do que estava fazendo.

Note que não existe mágica. O compilador converte lambdas em
functors anônimos, que têm uma função operator()() com os
mesmos argumentos que você tiver usado para o lambda.

Note ainda que lambdas não permitem o uso de templates.
*/
sort(
vv_mat.begin(), vv_mat.end(),
[](const vv_line_t &a, const vv_line_t &b){ return a[0]<b[0]; }
);
sort(
va_mat.begin(), va_mat.end(),
[](const va_line_t &a, const va_line_t &b){ return a[0]<b[0]; }
);
sort(
aa_mat.begin(), aa_mat.end(),
[](const aa_line_t &a, const aa_line_t &b){ return a[0]<b[0]; }
);
sort(
pp_mat, pp_mat+rows,
[](const pp_line_t &a, const pp_line_t &b){ return a[0]<b[0]; }
);

/*
Lambdas ainda.

Como lambdas criam functors anônimos, é possível associá-los
a objetos específicos, que poderiam ser reaproveitados para
ordenar objetos do mesmo tipo.
*/
auto vv_1st_col_lamb_cmp=
[](const vv_line_t &a, const vv_line_t &b){ return a[0]<b[0]; }
;
stable_sort(vv_mat.begin(), vv_mat.end(), vv_1st_col_lamb_cmp);

/* ... */
}


3. Re: Método de ordenamento: Qual usar? std::sort ou for aninhado? [RESOLVIDO]

rob
robgeek

(usa Debian)

Enviado em 10/02/2016 - 14:35h

Seria um std::vector<std::vector<int>>.

Para ser mais honesto, na verdade estou programando usando o Framework Qt, então, na verdade é QVector<QVector<int>>. Só que me aconselharam a usar o std::sort para fazer este ordenamento ao invés de dois for aninhados para cada linha da matriz.

Impressionante suas respostas, senhor paulo1205! Muito obrigado!






Patrocínio

Site hospedado pelo provedor RedeHost.
Linux banner

Destaques

Artigos

Dicas

Tópicos

Top 10 do mês

Scripts