Uma nota sobre metodologia
Pensei um pouco sobre esse problema e cheguei a uma solução. Quando li a resposta de Saeed Amiri , percebi que o que surgiu foi uma versão especializada do algoritmo de localização de subsequência mais longa padrão para uma sequência de comprimento 3. Estou publicando o caminho para a solução, porque acho que é um exemplo interessante de solução de problemas.
A versão de dois elementos
Vamos começar pequenos: em vez de procurar três índices nos quais os elementos estão em ordem, vamos procurar dois: modo que .A [ i ] < A [ j ]i<jA[i]<A[j]
Se estiver diminuindo (por exemplo, ou equivalentemente ), então não existem tais índices. Caso contrário, há um índice tal que .A∀i<j,A[i]≥A[j]∀i,A[i]≥A[i+1]iA[i]<A[i+1]
Este caso é muito simples; vamos tentar generalizar. Isso mostra que o problema descrito não é solucionável: os índices solicitados nem sempre existem. Portanto, pediremos que o algoritmo retorne índices válidos, se existirem, ou afirme corretamente que esses índices não existem.
Apresentando o algoritmo
Usarei o termo subsequência para significar uma extração da matriz consiste em índices que podem não ser consecutivos ( com ) e corra para significar elementos consecutivos de ( ).A(A[i1],…,A[im])i1<⋯<imA(A[i],A[i+1],…,A[i+m−1])
Acabamos de ver que os índices solicitados nem sempre existem. Nossa estratégia será estudar quando os índices não existirem. Faremos isso supondo que estamos tentando encontrar os índices e ver como nossa pesquisa pode dar errado. Os casos em que a pesquisa não der errado fornecerão um algoritmo para encontrar os índices.
Com dois índices, podemos encontrar índices consecutivos. Com três índices, talvez não consigamos criar e . No entanto, podemos considerar o caso em que há uma execução de três elementos estritamente crescentes ( ) a serem resolvidos, pois é fácil reconhecer essas execuções, e veja como essa condição pode não ser atendida. Suponha que a sequência não tenha uma execução estritamente crescente de comprimento 3.j=i+1k=j+1A[i]<A[i+1]<A[i+2]
A sequência só tem execuções estritamente crescentes de comprimento 2 (que chamaremos de pares ordenados para abreviar), separadas por uma execução decrescente de comprimento pelo menos 2. Para uma execução estritamente crescente para fazer parte de uma sequência crescente de 3 elementos, deve haver um elemento anterior tal que ou um elemento posterior tal que .A[j]<A[j+1]iA[i]<A[j]kA[j+1]<A[k]
Um caso em que nem nem existe é quando cada par ordenado é inteiramente menor que o próximo. Isso não é tudo: quando os pares estão entrelaçados, precisamos compará-los com mais detalhes.ik
O elemento mais à esquerda de uma subsequência crescente precisa chegar cedo e ser pequeno. O próximo elemento precisa ser maior, mas o menor possível para encontrar um terceiro elemento maior . O primeiro elemento nem sempre é o menor elemento da sequência e nem sempre é o primeiro para o qual existe um elemento maior subsequente - às vezes há uma subsequência de 2 elementos mais baixa adiante e às vezes há uma melhor apto para o mínimo já encontrado.ijki
Indo da esquerda para a direita, tentativamente escolhemos o menor elemento como . Se encontrarmos um elemento maior mais à direita, escolhemos esse par como tentativa . Se encontrarmos um ainda maior , vencemos. O ponto principal a ser observado é que nossa escolha de e nossa escolha de são atualizadas independentemente: se tivermos um candidato e encontrarmos modo que , se torna o próximo candidato mas permanece. Somente se acharmos tal que irái(i,j)ki(i,j)(i,j)i′>jA[i′]<A[i]i′i(i,j)j′A[j′]<A[j](i′,j′) tornar-se o novo par candidato.
Declaração do algoritmo
Dado na sintaxe do Python, mas cuidado, eu não testei.
def subsequence3(A):
"""Return the indices of a subsequence of length 3, or None if there is none."""
index1 = None; value1 = None
index2 = None; value2 = None
for i in range(0,len(A)):
if index1 == None or A[i] < value1:
index1 = i; value1 = A[i]
else if A[i] == value1: pass
else if index2 == None:
index2 = (index1, i); value2 = (value1, A[i])
else if A[i] < value2[1]:
index2[1] = i; value2[1] = A[i]
else if A[i] > value2[1]:
return (index2[0], index2[1], i)
return None
Esboço de prova
index1
é o índice do mínimo da parte da matriz que já foi percorrida (se ocorrer várias vezes, reteremos a primeira ocorrência) ou None
antes de processar o primeiro elemento. index2
armazena os índices da crescente subsequência do comprimento 2 na parte já percorrida da matriz que possui o menor elemento maior ou None
se essa sequência não existir.
Quando return (index2[0], index2[1], i)
executado, temos value2[0] < value[1]
(isso é invariável value2
) e value[1] < A[i]
(óbvio do contexto). Se o loop terminar sem chamar o retorno antecipado, value1 == None
nesse caso, não haverá subsequência crescente do comprimento 2 e muito menos 3 ou value1
conterá a subsequência crescente do comprimento 2 que possui o menor elemento maior. No último caso, temos além disso o invariante de que nenhuma subsequência crescente do comprimento 3 termina mais cedo do que value1
; portanto, o último elemento de qualquer subsequência desse tipo, adicionado a value2
, formaria uma subsequência crescente de comprimento 3: como também temos o invariante que value2
não faz parte de uma subsequência crescente de comprimento 3 contida na parte já percorrida da matriz, não existe essa subsequência em toda a matriz.
Provar os invariantes mencionados acima é deixado como um exercício para o leitor.
Complexidade
Usamos memória adicional e atravessamos a matriz como um fluxo da esquerda para a direita. Executamos processamento para cada elemento, levando a um tempo de execução de .O ( 1 ) O ( n )O(1)O(1)O(n)
Prova formal
Deixado como um exercício para o leitor.