Algoritmo per distribuire gli articoli "in modo uniforme"


25

Sto cercando un algoritmo per distribuire i valori da un elenco in modo che l'elenco risultante sia il più "bilanciato" o "uniformemente distribuito" possibile (tra virgolette perché non sono sicuro che questi siano i modi migliori per descriverlo ... in seguito fornirò un modo per misurare se un risultato è migliore di altri).

Quindi, per l'elenco:

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

Uno dei migliori risultati, dopo aver ridistribuito i valori, è:

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

Potrebbero esserci altri risultati buoni come questo, e ovviamente questo diventa più complicato con un insieme di valori meno uniforme.

Ecco come misurare se un risultato è migliore di altri:

  1. Contare le distanze tra ciascun elemento e l'elemento successivo con lo stesso valore.

  2. Calcola la deviazione standard per quella serie di distanze. Una dispersione più bassa significa un risultato migliore.

osservazioni:

  • Quando si calcola una distanza e si raggiunge la fine dell'elenco senza trovare un oggetto con lo stesso valore, torniamo all'inizio dell'elenco. Quindi, al massimo, verrà trovato lo stesso oggetto e la distanza per quell'elemento sarà la lunghezza dell'elenco. Ciò significa che l'elenco è ciclico ;
  • Un elenco tipico ha ~ 50 articoli con ~ 15 valori diversi in varie quantità.

Così:

  • Per il risultato [1, 2, 3, 1, 2, 3], le distanze sono [3, 3, 3, 3, 3, 3]e la deviazione standard è 0;
  • Per il risultato [1, 1, 2, 2, 3, 3], le distanze sono [1, 5, 1, 5, 1, 5]e la deviazione standard è 2;
  • Il che rende il primo risultato migliore del secondo (deviazione inferiore è migliore).

Date queste definizioni, chiedo un indizio su quali algoritmi o strategie dovrei cercare.


