こういうのはしばらく私を悩ませたので、それが解決するかどうか見に行かなければなりませんでした。これが私の考えです。最初から、私が知っているアルゴリズムのアプリケーションではありません。これはかなり高価なブルートフォースアルゴリズムになりますが、かなり効果的です。これは、あなたが記述した現実的に小さなデータセット(4行100列)を処理し、十分なRAMを備えた最新のコンピューターで作業していることを前提としています。
概要:並べ替えられたリストで再帰アルゴリズムを使用して、類似レコードを類似レコード内の最大距離に分散します。各呼び出しの後、同じ親を持つすべてのレコードは最大距離にあります。最初の呼び出しにはすべてのレコードが含まれます。そのため、裏返しに並べ替えられます。
データ構造:
newIndexes
ですarray<integer>
。配列のインデックスは、行の既存のインデックスです。値は-1で始まる新しいインデックスになります
data
ですarray<array<string>>
。キーはインデックスで、内部配列は1行の値の文字列表現です。データをグループ化する方法がある場合は、文字列である必要はありません。最初の配列要素は、最大の重みを持つものです。
data
重量順に並べ替えます。最初に、重みが最大の列で並べ替え、次に重みが2番目に大きい列で並べ替えます。結果は、希望とは逆になります。順次インデックスを付けます。
これは、アルゴリズム(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";
}
次に、ソートされていないデータにnewIndexesを適用します。
アプローチについての考え:これはテストしませんでしたが、最初のインデックスは最下位の列に基づいて割り当てられるため、newIndexesの保存と競合の解決は問題になる可能性があります。最初にオフセットを正に適用してから、負に適用してみてください。または、配列の代わりにリンクリストに挿入することもできます。