in O (n) time: trova il massimo elemento nel set in cui il confronto non è transitivo


21

Il titolo afferma la domanda.

Abbiamo come input un elenco di elementi, che possiamo confrontare (determinare quale è il più grande ). Nessun elemento può essere uguale.

Punti chiave:

  1. Il confronto non è transitivo (pensa alle forbici di carta rock): questo può essere vero: A> B, B> C, C> A (nota che questo non è un input valido poiché non esiste una risposta valida qui, sto solo descrivendo cosa " confronto non transitivo "significa)
  2. Ogni array di input avrà una risposta garantita
  3. maggiore indica che l'elemento deve essere maggiore di ogni altro elemento
  4. La proprietà Converse vale a dire A> B implica che B <A

Esempio:

Input: [A,B,C,D]
A > B, B > C, C > A
D > A, D > B, D > C
Output: D

Non riesco a capire un modo per farlo in O (n) tempo, la mia migliore soluzione è O (n ^ 2).

Sono bloccato su ogni approccio a causa del fatto che per essere sicuro di una risposta, l'elemento deve essere esplicitamente confrontato con ogni altro elemento, per dimostrare che è davvero la risposta (perché il confronto non è transitivo).

Questo esclude l'uso di un heap, l'ordinamento, ecc.


8
Non è chiaro come sarebbe definito "il più grande elemento"? Ad esempio, quale elemento è il più grande se ? Hai altre regole di confronto? A>B,B>C,C>A
fade2black,

6
Non riesco a immaginare come selezioneremmo il massimo elemento in un set che non è almeno parzialmente ordinato. Si prega di vedere la definizione del massimo e del minimo elemento. La mancanza di transitività esclude l'ordine parziale.
fade2black,

3
@ fade2black Perché mi colleghi a un'altra definizione di "più grande". Sto esplicitamente affermando la definizione di great per il contesto di questa domanda. Il più grande significa, l'elemento è maggiore di ogni altro elemento. Nessun elemento è uguale. Questo è tutto ciò che c'è da fare. Non è chiaro?
James Wierzba,

2
Il tuo ultimo esempio con A, B, C, D sarebbe utile per capire la tua domanda se l'hai inclusa nel tuo PO.
fade2black,

3
Un membro del team del compilatore C # era solito porre questa domanda come un'intervista; è rilevante perché in C #, l'algoritmo di risoluzione del sovraccarico deve scegliere il miglior membro univoco di un set dato una relazione di "betterness" che di solito è, ma non necessariamente transitiva. (O dare una risposta adeguata se non esiste un membro così unico e unico; i legami sono possibili.) Anche se sono riuscito a rispondere correttamente, non ho mai pensato che fosse una domanda di intervista particolarmente buona poiché si basa su un'intuizione "aha" per ottenere il algoritmo lineare.
Eric Lippert,

Risposte:


38

L'algoritmo standard per trovare un massimo funziona ancora. Inizia con e vai oltre gli elementi, se vedi un valore più grande, aggiorna il massimo per essere quel valore. La ragione per cui funziona è che ogni elemento che hai saltato è più piccolo di almeno un elemento e non può quindi essere il massimo.a1

Per essere chiari, per "algoritmo standard" intendo quanto segue:

max <- a_1
for i=2 to n
   if a_i > max
      max <- a_i
output max

Per completezza, discuterò qui le questioni sollevate nei commenti. L'impostazione nella discussione sopra sta trovando un massimo relativo ad una relazione anti simmetrica , dove a i è un massimo se per tutti j i abbiamo un i > a j . L'algoritmo sopra funziona supponendo che esista un massimo, tuttavia se questo non è noto, è possibile utilizzarlo per verificare l'esistenza di un massimo (verificare se l'elemento restituito è effettivamente maggiore di tutti gli altri elementi, questo è menzionato nel commento di Chi e nella risposta di Ilmari Karonen).<aijiai>aj

Se non è necessariamente anti simmetrico, allora l'algoritmo sopra fallisce (come Emil menzionato nei commenti). Se < è una relazione arbitraria (cioè stiamo rilassando sia la transitività sia l'anti simmetria), allora non è difficile dimostrare che non è possibile trovare un massimo nel tempo lineare. Indichiamo con # un i il numero di volte in cui un io partecipai in una query, si definisce un rapporto contraddittorio, in modo che il massimo non può essere rivelato senza query abbastanza. Data la query a i > ? a j , rispondi a i > a j se # a i<<#aiaiai>?ajai>aj e a i < a j in caso contrario. Se il numero di query è o ( n 2 ) , non è stato ancora visto un massimo e può essere impostato come uno degli elementi nel set.#ai<n1ai<ajo(n2)