Sembra che tu voglia risolvere il problema (variante dell'ottimizzazione del) Partizione , almeno approssimativamente. Probabilmente ci sono molti algoritmi per quello!
Raffaello

Rileggendo questo, perché contare le occorrenze di tutti i valori e quindi posizionare ciclicamente i valori non sempre fornisce la soluzione ottimale?
Raffaello

Risposte:


8

Mi sono imbattuto in questa domanda mentre cercavo un problema simile: aggiunte ottimali di liquidi per ridurre la stratificazione. Sembra che la mia soluzione sia applicabile anche alla tua situazione.

Se vuoi mescolare i liquidi A, B e C nella proporzione 30,20,10 (cioè 30 unità di A, 20 unità di B e 10 unità di C), finisci con la stratificazione se aggiungi tutto la A, poi tutta la B e poi tutta la C. Farai meglio a mescolare unità più piccole. Ad esempio, eseguire aggiunte di unità singole nella sequenza [A, B, A, C, B, A]. Ciò impedirà del tutto la stratificazione.

Il modo in cui ho scoperto di farlo è di trattarlo come una specie di unione, usando una coda prioritaria. Se creo una struttura per descrivere le aggiunte:

MergeItem
    Item, Count, Frequency, Priority

La frequenza è espressa come "uno ogni N". Quindi A, che viene aggiunto tre volte su sei, ha una frequenza di 2 (6/3).

E inizializza un heap che inizialmente contiene:

(A, 3, 2, 2)
(B, 2, 3, 3)
(C, 1, 6, 6)

Ora rimuovo il primo elemento dall'heap e lo output. Quindi riduci il conteggio di 1 e aumenta la priorità per frequenza e aggiungilo nuovamente all'heap. L'heap risultante è:

(B, 2, 3, 0)
(A, 2, 2, 4)
(C, 1, 6, 6)

Quindi, rimuovere B dall'heap, emetterlo e aggiornarlo, quindi aggiungere di nuovo all'heap:

(A, 2, 2, 4)
(C, 1, 6, 6)
(B, 1, 3, 6)

Se continuo in quel modo, ottengo la miscela desiderata. Uso un comparatore personalizzato per garantire che quando vengono inseriti nell'Heap articoli con priorità uguale, venga ordinato per primo quello con il valore di Frequenza più alto (ovvero il meno frequente).

Ho scritto una descrizione più completa del problema e della sua soluzione sul mio blog e ho presentato un codice C # funzionante che lo illustra. Vedi Distribuire uniformemente gli articoli in un elenco .

Aggiorna dopo i commenti

Penso che il mio problema sia simile al problema del PO e quindi che la mia soluzione sia potenzialmente utile. Mi scuso per non aver inquadrato più la mia risposta nei termini della domanda del PO.

La prima obiezione, secondo cui la mia soluzione sta usando A, B e C anziché 0, 1 e 2, è facilmente risolta. È semplicemente una questione di nomenclatura. Trovo più facile e meno confuso pensare e dire "due A" anziché "due 1". Ma ai fini di questa discussione ho modificato i miei risultati di seguito per utilizzare la nomenclatura del PO.

Ovviamente il mio problema riguarda il concetto di distanza. Se si desidera "distribuire le cose in modo uniforme", è implicita la distanza. Ma, ancora una volta, non sono riuscito a dimostrare in modo adeguato come il mio problema fosse simile a quello del PO.

Ho eseguito alcuni test con i due esempi forniti dall'OP. Questo è:

[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]

Nella mia nomenclatura quelli sono espressi come [2,2,2] e [4,3,2,1], rispettivamente. Cioè, nell'ultimo esempio, "4 articoli di tipo 0, 3 articoli di tipo 1, 2 articoli di tipo 2 e 1 articolo di tipo 3."

Ho eseguito il mio programma di test (come descritto immediatamente di seguito) e ho pubblicato i miei risultati. Assente input dall'OP, non posso dire se i miei risultati sono simili, peggiori o migliori dei suoi. Né posso confrontare i miei risultati con quelli di chiunque altro perché nessun altro ha pubblicato alcun risultato.

Posso dire, tuttavia, che l'algoritmo fornisce una buona soluzione al mio problema di eliminare la stratificazione durante la miscelazione di liquidi. E sembra che fornisca una soluzione ragionevole al problema del PO.

Per i risultati mostrati di seguito, ho usato l'algoritmo che ho dettagliato nel mio post di blog, con la priorità iniziale impostata su Frequency/2e il comparatore di heap modificato per favorire l'elemento più frequente. Il codice modificato viene mostrato qui, con le righe modificate commentate.

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;
    }
}

Eseguendo il mio programma di test con il primo esempio dell'OP, ottengo:

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

Quindi il mio algoritmo funziona per il banale problema di eguagliare tutti i conteggi.

Per il secondo problema pubblicato dall'OP, ho ottenuto:

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

Non vedo un modo ovvio per migliorarlo. Potrebbe essere riorganizzato per fare le distanze per l'articolo 0 [2,3,2,3] o qualche altra disposizione di 2 e 3, ma ciò cambierà le deviazioni per gli articoli 1 e / o 2. Non so davvero cosa "ottimale" è in questa situazione. È meglio avere una deviazione maggiore sugli articoli più frequenti o meno frequenti?

In assenza di altri problemi dall'OP, ho usato le sue descrizioni per inventarne alcune. Ha detto nel suo post:

Un elenco tipico ha ~ 50 articoli con ~ 15 valori diversi in varie quantità.

Quindi i miei due test sono stati:

