Algorithmus zur „gleichmäßigen“ Verteilung von Gegenständen


25

Ich suche nach einem Algorithmus zum Verteilen von Werten aus einer Liste, damit die resultierende Liste möglichst "ausgeglichen" oder "gleichmäßig verteilt" ist (in Anführungszeichen, da ich nicht sicher bin, ob dies die beste Art ist, sie zu beschreiben ... Später werde ich einen Weg zeigen, um zu messen, ob ein Ergebnis besser ist als das andere.

Also, für die Liste:

[1, 1, 2, 2, 3, 3]

Eines der besten Ergebnisse nach der Neuverteilung der Werte ist:

[1, 2, 3, 1, 2, 3]

Möglicherweise gibt es andere so gute Ergebnisse wie dieses, und dies wird natürlich mit einem weniger einheitlichen Satz von Werten komplizierter.

So messen Sie, ob ein Ergebnis besser ist als andere:

  1. Zählen Sie die Abstände zwischen jedem Artikel und dem nächsten Artikel mit demselben Wert.

  2. Berechnen Sie die Standardabweichung für diesen Abstandssatz. Eine geringere Dispersion bedeutet ein besseres Ergebnis.

Beobachtungen:

  • Wenn eine Entfernung berechnet wird und das Ende der Liste erreicht ist, ohne einen Artikel mit demselben Wert zu finden, kehren wir zum Anfang der Liste zurück. Es wird also höchstens derselbe Artikel gefunden, und der Abstand für diesen Artikel entspricht der Länge der Liste. Dies bedeutet, dass die Liste zyklisch ist .
  • Eine typische Liste enthält ~ 50 Elemente mit ~ 15 verschiedenen Werten in unterschiedlichen Mengen.

So:

  • Für das Ergebnis [1, 2, 3, 1, 2, 3]sind die Abstände [3, 3, 3, 3, 3, 3]und die Standardabweichung ist 0;
  • Für das Ergebnis [1, 1, 2, 2, 3, 3]sind die Abstände [1, 5, 1, 5, 1, 5]und die Standardabweichung ist 2;
  • Damit ist das erste Ergebnis besser als das zweite (geringere Abweichung ist besser).

Angesichts dieser Definitionen frage ich nach einem Hinweis, nach welchen Algorithmen oder Strategien ich suchen soll.


Scheint, als ob Sie das (Optimierungsvariante des) Partitionsproblems zumindest näherungsweise lösen wollen . Es gibt wahrscheinlich viele Algorithmen dafür!
Raphael

Wenn Sie dies noch einmal lesen, warum ergibt das Zählen aller Werte und das anschließende zyklische Platzieren von Werten nicht immer die optimale Lösung?
Raphael

Antworten:


8

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/2und 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

@ DW Bitte siehe mein Update. Ich glaube, dass ich zeige, wie mein Problem dem OP-Problem ähnelt und wie mein Algorithmus eine Lösung für das OP-Problem bietet.
Jim Mischel

Gutes Zeug! Vielen Dank für das hervorragende Update. Upvoted.
DW

Ganz interessant, wie ich schon sagte. Die Einfachheit der Idee ist ansprechend. Ich hatte keine Zeit, alles sorgfältig zu lesen. Berücksichtigt Ihre Lösung tatsächlich die Zyklizität der ursprünglichen Frage? Es mag einen Weg geben, es für den Zweck anzupassen, aber ich bin nicht ganz sicher, ob es funktioniert.
Babou

@babou: Meine Entfernungsberechnungen machen einen Umbruch, wie Sie in den Ergebnissen sehen können, aber der Algorithmus selbst berücksichtigt die zyklische Natur des OP-Problems nicht spezifisch. Ich sehe auch keine Möglichkeit, den Algorithmus dafür anzupassen. Oder wie die Berücksichtigung des zyklischen Charakters die Ergebnisse verbessern würde. Obwohl es interessant ist, in Betracht zu ziehen, alle Zählungen zu verdoppeln (dh [3,2,1] in [6,4,2] zu ändern), wäre dies praktisch dasselbe. Mein Verdacht ist, dass der Algorithmus identische Ergebnisse liefern würde.
Jim Mischel

6

Dies "riecht" wie es NP-schwer sein könnte. Also, was machst du, wenn du ein NP-hartes Problem hast? Werfen Sie eine Heuristik oder einen Approximationsalgorithmus oder verwenden Sie einen SAT-Solver.

Wenn Sie in Ihrem Fall nicht die absolut optimale Lösung benötigen, besteht ein vernünftiger Ausgangspunkt darin, das simulierte Tempern zu versuchen . Es gibt eine natürliche Möglichkeit, eine Kandidatenlösung zu übernehmen und in eine nahe gelegene Kandidatenlösung zu verschieben: Wählen Sie zwei Elemente in der Liste nach dem Zufallsprinzip aus und tauschen Sie sie aus. Durch simuliertes Tempern wird iterativ versucht, die Lösung zu verbessern. Sie können viele Ressourcen zum simulierten Tempern finden, wenn Sie nicht damit vertraut sind. Sie können auch mit anderen Sätzen von "lokalen Bewegungen" experimentieren, die kleine Änderungen an einer Kandidatenlösung vornehmen, mit der Hoffnung, diese schrittweise zu verbessern (dh die Standardabweichung der Entfernungen zu verringern).

ttt2xich,jxich,jichjt2

Aber ich würde vorschlagen, dass Sie mit simuliertem Tempern beginnen. Das ist das erste, was ich versuchen würde, weil ich denke, es könnte einfach funktionieren.


Sind Ihre Vorschläge die Standardmethode, um diese Art von Planungsproblemen anzugehen? Ich denke, es gibt kommerzielle Software dafür. Wie gehen sie damit um?
Babou

@babou, tolle Frage - ich habe keine Ahnung!
DW

Ich habe die Details meines Algorithmus weiterentwickelt, aber ich bezweifle, dass sehr viele bestehende Anwendungen dies nutzen würden. Eigentlich frage ich mich sogar, ob sich Scheduling-Anwendungen mit einem Problem dieser Art befassen. Ich habe nach Informationen zu SE.softwarerecs gefragt, da ich nicht sehe, wie ich die Frage hier stellen kann, außer als Kommentar, wie ich es gerade getan habe.
Babou

Die optimale Lösung könnte NP-hart sein. Eine durchaus praktikable Lösung ist jedoch O (n log k), wobei n die Gesamtzahl der Elemente und k die Anzahl der Elementtypen ist. Siehe meine Antwort und meinen verlinkten Blog-Beitrag.
Jim Mischel

2

Skizze eines heuristischen Algorithmus

Ich habe keine genaue Lösung für dieses Problem. Da Raphaels Kommentar jedoch vermuten lässt, dass es sich um das Partitionsproblem handelt, für das heuristische Algorithmen entwickelt wurden, werde ich einen heuristischen Ansatz ausprobieren. Dies ist nur eine Skizze eines heuristischen Algorithmus.

vn[1 ..n]ichnich

nvnvn/nv

v

ichn/nichnmodnichn/nich

Das wird unseren Algorithmus leiten.

n

ich|n/nich-v|

Es kann sich um einen Wert handeln, bei dem zunächst nur sehr wenige Vorkommen auftreten. Ich denke, es macht eigentlich keinen Unterschied, da die durch die Belegung von Slots verursachten Einschränkungen im Verhältnis zur Anzahl der gut platzierten Werte (?) Stehen.

Der erste betrachtete Wert kann ohne Einschränkung gesetzt werden. Dann müssen die anderen Werte so platziert werden, dass ihr Beitrag zur Standardabweichung minimiert wird, jedoch nur in den Slots, die durch die zuvor platzierten Werte frei bleiben.

Die Platzierung des Auftretens eines Werts in den verbleibenden Slots kann mit einem dynamischen Programmieralgorithmus erfolgen, um Berechnungen, die die gleiche Anzahl von Werten zwischen zwei Positionen platzieren, zusammenzuführen, wobei nur diejenigen beibehalten werden, die einen minimalen Beitrag zur Standardabweichung leisten (d. H Mindestwert für die Summe der Quadrate ihrer Abweichungen).

v

j|n/nj-v|

Dann setzen Sie die Singleton-Werte in die verbleibenden Slots.

Ich bin der Meinung, dass dies im Allgemeinen eine vernünftige Lösung sein sollte, aber ich habe noch keine Ahnung, wie ich es beweisen oder die Lücke mit einer optimalen Lösung abschätzen soll.


Ich habe den gleichen Eindruck, dass es keine Rolle spielt, ob wir mit den häufigsten oder den am wenigsten verbreiteten beginnen und die Singletons beiseite lassen. Die Strategie, mit der ich anscheinend die besten Ergebnisse erzielt habe, sortiert die Werte nach Vorkommen und ordnet sie in der Reihenfolge der am häufigsten vorkommenden Werte an. Dies lässt natürlich Singletons bis zum Ende.
Moraes

vn/vV

Meinen Sie damit, dass wir für eine Liste mit 10 Werten [0, 0, 0, 0, 1, 1, 1, 2, 2, 3]und v 4zuerst Werte 1( 10/3 = 3.33, am nächsten an v), dann 2( 10/2 = 5, am nächsten) und dann 0( 10/4 = 2.5) setzen würden? Oder: Könnten Sie ein Beispiel für "abnehmende mittlere Distanzabweichung vom Wert v" geben?
Moränen

1
Nein, ich mache genau das Gegenteil. Nehmen Sie Ihr Beispiel, die Reihenfolge der Positionierung ist zuerst O, da der mittlere Abstand 2,5 am meisten von v = 4, dann 2, dann 1 und dem Singleton 3 abweicht Teil meiner Erklärung für diese Strategie?
Babou

Nein, alles in Ordnung. Ich werde etwas in diese Richtung versuchen und berichten.
Moraes

1

Es sieht so aus, als ob ich sehr spät zur Party komme, aber wenn jemand etwas posten sollte, stößt er erneut darauf. Meine Lösung ähnelt @ babou's plus. Ich hatte heute früher ein Planungsproblem in einem eingebetteten System, das mich zu diesem Thread führte. Ich habe eine Implementierung speziell für mein Problem in C, aber ich dachte, ich würde hier eine allgemeinere Lösung in Python veröffentlichen (die C-Version wird durch die Tatsache kompliziert, dass ich mich auf einen kleinen Stapel fester Größe und keinen Speicher beschränkt habe Zuweisungen, also führe ich den gesamten Algorithmus vor Ort aus). Die unten verwendete Anti-Aliasing-Technik können Sie zum Zeichnen einer Linie auf einem Bildschirm mit 2-Bit-Farbe verwenden. Der Algorithmus erzielt hier eine niedrigere Punktzahl (dh eine bessere), wenn er die Summe der Standardabweichung für die von Jim Mischel verwendeten Eingaben als diese bestimmte Lösung verwendet.

def generate(item_counts):
'''item_counts is a list of counts of "types" of items. E.g., [3, 1, 0, 2] represents
   a list containing [1, 1, 1, 2, 4, 4] (3 types of items/distinct values). Generate
   a new list with evenly spaced values.'''
# Sort number of occurrences by decreasing value.
item_counts.sort(reverse=True)
# Count the total elements in the final list.
unplaced = sum(item_counts)
# Create the final list.
placements = [None] * unplaced

# For each type of item, place it into the list item_count times.
for item_type, item_count in enumerate(item_counts):
    # The number of times the item has already been placed
    instance = 0
    # Evenly divide the item amongst the remaining unused spaces, starting with
    # the first unused space encountered.
    # blank_count is the number of unused spaces seen so far and is reset for each
    # item type.
    blank_count = -1
    for position in range(len(placements)):
        if placements[position] is None:
            blank_count += 1
            # Use an anti-aliasing technique to prevent bunching of values.
            if blank_count * item_count // unplaced == instance:
                placements[position] = item_type
                instance += 1
    # Update the count of number of unplaced items.
    unplaced -= item_count

return placements

Ergebnisse für

Input counts: 8,7,6,5,5,4,3,3,2,2,2,1,1,1,1
Sum of stddev: 16.8 (vs. 22.3 via Jim Mischel)

Input of counts: 12,6,5,4,4,3,3,3,2,2,2,1,1
Sum of stddev: 18.0 (vs. 19.3 via Jim Mischel)

Wenn Sie Eingaben in der von @moraes angegebenen Form vornehmen, können Sie diese in Schritten von O (n) in eine von dieser Funktion verwendbare Form umwandeln. In einer Liste mit 255 Elementen benötigen Sie nicht mehr als 255 zusätzliche Bytes, indem Sie ein paralleles Array mit den Wiederholungszählungen beibehalten. Alternativ kann ein Paar von In-Place-Sortierungen mit O (1) zusätzlichem Speicher ausgeführt werden.

PS

import numpy
import collections

def evaluate(l):
    '''Given a distribution solution, print the sum of stddevs for each type in the solution.'''
    l2 = l * 2
    distances = [None] * len(l)
    distance_dict = collections.defaultdict(list)
    for i in range(len(l)):
        distances[i] = l2.index(l[i], i + 1) - i
        distance_dict[l[i]].append(l2.index(l[i], i + 1) - i)

    keys = list(distance_dict.keys())
    keys.sort()
    score = 0
    # Calculate standard deviations for individual types.
    for key in keys:
        sc = numpy.std(distance_dict[key])
        score += sc
    print('Stddev sum: ', score)

Bearbeiten: Ich weiß, dass diese Lösung nicht die optimale Ausgabe durch Gegenbeispiel erzeugt. Eine Eingabe von [6, 2, 1]Erzeugnissen [0, 1, 0, 0, 2, 0, 0, 1, 0]; eine bessere lösung ist [0, 0, 1, 0, 2, 0, 0, 1, 0].


Ich glaube, ich habe meinen Algorithmus in den Codekommentaren und die Grundlage für den Algorithmus in der Präambel erklärt.
28.

Ich hätte es vorgezogen, eine in sich geschlossene Beschreibung der Ideen hinter Ihrem Algorithmus und einen prägnanten Pseudocode für den Algorithmus zu sehen. Derzeit sehe ich im Einführungstext Folgendes: (1) Ihr Ansatz ähnelt dem von @ babou und (2) verwendet (irgendwie) eine Antialiasing-Technik. Auch liest hier nicht jeder Python. Auf jeden Fall ist es eine alte Antwort, also verstehe ich, wenn Sie sie nicht verbessern möchten, aber ich nehme nur unsere Erwartungen auf dieser Site zur Kenntnis - nicht nur für Sie, sondern für andere, die diese Seite in möglicherweise finden die Zukunft und neigen dazu, zu antworten.
DW

0

Dieser Algorithmus arbeitet mit einem Array von Ganzzahlen, wobei jede Ganzzahl eine andere Kategorie darstellt. Es werden separate Arrays für jede Kategorie erstellt. Wenn das Start-Array beispielsweise [1, 1, 1, 2, 2, 3] ist, werden drei Arrays [3], [2, 2], [1, 1, 1] erstellt.

Von dort aus werden die beiden kleinsten Arrays (in diesem Beispiel [3] und [2,2]) rekursiv kombiniert und die Position der Elemente des kleineren Arrays in das zweitkleinste Array eingeteilt, hauptsächlich basierend auf dem Verhältnis der Anzahl von Vorkommen der größeren gegen die kleineren Kategorien. In diesem Beispiel würden wir mit [2,3,2] abschließen. Dann würde es dieses Array als das kleinere Array verwenden, das zu dem nächstgrößeren Array kombiniert wird, bis nur noch ein Array übrig ist.

<?php
/**
 *This will separete the source array into separate arrays for each category
 */
function splitArrayByCategory($source) {

    //  index is the category, value is the tally
    $categoryCounts  = array_count_values($source);

    // Sort that list, keep index associations
    asort($categoryCounts);

    // build separate arrays for each category
    // index = order, smaller index = smaller category count
    // value = array of each category
    $separated = array();
    foreach ($categoryCounts as $category => $tally)
        $separated[] = array_fill(0, $tally, $category);

    return $separated;
}

/**
 * Will take the array of arrays generated by splitArrayByCategory, and merge
 * them together so categories are evenly distributed
 */
function mergeCategoryArrays($categoryArray) {

    // How many entries are there, for the smallest & second smallest categories
    $smallerCount = count($categoryArray[0]);
    $largerCount  = count($categoryArray[1]);

    // Used to determine how much space there should be between these two categories
    $space = $largerCount/$smallerCount;

    // Merge the array of the smallest category into the array of the second smallest
    foreach ($categoryArray[0] as $domIndex => $domain) {
        // Where should we splice the value into the second array?
        $location = floor($domIndex*$space+$domIndex+($space/2));
        // actually perform the splice
        array_splice($categoryArray[1], $location, 0, $domain);
    }

    // remove the smallest domain we just spliced into the second smallest
    unset($categoryArray[0]);

    // reset the indexes
    $categoryArray = array_values($categoryArray);

    // If we have more than one index left in the categoryArray (i.e. some things
    // still need to get merged), let's re-run this function,
    if (count($categoryArray)>1)
        $categoryArray = mergeCategoryArrays($categoryArray);

    return $categoryArray;
}

// The sample list we're working with.
// each integer represents a different category
$listSample = [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,5,5,5,5,6,6,7,7,7,7];

// Split the sample list into separate arrays for each category
$listSplitByCategory = splitArrayByCategory($listSample);

// If there are not at least two categories, what's the point?
if (count($listSplitByCategory) < 2) throw new Exception("You need at least two categories");

// perform the actual distribution of categories.
$listEvenlyDistributed = mergeCategoryArrays($listSplitByCategory)[0];

// Display the array before and after the categories are evenly distributed
for ($i=0; $i<count($listSample); $i++) {
    print $listSample[$i].",";
    print $listEvenlyDistributed[$i]."\n";
}

2
Dies ist keine Codierungssite. Bitte poste keine Code-only-Antworten. Stattdessen möchten wir Sie bitten, die Ideen hinter Ihrer Antwort zu erläutern und einen präzisen Pseudocode für Ihren Algorithmus bereitzustellen.
DW

Willkommen in der Informatik ! Nur für den Fall, dass Sie sich dessen nicht bewusst waren oder es für einen Moment vergessen haben, ist das Lesen von Code in einer bestimmten Sprache normalerweise eine der schwierigsten Aufgaben, die wir haben können, selbst wenn der Code von uns selbst geschrieben wurde. Dies ist einer der Gründe, warum wir echten Code auf dieser Site nicht sehr schätzen, obwohl er möglicherweise viel mehr Arbeit darstellt als lose geschriebenen Pseudocode. Natürlich schätze ich jeden aktuellen Arbeitscode, der sofort ausgeführt oder geblinkt werden kann.
Apass.Jack

Die Erklärung ist da. im kommentierten Demonstrationscode; was nicht in irgendeiner archaischen Syntax wie APL, sondern in einer leicht verständlichen Syntax, die nahe genug an Pseudocode liegt. Wäre es hilfreich, wenn meine Erklärung nicht in Monospace-Schrift wäre?
29.

Ja. Es hilft. Nicht jeder liest PHP, vielleicht kann nicht jeder feststellen, was ein Kommentar ist (vielleicht ist es ein Strohmann-Argument), oder er möchte einfach nicht den Codeblock lesen und interpretieren, sondern die Idee lesen, die Sie oben und oben eingefügt haben es sagt alles. +1 von mir. Ihr Code ist sauber und gut dokumentiert, aber wir sind einfach keine Codierungs-Site, daher ist die textuelle Beschreibung hier wichtig. Vielen Dank für deine Bearbeitung.
Böser

-1

ANSI C CODE

Dieser Code stellt sich eine gerade Linie im n-dimensionalen Raum vor (wobei n die Anzahl der Kategorien ist), die mit dem Richtungsvektor (v1, v2, ..., vi, ... vn) durch den Ursprung verläuft, wobei vi die Anzahl von ist Artikel in der Kategorie i. Ausgehend vom Ursprung ist es das Ziel, den nächstgelegenen Punkt zur Linie zu finden. Am Beispiel [0 0 0 0 0 1 1 1 2 2 2 3] ergibt sich das Ergebnis [0 1 2 0 3 1 0 2 0 1 2 0]. Mit dem Beispiel von Lungj [0 0 0 0 0 0 1 1 2] erhalten wir [0 1 0 0 2 0 0 1 0], was genau dem Ergebnis von Lungj entspricht.

Der Algorithmus wird effizienter, indem nur Ganzzahlarithmetik verwendet und nur die Deltas zwischen den Abständen von jedem Punkt zur Linie berücksichtigt werden.

# MAXKATEGORIEN definieren 100

int main () {int i = 0; int j = 0; int catsize = 0; int vector [MAXKATEGORIEN]; int point [MAXCATEGORIES]; int categories = 0; int totalitems = 0; int best = 0; lang d2 = 0L; lang vp = 0 l; lang v2 = 0L; langes Delta = 0 l; langes Beta = 0 l;

printf("Enter the size of each category (enter 0 to finish):\r\n");
do
{
    catsize = 0;
    #ifdef _MSVC_LANG
            scanf_s("%d", &catsize);
    #else
            scanf("%d", &catsize)
    #endif
    if (catsize > 0)
    {
        vector[categories] = catsize;
        totalitems += catsize;
        categories++;
    }
} while (catsize > 0);

for (i = 0; i < categories; i++)
{
    v2 += vector[i] * vector[i];
    point[i] = 0;
}

for (i = 0; i < totalitems; i++)
{
    for (j = 0; j < categories; j++)
    {
        delta = (2 * point[j] + 1)*v2 - (2 * vp + vector[j])*vector[j];
        if (j == 0 || delta < beta)
        {
            best = j;
            beta = delta;
        }
    }
    point[best]++;
    vp += vector[best];
    printf("%c ", best + '0');  // Change '0' to 'A' if you like letters instead
}
return 0;

}


1
Willkommen auf der Seite! Beim Formatieren müssen Sie jede Codezeile mit vier Leerzeichen einrücken, damit das System die richtige Markierung erhält. Im Allgemeinen suchen wir keine großen Codeblöcke als Antworten auf Fragen, und insbesondere Ihre Dateneingaberoutinen fügen hier nichts hinzu. Sie haben oben in Ihrem Beitrag eine Erklärung, aber es ist besser, diese zu erweitern und den Code zu reduzieren.
David Richerby

Dies ist keine Codierungssite. Bitte poste keine Code-only-Antworten. Stattdessen möchten wir Sie bitten, die Ideen hinter Ihrer Antwort zu erläutern und einen präzisen Pseudocode für Ihren Algorithmus bereitzustellen.
DW

-1

meine Lösung:

    vc = train['classes'].value_counts()
    vc = dict(sorted(vc.items()))
    df = pd.DataFrame()
    train['col_for_sort'] = 0.0

    i=1
    for k,v in vc.items():
        step = train.shape[0]/v
        indent = train.shape[0]/(v+1)
        df2 = train[train['classes'] == k].reset_index(drop=True)
        for j in range(0, v):
        df2.at[j, 'col_for_sort'] = indent + j*step + 0.0001*i   
    df= pd.concat([df2,df])
    i+=1

    train = df.sort_values('col_for_sort', ascending=False).reset_index(drop=True)
    del train['col_for_sort']

Bitte verwenden Sie Pseudocode (mit einigen notwendigen Kommentaren), um Ihren Algorithmus zu beschreiben.
Xskxzr

Dies ist keine Codierungssite. Bitte poste keine Code-only-Antworten. Stattdessen möchten wir Sie bitten, die Ideen hinter Ihrer Antwort zu erläutern und einen präzisen Pseudocode für Ihren Algorithmus bereitzustellen.
DW
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.