1
@JamesWierzba (penso) intende solo che un elemento "saltato" è uno che non è maggiore del tuo attuale massimo. Considera l'algoritmo standard: controlli ogni valore nel tuo elenco rispetto al massimo corrente. Hai detto che c'è un elemento fondamentale in ogni elenco. Ad un certo punto, lo confronterai con il tuo massimo attuale e, dato che è più grande, quel valore diventa il tuo nuovo massimo. Poiché questo valore è maggiore di tutto il resto nell'elenco, non troverai mai un elemento maggiore e il tuo valore più grande non verrà sostituito. Dopo i tuoi nconfronti, l'attuale massimo deve essere la risposta
Lord Farquaad,

1
Modificato per chiarezza, questo algoritmo non presuppone la transitività. Se ritieni che sia difficile da credere, segui i dettagli della prova di correttezza (supponi ai fini della contraddizione che il valore restituito non sia il massimo e usa l'idea del primo paragrafo).
Ariel,

7
Questo si basa sul presupposto 2 nella domanda: c'è sempre un massimo nell'array. Se così non fosse, maxpotrebbe essere solo il massimo di un subarray. Tuttavia, anche senza ipotesi 2, si può trovare un massimo provvisorio e quindi verificarlo sull'intero array usando una seconda scansione, all'interno del limite O (n).
chi,

7
Questa risposta presuppone che e B > A non possano essere mantenuti contemporaneamente. Per quanto posso vedere, questo non è escluso nella domanda. A>BB>A
Emil Jeřábek sostiene Monica il

4
@ oconnor0 Ciò non segue. Per un esempio concreto, assumere A> B, A> C, B> A e C> B. Quindi A è maggiore di qualsiasi altro elemento nell'insieme (ed è l'unico elemento con questa proprietà), ma se gli elementi sono incontrato nell'ordine A, B, C, l'algoritmo produrrà C.
Emil Jeřábek supporta Monica il

24

Come osserva Ariel , l'algoritmo standard di massima ricerca riportato di seguito:

def find_maximum(a):
    m = a[0]
    for x in a:
        if x > m: m = x
    return m

funzionerà infatti senza modifiche purché:

  • qualsiasi coppia di elementi può essere confrontata, e
  • si garantisce che l'input contenga un elemento massimo, ovvero un elemento che è coppia maggiore di qualsiasi altro elemento nell'input.

(Il primo presupposto sopra può effettivamente essere rilassato, anche senza dover modificare l'algoritmo, purché ipotizziamo che l'elemento massimo sia comparabile con ogni altro elemento e che x > ysia sempre falso se gli elementi xe ysono incomparabili).

In particolare, afferma che:

[…] Per essere certi di una risposta, l'elemento deve essere esplicitamente confrontato con ogni altro elemento (perché il confronto non è transitivo).

non è vero sotto i presupposti di cui sopra. Infatti, per dimostrare che l'algoritmo sopra troverà sempre l'elemento massimo, è sufficiente osservare che:

  1. poiché il ciclo scorre su tutti gli elementi di input, in alcune iterazioni xsarà l'elemento massimo;
  2. poiché l'elemento massimo è di coppia maggiore di ogni altro elemento, ne consegue che, al termine di tale iterazione, msarà l'elemento massimo; e
  3. poiché nessun altro elemento può essere più grande della coppia dell'elemento massimo, ne consegue che mnon cambierà in nessuna delle iterazioni successive.

Pertanto, alla fine del ciclo, msarà sempre l'elemento massimo, se l'input ne contiene uno.


Ps. Se l'ingresso non non necessariamente contengono sempre un elemento massimale, quindi verificare che infatti sarà davvero richiederà testare la risposta candidato contro ogni altro elemento per verificare che sia realmente massima. Tuttavia, possiamo ancora farlo in O ( n ) tempo dopo aver eseguito l'algoritmo di ricerca massima sopra:

def find_maximum_if_any(a):
    # step 1: find the maximum, if one exists
    m = a[0]
    for x in a:
        if x > m: m = x

    # step 2: verify that the element we found is indeed maximal
    for x in a:
        if x > m: return None  # the input contains no maximal element
    return m  # yes, m is a maximal element

