Isso meio que me incomodou por um tempo, então eu tive que vir ver se estava resolvido. Aqui está a minha ideia. Do zero, não é uma aplicação de nenhum algoritmo que eu conheço. Esse seria um algoritmo de força bruta bastante caro, mas deveria ser bastante eficaz. Supõe-se que você esteja lidando com o conjunto de dados realmente pequeno que você descreveu (100 linhas de 4 colunas) e esteja trabalhando em computadores modernos com memória RAM suficiente.
Visão geral : usamos um algoritmo recursivo em uma lista classificada para dispersar registros semelhantes à sua distância máxima dentro de registros semelhantes. Após cada chamada, todos os registros com o mesmo pai estão em sua distância máxima. A chamada superior inclui todos os registros. Por isso, desagrega de dentro para fora.
Estruturas de dados :
newIndexes
é um array<integer>
. O índice da matriz é o índice existente da linha. O valor será o novo índice, começa com -1
data
é um array<array<string>>
. A chave é o índice, a matriz interna é uma representação de string dos valores em uma linha. Não precisa ser uma sequência se você tiver alguma maneira de agrupar seus dados. O primeiro elemento da matriz é aquele com o maior peso.
Classifique data
por ordem de peso. Classifique-o primeiro pela coluna com maior peso, dentro da coluna com o segundo maior peso, etc. O resultado é o inverso do que você deseja. Índice sequencialmente.
Aqui está o algoritmo (no código psudo).
// 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";
}
Em seguida, aplique os newIndexes aos dados a serem não classificados.
Considerações sobre a abordagem: não testamos isso, mas o armazenamento dos novos Índices e a resolução de conflitos podem ser problemáticos, pois os primeiros índices são atribuídos com base em colunas menos significativas; portanto, se houver muitos conflitos, as colunas mais significativas poderão se agrupar. Pode-se tentar aplicar o deslocamento como positivo primeiro e depois negativo. Ou, possivelmente, faça esse tipo de inserção em uma lista vinculada, em vez de em uma matriz.