9

EDIT: Wow, molte risposte grande. Sì, sto usando questo come funzione di fitness per giudicare la qualità di un tipo eseguito da un algoritmo genetico. Così costo della valutazione è importante (ad esempio, deve essere veloce, preferibilmente O(n).)Algoritmo per aver valutato la monotonia di una matrice (cioè giudicare la "sortedness" di un array)


Come parte di un programma AI sto accarezzando, mi piacerebbe essere in grado di votare un candidato array di interi in base alla sua monotonicità, ovvero la sua "ordinata". Al momento, sto usando un euristica che calcola la più lunga ordinato, e poi divide che entro la lunghezza dell'array:

public double monotonicity(int[] array) { 
    if (array.length == 0) return 1d; 

    int longestRun = longestSortedRun(array); 
    return (double) longestRun/(double) array.length; 
} 

public int longestSortedRun(int[] array) { 

    if (array.length == 0) return 0; 

    int longestRun = 1; 
    int currentRun = 1; 

    for (int i = 1; i < array.length; i++) { 
     if (array[i] >= array[i - 1]) { 
      currentRun++; 
     } else { 
      currentRun = 1; 
     } 

     if (currentRun > longestRun) longestRun = currentRun; 
    } 

    return longestRun; 
} 

Questo è un buon inizio, ma non riesce a prendere in considerazione la possibilità di che potrebbero esserci "raggruppamenti" di sottosequenze ordinate. Es .:

{ 4, 5, 6, 0, 1, 2, 3, 7, 8, 9} 

Questo array è suddiviso in tre sottosequenze ordinate. Il mio algoritmo valuterà come solo il 40% ordinato, ma intuitivamente, dovrebbe ottenere un punteggio più alto di quello. Esiste un algoritmo standard per questo genere di cose?

+1

Anche se questo è in un contesto di programmazione, si potrebbe desiderare di chiedere questo su mathoverflow.com ... potrebbero essere più adatti a fornire una risposta che è utile. –

+1

Sarebbe di grande aiuto se ci fornissi qualche dettaglio in più sul tipo di decisioni che la tua applicazione AI farà in base alla "ordinatezza" –

+0

@ Michael Bray: in realtà è http://mathoverflow.net/. Stranamente, mathoverflow.com risolve lo stesso IP, ma non funziona qui. –

risposta

3

Mi aspetto che la scelta della funzione da utilizzare dipenda molto fortemente da cosa si intende utilizzare. In base alla tua domanda, suppongo che tu stia utilizzando un sistema genetico per creare un programma di ordinamento, e questa deve essere la funzione di classificazione. Se questo è il caso, allora la velocità di esecuzione è cruciale. Sulla base di questo, scommetto che il tuo algoritmo di sequenziamento più lungo potrebbe funzionare abbastanza bene. Sembra che dovrebbe definire abbastanza bene la forma fisica.

5

questo mi sembra un buon candidato per Levenshtein Damerau–Levenshtein distanza - il numero di swap necessari per ordinare l'array. Questo dovrebbe essere proporzionale alla distanza di ciascun oggetto da dove dovrebbe trovarsi in un array ordinato.

Ecco un semplice algoritmo di ruby ​​che somma i quadrati delle distanze. Sembra una buona misura di ordine - il risultato diventa più piccolo ogni volta che vengono scambiati due elementi fuori ordine.

ap = a.sort 
sum = 0 
a.each_index{|i| j = ap.index(a[i])-i 
    sum += (j*j) 
} 
dist = sum/(a.size*a.size) 
+1

Ma questa non è la distanza di levenshtein. La distanza di levenshtein è la distanza di modifica, il numero minimo di operazioni di modifica (inserire, eliminare e sostituire) per passare da una sequenza all'altra. – nlucaroni

+0

L'approccio generale è interessante, si potrebbe provare a scoprire quanti interventi di "scambio 2 intervalli dalla sequenza" sono necessari per ordinare l'array. Ma sospetto, in pratica è molto difficile da calcolare. –

+0

@Doc, ancora una volta, la distanza di scambio non è la distanza di levenshtein. – nlucaroni

1

