Tenemos una serie de elementos que el usuario final podrá organizar en el orden deseado. El conjunto de elementos no está ordenado, pero cada elemento contiene una clave de clasificación que se puede modificar.
Estamos buscando un algoritmo que permita generar una nueva clave de clasificación para un elemento que se agrega o mueve para ser el primer elemento, el último elemento o entre dos elementos. Esperamos tener que modificar la clave de clasificación del elemento que se está moviendo.
Un algoritmo de ejemplo sería hacer que cada clave de clasificación sea un número de coma flotante, y cuando coloque un elemento entre dos elementos, configure la clave de clasificación como su promedio. Colocar un elemento primero o último tomaría el valor más externo + - 1.
El problema aquí es que la precisión de coma flotante puede hacer que falle la clasificación. El uso de dos enteros para representar un número fraccionario podría hacer que los números se vuelvan tan grandes que no se puedan representar con precisión en tipos numéricos regulares (por ejemplo, cuando se transfieren como JSON). No nos gustaría usar BigInts.
¿Existe un algoritmo adecuado para esto que funcione, por ejemplo, usando cadenas, que no se verían afectadas por estas deficiencias?
No buscamos admitir grandes cantidades de movimientos, pero el algoritmo descrito anteriormente podría fallar en un número de coma flotante de doble precisión después de aproximadamente 50 movimientos.
fuente
A, B, C
-A, AA, B, C
-A, AA, AB, B, C
-A, AA, AAA, AAB, AAC, AB, AC, B, C
. Por supuesto, es probable que desee espaciar más sus letras para que las cadenas no crezcan tan rápido, pero se puede hacer.Respuestas:
Como resumen de todos los comentarios y respuestas:
TL; DR : el uso de números de coma flotante de doble precisión con el algoritmo propuesto inicialmente debería ser suficiente para las necesidades más prácticas (al menos ordenadas manualmente). También se debe considerar mantener una lista ordenada separada de los elementos. Otras soluciones clave de clasificación son algo engorrosas.
Las dos operaciones problemáticas son insertar elementos al principio / final una y otra vez, e insertar o mover elementos repetidamente al mismo lugar (por ejemplo, con tres elementos moviendo repetidamente el tercer elemento entre los dos primeros, o agregando elementos nuevos repetidamente como el segundo elemento).
Desde un punto de vista teórico (es decir, permitiendo una reordenación infinita), la única solución que se me ocurre es usar dos enteros de tamaño ilimitado como a / b fraccional. Esto permite una precisión infinita para las inserciones medias, pero los números pueden ser cada vez más grandes.
Las cadenas pueden ser compatibles con una gran cantidad de actualizaciones (aunque todavía tengo problemas para descifrar el algoritmo para ambas operaciones), pero no infinito, porque no se pueden agregar infinitamente muchas en la primera posición (al menos usando una ordenación de cadena normal) comparación).
Los enteros requerirían elegir un espacio inicial para las teclas de clasificación, lo que limita la cantidad de inserciones medias que puede realizar. Si inicialmente separa las claves de clasificación 1024, solo puede realizar 10 inserciones medias en el peor de los casos antes de tener números adyacentes. Elegir un espacio inicial más grande limita la cantidad de inserciones primeras / últimas que puede realizar. Usando un número entero de 64 bits, está limitado a ~ 63 operaciones de cualquier manera, que necesita dividir entre las inserciones intermedias y las inserciones primera / última a priori.
El uso de valores de punto flotante elimina la necesidad de seleccionar el espaciado a priori. El algoritmo es simple:
El uso de un flotador de doble precisión permite 52 insertos intermedios en el peor de los casos y, de manera efectiva, infinitos (aproximadamente 1e15) primeros / últimos insertos. En la práctica, al mover elementos alrededor del algoritmo debe autocorregirse, ya que cada vez que mueve un elemento primero o último, se amplía el rango que se puede utilizar.
Los flotadores de doble precisión también tienen la ventaja de que son compatibles con todas las plataformas y se almacenan y transportan fácilmente en prácticamente todos los formatos y bibliotecas de transporte. Esto fue lo que terminamos usando.
fuente
Escribí una solución en TypeScript basada en el resumen de @ Sampo. El código se puede encontrar a continuación.
Un par de ideas adquiridas en el camino.
Solo la inserción en el medio entre dos claves de clasificación existentes debe generar una nueva clave de clasificación, el intercambio (es decir, la reorganización) no causa divisiones (es decir, nuevos puntos medios). Si mueve dos elementos y solo toca uno de ellos, está perdiendo información sobre qué dos elementos cambiaron de posición en la lista. Incluso si para empezar era un requisito, tenga en cuenta que es una buena idea.
Cada 1074: división del punto medio necesitamos normalizar el rango de coma flotante. Detectamos esto simplemente verificando si el nuevo punto medio satisface el invariante
El escalado no importa, ya que las teclas de clasificación están normalizadas, la normalización todavía ocurre cada
1074
división de punto medio. La situación no mejoraría si distribuimos los números más ampliamente para empezar.La normalización de la clave de clasificación es increíblemente rara. Usted amortizará este costo hasta el punto en que la normalización no sea notable. Sin embargo, sería cuidadoso con este enfoque si tiene más de 1000 elementos.
fuente
He estado allí, hecho eso, puede que tenga que hacer eso de nuevo. Use una cadena como clave de clasificación, siempre puede encontrar una clave que esté entre dos claves dadas. Si las cadenas se alargan demasiado para su gusto, debe modificar varias o todas las claves de clasificación.
fuente
Use números enteros y configure la clave de clasificación para la lista inicial en 500 * número de artículo. Al insertar entre elementos, puede usar el promedio. Esto permitirá muchas inserciones para comenzar
fuente