Algoritmo per abbinare i numeri con il numero minimo di mosse


11

Questa è una sorta di domanda a distanza di modifica ed è molto semplice. Sono solo un cervello morto su questo argomento e non riesco a capirlo finora.


Data una serie di numeri, ad es

[3, 1, 1, 1]

Come si trasformerebbero tutti i numeri in modo più efficiente nello stesso numero, con il numero minimo di "mosse"? Con "mossa" si intende aggiungere o rimuovere uno da un numero.

Nell'esempio sopra, le mosse più efficienti sarebbero:

[1, 1, 1, 1]

Ciò richiederebbe 2 mosse, riducendo il primo numero due volte.

Non riesco a capire il modo migliore per scoprirlo, dati array molto più grandi di centinaia di numeri.

Inizialmente ho provato a calcolare il numero medio arrotondato (somma di tutto diviso per lunghezza) e quindi a ridurli alla media calcolata, ma l'esempio sopra ha rotto questo, richiedendo 4 mosse anziché 2.

Suppongo di poter capire:

  1. La media,
  2. Il modo,
  3. La mediana

e ottieni la distanza di modifica di ciascuno di essi, scegliendo la distanza minima. Tuttavia, non sono sicuro che ciò sia corretto in ogni singola istanza. Come posso sapere?


Se il dominio è limitato, puoi provare tutte le possibilità da min a max. Altrimenti potresti provare a usare la modalità o la mediana.
Bartosz Przybylski,

Grazie @Bartek. Sembra che provare tutte le possibilità sarebbe tremendamente inefficiente se si trattano di centinaia o migliaia di numeri. Controllerò la modalità / mediana. Ma questi sono certi di produrre risultati in ogni caso? Questa è la mia domanda principale. Sto cercando un algoritmo certo ed efficiente.
tre

Il numero deve essere nell'insieme dei numeri o può essere un numero intero?
TCSGrad,

@TCSGrad Può essere qualsiasi numero intero, ma ovviamente dovresti sceglierne uno compreso tra il numero minimo e massimo. In questo caso, 1, 2 o 3. 3
1414

Risposte:


10

La risposta è prendere la mediana. Una delle proprietà della mediana è che minimizza la distanza L1 da ciascun elemento. (Per dare un senso all'articolo di Wikipedia, prendi la distribuzione di probabilità come distribuzione uniforme sulle tue serie originali di numeri).

Questo è l'algoritmo che risolve il problema (originariamente scritto da dc2 ):

function median(arr) {
  arr.sort(function(a, b) { return a - b; });
  var half = floor(arr.length/2);
  if ( arr.length % 2 ) {
    return arr[half];
  } else {
    return (arr[half-1] + arr[half]) / 2.0;
  }
}

function minl1(arr) {
  var moves = 0;
  var mdn = median(arr);
  for ( var i = 0; i < arr.length; ++i ) {
    moves += Math.abs(mdn - arr[i]);
  }
  return moves;
}

minl1([3, 1, 1, 1]); // -> 2

Sì, lo ha fatto. Divertente come funziona. Non sembra che la mediana lo farebbe, ma ehi. Molte grazie.
tre

1
Vedi la mia risposta per una prova.
Yuval Filmus,

@ dc2: non puoi "assicurarti" "provandolo".
Raffaello

1
Solo per notare: puoi calcolare il tempo mediano O (n)
Bartosz Przybylski

1
@Raphael Va bene includere il codice OP in qualche altra risposta, senza riferimento a OP?
thefourtheye

10

x1,,xnm

δ(m)=i=1n|mxi|.
δ(m+1)δ(m)
δ(m+1)δ(m)=i=1n{+1mxi1m<xi=#{i:mxi}#{i:m<xi}.
m+δ(m+1)δ(m)nnx1,,xnmδ(m+1)δ(m)0ximin(δ(x1),,δ(xn))

Supponiamo inoltre che tutti siano distinti e che sia dispari. Sia la mediana di . Quindi mentre , e quindi è l'ottimale unico. Se è pari, un calcolo simile mostra che possiamo scegliere qualsiasi punto nell'intervallo che collega le mediane. Ragionamenti simili ma più elaborati mostrano che qualsiasi mediana è ottimale anche quando non sono distinti. Quindi in realtà non è necessario calcolare su tutti .xinmxiδ(m+1)δ(m)=1δ(m)δ(m1)=1mnxiδxi


Potresti averlo perso, ma questa risposta (quasi) dimostra che la mediana è la scelta ottimale.
Yuval Filmus,

1
la tua risposta è stata eccellente e l'ho votata. Sfortunatamente per me, un po 'troppo eccellente in quanto non sono molto esperto in notazione scientifica, lasciando gran parte di essa come resa agevole. Questo è il mio problema, non il tuo.
tre

5

Il problema può essere formulato come problema LP:

Dato un set di numeri , risolvi il seguente LP:n[a1,a2...an]

min|aix|

(Rimossi i vincoli su , che non erano necessari come sottolineato da Raffaello)x

Una volta risolto l'LP, otterrai un valore di corrispondente alla soluzione. Se è un numero intero, hai finito, altrimenti arrotondalo al numero intero più vicino.xx

EDIT : Come sottolineato nei commenti, la funzione obiettivo dovrebbe essere la somma delle differenze assolute. Per trasformarlo in un LP standard, possiamo riscrivere l'LP come:

minai

soggetto a:

aiaix i
aiaix i
ai,x0 i

Alla soluzione ottimale, , e possiamo ottenere il valore di dalla soluzione.xai=|aix| ix


Quindi, se ho capito bene, nel mio esempio, x sarebbe 1 - 3, e quindi troverei la distanza di modifica di 1, 2 e 3, e poi farei un minuto su quello?
tre

@ dc2: ciò ridurrebbe al minimo la somma delle distanze tra ciascun numero e , dove è il numero convergente. I vincoli assicurano che l'LP si interrompa rapidamente e non cerchi su tutti gli interi! xxx
TCSGrad

Perché sono necessari i vincoli?
Raffaello
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.