(Suppongo qui che la relazione >sia irreflessiva, cioè nessun elemento può essere maggiore di se stesso. In caso contrario, il confronto x > mnel passaggio 2 dovrebbe essere sostituito con x ≠ m and x > m, dove indica il confronto di identità. Oppure potremmo semplicemente applicare l'ottimizzazione indicato di seguito.)

Per dimostrare la correttezza di questa variazione dell'algoritmo, considerare i due possibili casi:

  • Se l'input contiene un elemento massimo, il passaggio 1 lo troverà (come mostrato sopra) e il passaggio 2 lo confermerà.
  • Se l'ingresso lo fa non contiene un elemento massimo, il passaggio 1 finirà per selezionare alcuni elementi arbitrari come m. Non importa quale sia, dal momento che in ogni caso non sarà massimo, quindi il passaggio 2 lo rileverà e tornerà None.

Se abbiamo memorizzato l'indice mnella matrice di ingresso a, si potesse davvero passo ottimizzare 2 per controllare solo quegli elementi che vengono prima ma a, dal momento che tutti gli elementi successivi sono già stati rispetto a mal punto 1. Ma questa ottimizzazione non cambia il tempo di complessità asintotica dell'algoritmo, che è ancora O ( n ).


3
In effetti l'OP salta molti dettagli. Ad esempio, non si dice nulla sulla riflessività della relazione, quindi se non è riflessiva non if x > m:è definita.
fade2black,

4

"maggiore significa che l'elemento deve essere maggiore di ogni altro elemento" è un grande suggerimento su come farlo in .O(n)

Se si passa attraverso l'elenco di elementi di confronto, qualsiasi elemento che "perde" un confronto può essere immediatamente scartato poiché, per essere il più grande, deve essere maggiore di TUTTI gli altri elementi, quindi la singola perdita lo squalifica.

n-1 confronti e finire con il massimo elemento come risultato del tuo ultimo confronto, scartando un perdente ogni volta.

Questa soluzione è abilitata da una sottigliezza: "Nessun elemento può essere uguale" combinato con il fatto che ci sarà sempre un elemento maggiore. Se mappiamo le relazioni delle vittorie come un grafico diretto, è chiaro che possiamo raggiungere il massimo elemento semplicemente seguendo le vincite.


1
" Grafico diretto aciclico " è il modello sbagliato: dovrebbe invece essere " torneo ". I cicli sono consentiti ed è fondamentale che ogni bordo esista esattamente in una direzione.
Peter Taylor,

@PeterTaylor hai perfettamente ragione, stavo solo pensando alle vittorie che portano all'elemento "più grande", le altre vincite sono meno rilevanti ma potrebbero essere percorse sulla strada della scoperta del più grande, quindi hai ragione che possano " essere scontato
Danikov,

3

Presumo che la relazione antisimmetrica per almeno un singolo elemento (che garantisca l'esistenza del più grande elemento), altrimenti il ​​compito è impossibile. Se tutti gli elementi dell'insieme finito sono comparabili, la normale procedura di ricerca del massimo funziona.

Se alcuni elementi non sono comparabili, funzionerà la seguente procedura

max = nil
For i=1 to n
   if max is nil then
      max = a[i]
   if max and a[i] are not comparable then
      max = nil
   else if max < a[i] then
      max = a[i]
End

A,B,C,D

A>B,B>C,C>A
D>A,D>B,D>C


i=1: max=A
i=2: max=AA>B
i=3: max=CA<C
i=4: max=DD>C

m>aa<mamm<aamam


Non credo else ifsia necessario il primo . Può essere attivato se maxè il massimo e se il massimo non è stato ancora raggiunto, non importa quale sia il valore di max.
rici,

Sì, quello è il primo. L'altro è il secondo :)
rici,

Vuoi dire lasciare ifs senza elses? È solo un'abitudine: con elses non ci confrontiamo nemmeno. :)
fade2black

1
Non sarebbe più semplice inizializzare maxqualsiasi elemento dell'elenco e, all'interno del ciclo, fare if max and a[i] are comparable and max < a[i] then max = a[i](dove la prima parte della condizione potrebbe essere omessa se assumiamo che il tentativo di confrontare due elementi incomparabili produca sempre falso)?
Ilmari Karonen,

1
@badallen l'OP presuppone che ci sia sempre l'elemento più grande. Nel tuo esempio non c'è elemento più grande.
fade2black,

2

A<BB<A

A1...AnAi<Aj

n2 query.)

Ai<Ajj

j0Ai<Aj0ijijAi<AjiijAij<Ajj0ij

Spero che questo sia in qualche modo comprensibile. Sentiti libero di chiedere nei commenti o modificare.

L'idea di base è che non puoi raccogliere alcuna informazione sugli elementi rimanenti da quelli che già conosci se permetti una relazione completamente arbitraria.

A<Ann2n


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.