2010-07-21 21 views
13

Supponiamo di avere una lista di cose (numeri, per mantenere le cose semplici qui) e ho una funzione che voglio usare per ordinarle, usando SortBy. Ad esempio, il seguente ordina un elenco di numeri da ultima cifra:Ordinamento stabile, ovvero ordinamento minimamente dirompente

SortBy[{301, 201}, Mod[#,10]&] 

e notare come due (cioè tutti) questi numeri hanno lo stesso ultima cifra. Quindi non importa quale ordine li restituiamo. In questo caso Mathematica li restituisce nell'ordine opposto. Come posso garantire che tutte le cravatte siano rotte a favore di come gli articoli sono stati ordinati nell'elenco originale?

(So che è un po 'banale, ma mi sembra che questo di tanto in tanto venga fuori così ho pensato che sarebbe stato utile per ottenerlo su StackOverflow.Però postare qualsiasi cosa mi venga in mente come risposta se nessuno mi batte ad esso)

tentativi di rendere questo più ricercabile:. liste con il minimo disturbo, specie con minor numero di swap, personalizzati tie-rottura, smistamento con costosi scambio, stabile ordinamento.

PS: grazie a Nicholas per indicare che questo è chiamato ordinamento stabile. Era sulla punta della mia lingua! Ecco un altro link: http://planetmath.org/encyclopedia/StableSortingAlgorithm.html

+2

Non è che la cosa che viene cercata qui di solito ha appena chiamato un algoritmo di ordinamento stabile? Vedi: http://en.wikipedia.org/wiki/Sorting_algorithm#Stability –

risposta

16

Dopo aver chiesto in giro, mi è stata data una spiegazione soddisfacente:

Risposta breve: Volete SortBy[list, {f}] ottenere una sorta stabile.

Risposta lunga:

SortBy[list, f] lista sorta nell'ordine determinato applicando f ad ogni elemento della lista, rompendo i legami con l'ordinamento canonico spiegato sotto Sort. (Questa è la seconda nota "Ulteriori informazioni" documentata nello documentation for SortBy.)

SortBy[list, {f, g}] interrompe i legami utilizzando l'ordine determinato applicando g a ciascun elemento.

Si noti che SortBy[list, f] corrisponde a SortBy[list, {f, Identity}].

SortBy[list, {f}] fa nessuna rottura cravatta (e dà una sorta stabile), che è ciò che si desidera:

In[13]:= SortBy[{19, 301, 201, 502, 501, 101, 300}, {Mod[#, 10] &}] 

Out[13]= {300, 301, 201, 501, 101, 502, 19} 

Infine, la soluzione di Sakra SortBy[list, {f, tie++ &}] è effettivamente equivalente a SortBy[list, {f}].

+0

Wow, grazie, Andrew! Ho letto questo ed è stato come "hai appena chiesto in giro dove lavori, Wolfram Research?" Poi ho cliccato sul tuo nome e ho visto che davvero lo fai. :) Sono continuamente stupito dai livelli di competenza che attira StackOverflow. Grazie mille per essere qui! – dreeves

2

Questo sembra funzionare:

stableSortBy[list_, f_] := 
    SortBy[MapIndexed[List, list], {[email protected][#], Last[#]}&][[All,1]] 

ma ora vedo rosettacode offre un modo molto più piacevole per farlo:

stableSortBy[list_, f_] := list[[Ordering[f /@ list]]] 

Così Ordinare è la chiave! Sembra che la documentazione di Mathematica non menzioni questa differenza a volte importante Ordina e ordina.

+1

Questo approccio è chiamato l'idioma "decora-ordina-non-decora" nei cerchi Lisp e anche se 'GatherBy' può essere l'approccio migliore per l'ordinamento stabile in in particolare, è un trucco straordinariamente utile in Mathematica. – Pillsy

+1

Sarei curioso di sapere come si sovrappone a 'GatherBy' in termini di velocità, e dipenderebbe dal modo in cui le liste vengono implementate internamente. Il mio problema è che "GatherBy" potrebbe toccare solo ogni elemento dell'elenco una volta, mentre la soluzione "Ordering" richiederebbe almeno due volte: una volta per l'ordinamento e una volta per il riordino degli elementi. Per le piccole liste, potrebbe non avere importanza. Ma, senza farlo davvero, ho il sospetto che per le liste più lunghe 'GatherBy' darà prestazioni superiori. – rcollyer

+0

PS: Penso che questo sia tutto controverso alla luce della risposta accettata. – dreeves

6

Fa Gather Fai quello che vuoi?

Flatten[GatherBy[{301, 201, 502, 501, 101}, Mod[#, 10] &]] 
+1

Oh, probabilmente sì, grazie! Non avevo pensato di usare GatherBy. Sono propenso ad andare con la soluzione di ordinazione. Se sei curioso e vuoi confrontare le soluzioni fino ad ora con i test di temporizzazione, renderò la tua la risposta accettata. (Oh, un problema con la tua soluzione: dovresti fare Flatten [..., 1] altrimenti se gli elementi fossero effettivamente elenchi allora il Flatten li rovinerebbe.) – dreeves

+0

PS: Penso che questo sia ora discutibile alla luce della risposta accettata . – dreeves

4

C'è una variante di SortBy che rompe i legami utilizzando le funzioni di ordinamento aggiuntive:

SortBy[list,{f1, f2, ...}]

Contando legami si può quindi ottenere un ordinamento stabile:

Module[{tie = 0}, 
SortBy[{19, 301, 201, 502, 501, 101, 300}, {Mod[#, 10] &, (tie++) &}]] 

rendimenti

{300, 301, 201, 501, 101, 502, 19} 
+0

Grazie, non lo sapevo! Si scopre che è ancora più semplice, come dimostra la risposta accettata. – dreeves