Suggerirei di guardare lo Pancake Problem e la distanza di inversione delle permutazioni. Questi algoritmi sono spesso usati per trovare la distanza tra due permutazioni (l'identità e la stringa permutata). Questa misura di distanza dovrebbe tenere conto di più gruppi di valori dell'ordine e di inversioni (diminuzione monotona invece di sottosequenze crescenti). Ci sono anche approximations that are polynomial time[PDF].

Tutto dipende da cosa significa il numero e se questa funzione di distanza ha comunque senso nel contesto.

+0

Trattando questo come problema di Pancake, se l'array è ordinato decrescente, c'è solo una operazione 'flip' necessaria per ordinarlo, quindi sarà visto come 'quasi ordinato'. Sospetto che non sia ciò che l'OP vuole. –

+0

È quasi ordinato. Inoltre, ha solo detto monotonicità. Discendente o Crescente, tuttavia, mostra un'essenza di ordine. Direi che 7654321 è più ordinato quindi 4237516. Risolve il suo problema di "clumping". – nlucaroni

0

Dipende molto da cosa si intende utilizzare la misura, ma un modo semplice per farlo è alimentare l'array in un algoritmo di ordinamento standard e misurare il numero di operazioni (swap e/o confronti) necessari essere fatto per ordinare l'array.

+0

Questo molto probabilmente darà risultati * molto * diversi in base all'algoritmo utilizzato. –

+1

Ovviamente, è ovvio - sebbene qualsiasi algoritmo di ordinamento ragionevole come il mergesort o il quicksort abbia un tempo generalmente decrescente per l'input "più ordinato". –

+2

Una versione ingenua di quicksort, in cui il primo elemento di ogni subrange è considerato l'elemento di partizionamento, sarà notoriamente O (n^2) per una lista già ordinata, quindi devi stare attento a questo! Secondo Sedgewick, l'insertion sort è la soluzione migliore per una lista prevalentemente ordinata. –

2

Eccone uno che ho appena inventato.

Per ciascuna coppia di valori adiacenti, calcolare la differenza numerica tra di essi.Se il secondo è maggiore o uguale al primo, aggiungilo al totale sorted, altrimenti aggiungi al totale unsorted. Al termine, prendi il rapporto dei due.

2

Calcola le lunghezze di tutte le sottosequenze ordinate, quindi piazza e aggiungi. Se si desidera calibrare la quantità di enfasi più grande, utilizzare una potenza diversa da 2.

Non sono sicuro quale sia il modo migliore per normalizzare questo per lunghezza, magari dividerlo per lunghezza al quadrato?

0

Alcuni esperimenti con un modificatore Ratcliff & Obershelp

>>> from difflib import SequenceMatcher as sm 
>>> a = [ 4, 5, 6, 0, 1, 2, 3, 7, 8, 9 ] 
>>> c = [ 0, 1, 9, 2, 8, 3, 6, 4, 7, 5 ] 
>>> b = [ 4, 5, 6, 0, 1, 2, 3, 7, 8, 9 ] 
>>> b.sort() 
>>> s = sm(None, a, b) 
>>> s.ratio() 
0.69999999999999996 
>>> s2 = sm(None, c, b) 
>>> s2.ratio() 
0.29999999999999999 

Quindi tipo di fa quello che deve. Non sono troppo sicuro di come provarlo.

2

Quello che stai probabilmente cercando è Kendall Tau. È una funzione uno a uno della distanza del tipo di bolla tra due array. Per verificare se un array è "quasi ordinato", calcola il suo Kendall Tau contro un array ordinato.

1

Ho lo stesso problema (punteggio di monotonicità) e suggerisco di provare Longest Increasing Subsequence. L'algoritmo più efficiente eseguito in O(n log n), non così male.

Prendendo esempio dalla domanda, la sequenza crescente più lunga di {4, 5, 6, 0, 1, 2, 3, 7, 8, 9} è {0, 1, 2, 3, 7, 8, 9} (lunghezza di 7). Forse è migliore (70%) rispetto all'algoritmo di elaborazione più lunga.

0

Come contare il numero di passaggi con valore crescente rispetto al numero di passaggi totali. Questo è O(n).

Problemi correlati