Ich bin auf diese Frage gestoßen, als ich ein ähnliches Problem untersucht habe: optimale Flüssigkeitszugaben zur Reduzierung der Schichtung. Meine Lösung scheint auch auf Ihre Situation anwendbar zu sein.
Wenn Sie die Flüssigkeiten A, B und C im Verhältnis 30,20,10 (dh 30 Einheiten A, 20 Einheiten B und 10 Einheiten C) mischen möchten, erhalten Sie eine Schichtung, wenn Sie alle addieren das A, dann das ganze B und dann das ganze C. Es ist besser, kleinere Einheiten zu mischen. Fügen Sie beispielsweise einzelne Einheiten in der Reihenfolge [A, B, A, C, B, A] hinzu. Das wird eine Schichtung insgesamt verhindern.
Ich habe es als eine Art Zusammenführung behandelt, die eine Prioritätswarteschlange verwendet. Wenn ich eine Struktur zur Beschreibung der Ergänzungen erstelle:
MergeItem
Item, Count, Frequency, Priority
Die Frequenz wird als "Eins alle N" ausgedrückt. Also hat A, das von sechs zu drei addiert wird, eine Frequenz von 2 (6/3).
Und initialisiere einen Heap, der anfänglich enthält:
(A, 3, 2, 2)
(B, 2, 3, 3)
(C, 1, 6, 6)
Jetzt entferne ich das erste Objekt aus dem Heap und gebe es aus. Reduzieren Sie dann die Anzahl um 1, erhöhen Sie die Priorität nach Häufigkeit und fügen Sie sie wieder dem Heap hinzu. Der resultierende Heap ist:
(B, 2, 3, 0)
(A, 2, 2, 4)
(C, 1, 6, 6)
Entfernen Sie anschließend B aus dem Heap, geben Sie es aus und aktualisieren Sie es. Fügen Sie es dann wieder zum Heap hinzu:
(A, 2, 2, 4)
(C, 1, 6, 6)
(B, 1, 3, 6)
Wenn ich so weitermache, erhalte ich die gewünschte Mischung. Ich verwende einen benutzerdefinierten Vergleicher, um sicherzustellen, dass beim Einfügen von Elementen mit gleicher Priorität das Element mit dem höchsten Frequenzwert (dh dem niedrigsten Frequenzwert) zuerst bestellt wird.
Ich habe in meinem Blog eine vollständigere Beschreibung des Problems und seiner Lösung verfasst und einen funktionierenden C # -Code vorgestellt, der es veranschaulicht. Siehe Gleichmäßige Verteilung von Elementen in einer Liste .
Update nach Kommentaren
Ich denke, mein Problem ähnelt dem des OP, und deshalb ist meine Lösung möglicherweise nützlich. Ich entschuldige mich dafür, dass ich meine Antwort nicht mehr in Bezug auf die Frage des OP formuliert habe.
Der erste Einwand, dass meine Lösung A, B und C anstelle von 0, 1 und 2 verwendet, ist leicht zu beheben. Es ist einfach eine Frage der Nomenklatur. Ich finde es einfacher und weniger verwirrend, darüber nachzudenken und "zwei Einsen" zu sagen, anstatt "zwei Einsen". Für die Zwecke dieser Erörterung habe ich meine nachstehenden Ausgaben geändert, um die Nomenklatur des OP zu verwenden.
Natürlich beschäftigt sich mein Problem mit dem Konzept der Distanz. Wenn Sie die Dinge "gleichmäßig verteilen" möchten, ist die Entfernung impliziert. Aber auch hier war es mein Versäumnis, nicht angemessen zu zeigen, wie ähnlich mein Problem dem des OP ist.
Ich habe einige Tests mit den beiden Beispielen durchgeführt, die das OP lieferte. Das ist:
[1,1,2,2,3,3] // which I converted to [0,0,1,1,2,2]
[0,0,0,0,1,1,1,2,2,3]
In meiner Nomenklatur werden diese als [2,2,2] bzw. [4,3,2,1] ausgedrückt. Das heißt, im letzten Beispiel "4 Elemente vom Typ 0, 3 Elemente vom Typ 1, 2 Elemente vom Typ 2 und 1 Element vom Typ 3".
Ich habe mein Testprogramm ausgeführt (wie nachstehend beschrieben) und meine Ergebnisse veröffentlicht. Ohne Eingaben aus dem OP kann ich nicht sagen, ob meine Ergebnisse ähnlich, schlechter als oder besser als seine sind. Ich kann meine Ergebnisse auch nicht mit den Ergebnissen anderer vergleichen, da noch niemand etwas veröffentlicht hat.
Ich kann jedoch sagen, dass der Algorithmus eine gute Lösung für mein Problem der Beseitigung der Schichtung beim Mischen von Flüssigkeiten darstellt. Und es sieht so aus, als ob es eine vernünftige Lösung für das OP-Problem darstellt.
Für die unten gezeigten Ergebnisse habe ich den Algorithmus verwendet, den ich in meinem Blogeintrag beschrieben habe, wobei die anfängliche Priorität auf festgelegt Frequency/2
und der Heap-Vergleich geändert wurde, um das häufigere Element zu bevorzugen. Der geänderte Code wird hier angezeigt, wobei die geänderten Zeilen kommentiert sind.
private class HeapItem : IComparable<HeapItem>
{
public int ItemIndex { get; private set; }
public int Count { get; set; }
public double Frequency { get; private set; }
public double Priority { get; set; }
public HeapItem(int itemIndex, int count, int totalItems)
{
ItemIndex = itemIndex;
Count = count;
Frequency = (double)totalItems / Count;
// ** Modified the initial priority setting.
Priority = Frequency/2;
}
public int CompareTo(HeapItem other)
{
if (other == null) return 1;
var rslt = Priority.CompareTo(other.Priority);
if (rslt == 0)
{
// ** Modified to favor the more frequent item.
rslt = Frequency.CompareTo(other.Frequency);
}
return rslt;
}
}
Beim Ausführen meines Testprogramms mit dem ersten Beispiel des OP erhalte ich:
Counts: 2,2,2
Sequence: 1,0,2,1,0,2
Distances for item type 0: 3,3
Stddev = 0
Distances for item type 1: 3,3
Stddev = 0
Distances for item type 2: 3,3
Stddev = 0
Mein Algorithmus arbeitet also für das triviale Problem, dass alle Zählungen gleich sind.
Für das zweite Problem, das das OP stellte, bekam ich:
Counts: 4,3,2,1
Sequence: 0,1,2,0,1,3,0,2,1,0
Distances for item type 0: 3,3,3,1
Stddev = 0.866025403784439
Distances for item type 1: 3,4,3
Stddev = 0.471404520791032
Distances for item type 2: 5,5
Stddev = 0
Distances for item type 3: 10
Stddev = 0
Standard dev: 0.866025403784439,0.471404520791032,0,0
Ich sehe keinen offensichtlichen Weg, dies zu verbessern. Es könnte anders arrangiert werden, um die Abstände für Punkt 0 [2,3,2,3] oder eine andere Anordnung von 2 und 3 festzulegen, aber das ändert die Abweichungen für Punkt 1 und / oder 2. Ich weiß wirklich nicht, was "optimal" ist in dieser Situation. Ist es besser, eine größere Abweichung bei den häufigeren oder den weniger häufigen Artikeln zu haben?
Da ich keine anderen Probleme aus dem OP hatte, verwendete ich seine Beschreibungen, um einige meiner eigenen zu erfinden. Er sagte in seinem Beitrag:
Eine typische Liste enthält ~ 50 Elemente mit ~ 15 verschiedenen Werten in unterschiedlichen Mengen.
Meine beiden Tests waren also:
[8,7,6,5,5,4,3,3,2,2,2,1,1,1,1] // 51 items, 15 types
[12,6,5,4,4,3,3,3,2,2,2,1,1] // 48 items, 13 types
Und meine Ergebnisse:
Counts: 8,7,6,5,5,4,3,3,2,2,2,1,1,1,1
Sequence: 0,1,2,3,4,5,7,6,0,1,2,8,9,10,4,3,0,1,5,2,0,1,3,4,6,7,14,11,13,12,0,2,5,1,0,3,4,2,8,10,9,1,0,7,6,5,3,4,2,1,0
Distances for item type 0: 8,8,4,10,4,8,8,1
Stddev = 2.82566363886433
Distances for item type 1: 8,8,4,12,8,8,3
Stddev = 2.76272565797339
Distances for item type 2: 8,9,12,6,11,5
Stddev = 2.5
Distances for item type 3: 12,7,13,11,8
Stddev = 2.31516738055804
Distances for item type 4: 10,9,13,11,8
Stddev = 1.72046505340853
Distances for item type 5: 13,14,13,11
Stddev = 1.08972473588517
Distances for item type 6: 17,20,14
Stddev = 2.44948974278318
Distances for item type 7: 19,18,14
Stddev = 2.16024689946929
Distances for item type 8: 27,24
Stddev = 1.5
Distances for item type 9: 28,23
Stddev = 2.5
Distances for item type 10: 26,25
Stddev = 0.5
Distances for item type 11: 51
Stddev = 0
Distances for item type 12: 51
Stddev = 0
Distances for item type 13: 51
Stddev = 0
Distances for item type 14: 51
Stddev = 0
Und zum zweiten Beispiel:
Counts: 12,6,5,4,4,3,3,3,2,2,2,1,1
Sequence: 0,1,2,0,3,4,7,5,6,0,1,8,9,10,0,2,0,3,4,1,0,2,6,7,5,12,11,0,1,0,3,4,2,0,1,10,8,9,0,7,5,6,0,
4,3,2,1,0
Distances for item type 0: 3,6,5,2,4,7,2,4,5,4,5,1
Stddev = 1.68325082306035
Distances for item type 1: 9,9,9,6,12,3
Stddev = 2.82842712474619
Distances for item type 2: 13,6,11,13,5
Stddev = 3.44093010681705
Distances for item type 3: 13,13,14,8
Stddev = 2.34520787991171
Distances for item type 4: 13,13,12,10
Stddev = 1.22474487139159
Distances for item type 5: 17,16,15
Stddev = 0.816496580927726
Distances for item type 6: 14,19,15
Stddev = 2.16024689946929
Distances for item type 7: 17,16,15
Stddev = 0.816496580927726
Distances for item type 8: 25,23
Stddev = 1
Distances for item type 9: 25,23
Stddev = 1
Distances for item type 10: 22,26
Stddev = 2
Distances for item type 11: 48
Stddev = 0
Distances for item type 12: 48
Stddev = 0