[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

E i miei risultati:

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

E per il secondo esempio:

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 Vedi il mio aggiornamento. Credo di mostrare come il mio problema sia simile al problema del PO e come il mio algoritmo fornisca una soluzione al problema del PO.
Jim Mischel,

Roba buona! Grazie per l'ottimo aggiornamento. Upvoted.
DW

Abbastanza interessante, come ho detto prima. La semplicità dell'idea è allettante. Non ho avuto il tempo di leggere tutto attentamente. La tua soluzione tiene effettivamente conto della ciclicità della domanda originale? Potrebbe esserci un modo per adattarlo allo scopo, ma non ne sono completamente sicuro.
babou,

@babou: I miei calcoli della distanza si concludono, come puoi vedere nei risultati, ma l'algoritmo stesso non tiene conto di specificità della natura ciclica del problema del PO. Né vedo alcun modo in cui potrei adattare l'algoritmo per farlo. O, del resto, come prendere in considerazione la natura ciclica migliorerebbe i risultati. Anche se è interessante considerare di raddoppiare tutti i conteggi (ovvero cambiare [3,2,1] in [6,4,2]), che sarebbe effettivamente la stessa cosa. Il mio sospetto è che l'algoritmo produrrebbe risultati identici.
Jim Mischel,

6

Questo "odora" come potrebbe essere NP-difficile. Quindi, cosa fai quando hai un problema NP-difficile? Lancia un euristico o un algoritmo di approssimazione o usa un risolutore SAT.

Nel tuo caso, se non hai bisogno della soluzione ottimale assoluta, un punto di partenza ragionevole potrebbe essere provare la ricottura simulata . Esiste un modo naturale per prendere qualsiasi soluzione candidata e spostarla in una soluzione candidata vicina: selezionare casualmente due elementi nell'elenco e scambiarli. La ricottura simulata proverà iterativamente a migliorare la soluzione. Puoi trovare molte risorse sulla ricottura simulata, se non ne hai familiarità. Puoi anche sperimentare altri insiemi di "mosse locali" che apportano piccole modifiche a una soluzione candidata, con la speranza di migliorarla in modo incrementale (cioè ridurre la deviazione standard delle distanze).

ttt2Xio,jXio,jiojt2

Ma suggerirei di iniziare con la ricottura simulata. Questa è la prima cosa che vorrei provare, perché penso che potrebbe funzionare.


I tuoi suggerimenti sono il modo standard per affrontare questo tipo di problemi di pianificazione. Immagino che ci sia un software commerciale in giro per questo. Come lo gestiscono?
babou,

@babou, grande domanda - Non ne ho idea!
DW

Ho ulteriormente sviluppato i dettagli del mio algoritmo, ma dubito che molte applicazioni esistenti lo userebbero. In realtà, mi chiedo persino se le applicazioni di pianificazione affrontino un problema di questo tipo. Ho chiesto informazioni su SE.softwarerecs, dal momento che non vedo come porre la domanda qui, a parte un commento come ho appena fatto.
babou,

La soluzione ottimale potrebbe essere NP-difficile. Ma una soluzione abbastanza praticabile è O (n log k), dove n è il numero totale di articoli e k è il numero di tipi di elementi. Vedi la mia risposta e il mio post sul blog collegato.
Jim Mischel,

2

Schizzo di un algoritmo euristico

Non ho una soluzione esatta per questo problema. Ma poiché il commento di Raphael suggerisce che sembra il problema della partizione, per il quale sono stati sviluppati algoritmi euristici, proverò un approccio euristico. Questo è solo uno schizzo di un algoritmo euristico.

vn[1 ..n]ionio

nvnvn/nv

v

ion/nionmodnion/nio

Questo guiderà il nostro algoritmo.

n

io|n/nio-v|

All'inizio può essere un valore con molte delle poche occorrenze. Penso che in realtà non faccia alcuna differenza, dal momento che i vincoli creati occupando gli slot sono in proporzione al numero di valori ben posizionati (?).

Il primo valore considerato può essere inserito senza alcun vincolo. Quindi gli altri valori devono essere posizionati in modo da ridurre al minimo il loro contributo alla deviazione standard, ma solo negli slot lasciati liberi da qualsiasi valore sia stato inserito in precedenza.

Il posizionamento delle occorrenze di un valore negli slot rimanenti può essere fatto con un algoritmo di programmazione dinamica, in modo da unire i calcoli che posizionano lo stesso numero di valori tra due posizioni, mantenendo solo quelli che hanno un contributo minimo alla deviazione standard (es. valore minimo per la somma del quadrato delle loro deviazioni).

v

j|n/nj-v|

Quindi inserisci i valori singleton negli slot rimanenti.

Credo che questo dovrebbe generalmente fornire una soluzione ragionevole, ma non ho ancora idea di come dimostrarlo o stimare il divario con una soluzione ottimale.


Ho la stessa impressione che non abbia importanza se iniziamo con quelli più o meno comuni, lasciando da parte i singoli. La strategia che apparentemente mi ha dato i migliori risultati inizia a ordinare i valori per occorrenza e a metterli in ordine a partire da quelli che si verificano maggiormente. Questo lascia naturalmente i singoli alla fine.
Moraes,

vn/vV

Vuoi dire che, per un elenco con 10 valori [0, 0, 0, 0, 1, 1, 1, 2, 2, 3]e v 4, inseriremo i primi valori 1( 10/3 = 3.33, il più vicino a v), quindi 2( 10/2 = 5, il prossimo più vicino), quindi 0( 10/4 = 2.5)? Oppure: potresti dare un esempio di "diminuzione della deviazione media della distanza dal valore v"?
Moraes,

1
No, faccio esattamente il contrario. Prendendo il tuo esempio, l'ordine del posizionamento è prima O poiché la sua distanza media 2,5 si discosta maggiormente da v = 4, quindi 2, quindi 1 e il singleton 3. - - - Stai suggerendo che dovrei riscrivere più chiaramente alcuni parte della mia spiegazione per questa strategia?
babou,

No, va bene. Proverò qualcosa insieme a questa idea e riporterò indietro.
Moraes,

1

Sembra che sia molto in ritardo alla festa, ma pubblicando nel caso qualcuno dovesse imbattersi di nuovo in questo. La mia soluzione è simile al plus di @ babou. Oggi, ho avuto un problema di pianificazione in un sistema incorporato che mi ha portato a questo thread. Ho un'implementazione specifica del mio problema in C, ma ho pensato di pubblicare qui una soluzione più generica in Python (la versione C è complicata dal fatto che mi sono limitato a uno stack di dimensioni fisse di piccole dimensioni e senza memoria allocazioni, quindi eseguo l'intero algoritmo sul posto). La tecnica di antialiasing utilizzata di seguito è qualcosa che potresti usare per disegnare una linea su uno schermo con colore a 2 bit. L'algoritmo qui ottiene un punteggio più basso (cioè, migliore) quando misurato usando la somma della deviazione standard per gli input utilizzati da Jim Mischel rispetto a quella particolare soluzione.

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

