Esto me molestó por un tiempo, así que tuve que ir a ver si estaba resuelto. Aquí está mi idea. Desde cero, no es una aplicación de ningún algoritmo que yo sepa. Este sería un algoritmo de fuerza bruta bastante costoso, pero debería ser bastante efectivo. Se supone que está tratando con el conjunto de datos realmente pequeño que describió (100 filas de 4 columnas) y está trabajando en una computadora moderna con suficiente ram.
Descripción general : Utilizamos un algoritmo recursivo en una lista ordenada para dispersar registros similares a su distancia máxima dentro de registros similares. Después de cada llamada, todos los registros con el mismo padre están a su máxima distancia. La llamada superior incluye todos los registros. Por lo tanto, se clasifica de adentro hacia afuera.
Estructuras de datos :
newIndexes
es un array<integer>
. El índice de la matriz es el índice existente de la fila. El valor será el nuevo índice, comienza con -1
data
es un array<array<string>>
. La clave es el índice, la matriz interna es una representación de cadena de los valores en una fila. No necesita ser una cadena si tiene alguna forma de agrupar sus datos. El primer elemento de matriz es el que tiene el mayor peso.
Ordenar data
por orden de peso. Ordénelo primero por la columna con mayor peso, dentro de eso por columna con el segundo mayor peso, etc. El resultado es el inverso de lo que desea. Indice secuencialmente.
Aquí está el algoritmo (en psudocódigo).
// siblingCount: On first call is the number of rows in the table,
// on recursive calls it is the number of elements with the same parent
// index: the index of current row in `data` - starts 0
// depth: The element index - starts 0
void unsort(int siblingCount, int index, int depth)
{
int count = 1;
string hash = concatColumns(index, depth + 1);
while ((index + count < data.count) && (hash == concatColumns(index + count, depth + 1)))
{
count++;
}
if (depth < columnCount)
unsort(count, index, depth);
else if (index < data.count)
unsort(count, index + count, 0);
int spacing = siblingCount / count;
for (int i = 0; i < count; i++)
{
var offset = 0;
while ((newIndexes[index + i + offset] > -1) & (index + i + offset + 1 < newIndexes.count))
offset++;
if (newIndexes[index + i + offset] > -1) throw new Exception("Shouldn't happen.");
newIndexes[index + i + offset] = index + spacing * i;
}
}
string concatColumns(int index, int count) // returns count columns concatinated
{
// 1,1 = "1"
// 1,2 = "1, blue"
// 1,3 = "1, blue, apple"
return "1, blue, apple";
}
Luego aplique newIndexes a los datos que se van a ordenar.
Reflexiones sobre el enfoque: no probé esto, pero el almacenamiento de los nuevos índices y la resolución de conflictos pueden ser problemáticos ya que los primeros índices se asignan en función de las columnas menos significativas, por lo que si hay muchos conflictos, las columnas significativas más grandes pueden agruparse. Podría intentar aplicar el desplazamiento como positivo primero, luego negativo. O posiblemente haga una especie de inserción en una lista vinculada en lugar de una matriz.