He escrito algunos métodos de consulta K-vecino más cercano que crean una lista de puntos que están más cerca de un punto de consulta determinado. Para mantener esa lista de vecinos, utilizo el std::priority_queue
de modo que el elemento superior sea el vecino más alejado del punto de consulta. De esta forma sé si debo presionar el nuevo elemento que se está examinando actualmente (si está a una distancia menor que el vecino más alejado actual) y puedo hacer estallar() el elemento más lejano cuando mi prioridad-cola tiene más de K elementos.Obteniendo los elementos `std :: priority_queue` en orden inverso?
Hasta ahora, todo está bien. Sin embargo, cuando publico los elementos, me gustaría ordenarlos desde el más cercano al más lejano. Actualmente, simplemente aparezco todos los elementos de la cola de prioridad y los pongo en el contenedor de salida (a través de un iterador), lo que da como resultado una secuencia de puntos ordenados desde el más lejano al más cercano, entonces llamo al std::reverse
en el iterador de salida distancia.
Como un simple ejemplo, aquí es una búsqueda lineal que utiliza la prioridad en colas (obviamente, los métodos de consulta del vecino más cercano reales que uso son mucho más complicadas):
template <typename DistanceValue,
typename ForwardIterator,
typename OutputIterator,
typename GetDistanceFunction,
typename CompareFunction>
inline
OutputIterator min_dist_linear_search(ForwardIterator first,
ForwardIterator last,
OutputIterator output_first,
GetDistanceFunction distance,
CompareFunction compare,
std::size_t max_neighbors = 1,
DistanceValue radius = std::numeric_limits<DistanceValue>::infinity()) {
if(first == last)
return output_first;
typedef std::priority_queue< std::pair<DistanceValue, ForwardIterator>,
std::vector< std::pair<DistanceValue, ForwardIterator> >,
detail::compare_pair_first<DistanceValue, ForwardIterator, CompareFunction> > PriorityQueue;
PriorityQueue output_queue = PriorityQueue(detail::compare_pair_first<DistanceValue, ForwardIterator, CompareFunction>(compare));
for(; first != last; ++first) {
DistanceValue d = distance(*first);
if(!compare(d, radius))
continue;
output_queue.push(std::pair<DistanceValue, ForwardIterator>(d, first));
while(output_queue.size() > max_neighbors)
output_queue.pop();
if(output_queue.size() == max_neighbors)
radius = output_queue.top().first;
};
OutputIterator it = output_first;
while(!output_queue.empty()) {
*it = *(output_queue.top().second);
output_queue.pop(); ++it;
};
std::reverse(output_first, it);
return it;
};
Lo anterior es todo Dandy, excepto por una cosa: requiere que el tipo de iterador de salida sea bidireccional y que esencialmente apunte a un contenedor preasignado. Ahora, esta práctica de almacenar la salida en un rango prescrito por algún iterador de salida es excelente y bastante estándar también (por ejemplo, std::copy
y otros algoritmos de STL son buenos ejemplos de eso). Sin embargo, en este caso, me gustaría poder solicitar solo un tipo de iterador de salida hacia adelante, lo que permitiría utilizar iteradores de inserción inversa como los provistos para contenedores STL y iostreams.
Por lo tanto, esto se reduce a revertir la cola de prioridad antes de volcar su contenido en el iterador de salida. Por lo tanto, éstas son las mejores opciones que he podido llegar a:
Crear un
std::vector
, volcar el contenido de prioridades de colas en ella, y volcar los elementos en la salida-iterador utilizando una inversa iterador en el vector.Reemplace el
std::priority_queue
con un contenedor clasificado (por ejemplo,std::multimap
), y luego volcar el contenido en el iterador de salida utilizando el orden transversal adecuado.
¿Hay alguna otra opción razonable?
Solía emplear un std::multimap
en una implementación previa de este algoritmo y otros, a partir de mi segunda opción anterior. Sin embargo, cuando cambié al std::priority_queue
, la ganancia de rendimiento fue significativa. Por lo tanto, prefiero no utilizar la segunda opción, ya que parece que utilizar una cola de prioridad para mantener la lista de vecinos es mucho mejor que confiar en una matriz ordenada. Por cierto, también probé un std::vector
que mantengo ordenado con std::inplace_merge
, que era mejor que multimap, pero no coincidía con la cola de prioridad.
En cuanto a la primera opción, que es mi mejor opción en este momento, me parece un desperdicio tener que hacer esta doble transferencia de datos (cola -> vector -> salida). Me inclino a pensar que debe haber una manera más simple de hacer esto ... algo que me falta ...
La primera opción realmente no es tan mala en esta aplicación (considerando la complejidad del algoritmo que lo precede), pero si hay un truco para evitar esta doble transferencia de memoria, me gustaría saberlo.
sucio de hecho .. Después de comprobar la documentación estándar, parece que el 'std :: priority_queue' se garantiza que sea de hecho almacenado de forma compacta (sin agujeros, como producirían muchas implementaciones de b-tree). Y, usando un 'std :: vector' por supuesto, el almacenamiento es contiguo. Entonces, supongo que tienes razón, este truco "sucio" estará garantizado para funcionar. Y no me importa romper la cola, no la necesito después de eso. –