risultati per

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)

Se vengono forniti input del modulo specificato da @moraes, è possibile convertirlo in un modulo utilizzabile da questa funzione nei passaggi O (n) utilizzando bit di memoria Big Omega (n * log (n)) dove n è il numero di elementi ( in un elenco con 255 elementi, non avrai bisogno di più di 255 byte extra) mantenendo un array parallelo con i conteggi delle ripetizioni. In alternativa, è possibile eseguire una coppia di ordinamenti sul posto con O (1) memoria aggiuntiva.

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)

Modifica: so che questa soluzione non produce l'output ottimale per controesempio. Un input di [6, 2, 1]produce [0, 1, 0, 0, 2, 0, 0, 1, 0]; una soluzione migliore è [0, 0, 1, 0, 2, 0, 0, 1, 0].


Credo di aver spiegato il mio algoritmo nei commenti sul codice e le basi dell'algoritmo nel preambolo.
lungj,

Avrei preferito vedere una descrizione autonoma delle idee alla base del tuo algoritmo e uno pseudocodice conciso per l'algoritmo. Attualmente quello che vedo nel testo introduttivo è (1) il tuo approccio è simile a quello di @ babou e (2) usa una tecnica anti-aliasing (in qualche modo). Inoltre, non tutti qui leggono Python. In ogni caso, è una vecchia risposta, quindi capisco se non vuoi migliorarlo, ma sto solo notando le nostre aspettative su questo sito - non solo per te, ma per gli altri che potrebbero imbattersi in questa pagina in il futuro ed essere propensi a rispondere.
DW

0

Questo algoritmo funziona con una matrice di numeri interi, in cui ogni numero intero rappresenta una categoria diversa. Crea matrici separate per ogni categoria. Ad esempio, se l'array iniziale è [1, 1, 1, 2, 2, 3], creerà tre array, [3], [2, 2], [1, 1, 1].

