Eu quero provar ou refutar a existência de um algoritmo que, dada uma matriz de números inteiros, encontre três índices e modo que e (ou descobre que não existe esse triplo) no tempo linear.i , j k i < j < k A [ i ] < A [ j ] < A [ k ]
Esta não é uma questão de lição de casa; Eu vi isso em um fórum de programação enquadrado como "tente implementar um algoritmo desse tipo". Suspeito que seja impossível após várias experiências. Minha intuição me diz isso, mas isso realmente não conta para nada.
Eu gostaria de provar isso formalmente. Como você faz isso? Idealmente, gostaria de ver uma prova apresentada passo a passo e, se você estiver inclinado, algumas explicações sobre como provar / refutar perguntas simples como esta em geral. Se ajudar, alguns exemplos:
[1,5,2,0,3] → (1,2,3)
[5,6,1,2,3] → (1,2,3)
[1,5,2,3] → (1,2,3)
[5,6,1,2,7] → (1,2,7)
[5,6,1,2,7,8] → (1,2,7)
[1,2,999,3] → (1,2,999)
[999,1,2,3] → (1,2,3)
[11,12,8,9,5,6,3,4,1,2,3] → (1,2,3)
[1,5,2,0,-5,-2,-1] → (-5,-2,-1)
Eu supunha que alguém poderia iterar sobre , e cada vez que existe um (nosso atual , ou seja), fazemos um novo triplo e o empurramos para uma matriz. Continuamos avançando e comparando cada triplo até que um de nossos triplos esteja completo. Então, é como , ! Mas acho que isso é mais complexo do que mero pois o número de triplos em nossa matriz tripla corresponderia, na pior das hipóteses, ao tamanho da lista de entradas.i < j j[1,5,2,0,-5,-2,-1] → 1..2.. -5.. -2.. -1
[1,5,2,0,-5,-2,3,-1] → 1..2.. -5.. -2.. 3
fonte
Respostas:
Essa é a variação do maior problema de subsequência crescente ; esta é a solução apresentada na Wikipedia usando duas matrizes auxiliares e :PM P
Esse algoritmo é executado no pior dos casos . Seu problema é um caso especial que permite retornar quando que empurra o tempo de execução para porque a pesquisa binária é executada apenas em matrizes de comprimento no máximo duas, o que é, portanto, no tempo em oposição a no caso geral.L = 3 O ( n ) O ( 1 ) Θ ( log n )Θ(nlogn) L=3 O(n) O(1) Θ(logn)
Considere o pseudo-código modificado:
fonte
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<j A[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] i A[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<⋯<im A (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+1 k=j+1 A[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] i A[i]<A[j] k A[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.i k
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.i j k i
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) k i (i,j) (i,j) i′>j A[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.
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) ouNone
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 ouNone
se essa sequência não existir.Quando
return (index2[0], index2[1], i)
executado, temosvalue2[0] < value[1]
(isso é invariávelvalue2
) evalue[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 ouvalue1
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 quevalue1
; portanto, o último elemento de qualquer subsequência desse tipo, adicionado avalue2
, formaria uma subsequência crescente de comprimento 3: como também temos o invariante quevalue2
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.
fonte
Existe um algoritmo de tempo , que usa espaço .O ( n )O(n) O(n)
Primeiro, percorra a matriz da esquerda para a direita, mantendo uma pilha e uma matriz auxiliar que informa para cada elemento o índice de um elemento maior que ele e à direita dele.
Inicialmente, a pilha está vazia e o array aux contém todos os s.−1
Sempre que considerar um novo elemento na matriz, se esse elemento for maior que o elemento superior da pilha, você o retirará da pilha e configurará o elemento auxiliar da matriz correspondente à parte superior para ter o índice do novo elemento em consideração.
Continue retirando os elementos da pilha e configurando o índice correspondente, enquanto o elemento atual é maior. Quando a parte superior tiver um elemento que não seja menor (ou ficar vazio), empurre o elemento atual para a pilha e prossiga para o próximo elemento da matriz, repetindo a etapa acima.
Faça outra passagem (e outra matriz auxiliar), mas indo da direita para a esquerda.
Faça um passe pelas matrizes aux e escolha uma posição em que as duas entradas correspondentes na matriz não sejam .−1
Como você considera cada elemento da matriz apenas um número constante de vezes, esse é o tempo .O(n)
Eu acredito que você também pode se livrar das pilhas (considerando "máximos de sufixo" e "mínimos de prefixo"), mas as pilhas fornecem os elementos mais próximos. Portanto, se você quiser minimizar o say , poderá usar as pilhas.k−i
O pseudo-código para a primeira passagem pode ser assim:
fonte