Da lì combina in modo ricorsivo i due array più piccoli (in questo esempio, il [3] e [2,2]) e distanzia il posizionamento degli elementi dell'array più piccolo nel secondo array più piccolo basato principalmente sul rapporto del numero di occorrenze delle categorie più grandi e più piccole. In questo esempio, finiremmo con [2,3,2]. Quindi userebbe questo array come array più piccolo che verrà combinato nel prossimo array più grande, fino a quando rimarrà solo un array.

<?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
Questo non è un sito di codifica. Si prega di non pubblicare risposte di solo codice. Vorremmo invece che tu spiegassi le idee alla base della tua risposta e fornisse uno pseudocodice conciso per il tuo algoritmo.
DW

Benvenuti in Informatica ! Nel caso in cui non fossi a conoscenza o ti dimenticassi per un momento, leggere il codice in una determinata lingua è di solito uno dei compiti più difficili che possiamo avere, a volte anche se il codice è stato scritto da noi stessi. Questo è uno dei motivi per cui non apprezziamo molto il codice reale su questo sito, sebbene possa rappresentare molto più lavoro rispetto allo pseudocodice vagamente scritto. Ovviamente, apprezzo tutto il codice di lavoro effettivo che può essere eseguito o scintillato immediatamente.
Apass

La spiegazione è lì nel codice dimostrativo commentato; che non in una sintassi arcaica come APL, ma una sintassi di facile comprensione abbastanza vicina allo pseudo codice. Aiuterebbe se la mia spiegazione non fosse in carattere monospace?
vtim

Sì. Aiuta. Non tutti leggono PHP, forse non tutti potrebbero determinare cos'è un commento (forse è argomento di paglia) o semplicemente non vogliono leggere il blocco di codice e interpretarlo, ma leggi l'idea, che hai incluso in alto e racconta tutto. +1 da me. Il tuo codice è pulito e ben documentato, ma semplicemente non stiamo codificando il sito, quindi la descrizione testuale è importante qui. Grazie per la tua modifica.
Evil

-1

CODICE ANSI C.

Questo codice funziona immaginando una linea retta nello spazio n dimensionale (dove n è il numero di categorie) che passa attraverso l'origine con il vettore direzionale (v1, v2, ..., vi, ... vn) dove vi è il numero di articoli nella categoria i. A partire dall'origine lo scopo è quello di trovare il prossimo punto più vicino alla linea. Utilizzando l'esempio [0 0 0 0 0 1 1 1 2 2 2 3] produce il risultato [0 1 2 0 3 1 0 2 0 1 2 0]. Usando l'esempio di Lungj [0 0 0 0 0 0 1 1 2] otteniamo [0 1 0 0 2 0 0 1 0], che è esattamente lo stesso del risultato di Lungj.

L'algoritmo è reso più efficiente utilizzando solo l'aritmetica dei numeri interi e considerando solo i delta tra le distanze da ciascun punto alla linea.

#define MAXCATEGORIES 100

int main () {int i = 0; int j = 0; int catsize = 0; int vector [MAXCATEGORIES]; int point [MAXCATEGORIES]; categorie int = 0; int totalitems = 0; int best = 0; long d2 = 0L; long vp = 0L; long v2 = 0L; delta lungo = 0L; beta lunga = 0L;

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
Benvenuti nel sito! Per quanto riguarda la formattazione, devi indentare ciascuna riga del codice con quattro spazi in modo che il sistema ottenga il markup corretto. In generale, non stiamo cercando grandi blocchi di codice come risposte alle domande e, in particolare, le routine di immissione dei dati non aggiungono nulla qui. Hai qualche spiegazione nella parte superiore del tuo post, ma sarebbe meglio espandersi su questo e ridurre il codice.
David Richerby,

Questo non è un sito di codifica. Si prega di non pubblicare risposte di solo codice. Vorremmo invece che tu spiegassi le idee alla base della tua risposta e fornisse uno pseudocodice conciso per il tuo algoritmo.
DW

-1

la mia soluzione:

    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']

Utilizza lo pseudocodice (con alcuni commenti necessari) per descrivere il tuo algoritmo.
xskxzr,

Questo non è un sito di codifica. Si prega di non pubblicare risposte di solo codice. Vorremmo invece che tu spiegassi le idee alla base della tua risposta e fornisse uno pseudocodice conciso per il tuo algoritmo.
DW
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.