Algoritmos eficientes para problemas de visibilidade vertical

18

Ao pensar em um problema, percebi que precisava criar um algoritmo eficiente para resolver a seguinte tarefa:

O problema: recebemos uma caixa quadrada bidimensional do lado n cujos lados são paralelos aos eixos. Podemos examiná-lo através do topo. No entanto, também existem m segmentos horizontais. Cada segmento tem um inteiro y coordenada x ( 0yn ) e x -coordena ( 0x1<x2n ) e pontos Connects (x1,y) e (x2,y) (veja o imagem abaixo).

Gostaríamos de saber, para cada segmento de unidade na parte superior da caixa, quão profundo podemos olhar verticalmente dentro da caixa se examinarmos esse segmento.

x{0,,n1}maxi: [x,x+1][x1,i,x2,i]yi

Exemplo: dados n=9 e m=7 segmentos localizados como na figura abaixo, o resultado é (5,5,5,3,8,3,7,8,7) . Veja como a luz profunda pode entrar na caixa.

Sete segmentos;  a parte sombreada indica a região que pode ser alcançada pela luz

Felizmente para nós, tanto n e m são bastante pequeno e podemos fazer os cálculos off-line.

O algoritmo mais fácil de resolver esse problema é a força bruta: para cada segmento, percorra toda a matriz e atualize-a sempre que necessário. No entanto, isso nos dá um não muito impressionante .O(mn)

Uma grande melhoria é usar uma árvore de segmentos capaz de maximizar valores no segmento durante a consulta e ler os valores finais. Não vou descrevê-lo mais, mas vemos que a complexidade do tempo é .O((m+n)logn)

No entanto, criei um algoritmo mais rápido:

Esboço:

  1. Classifique os segmentos em ordem decrescente de coordenada (tempo linear usando uma variação da classificação de contagem). Agora observe que, se qualquer segmento de unidade já foi coberto por qualquer segmento antes, nenhum segmento a seguir poderá limitar o feixe de luz que passa por esse segmento de unidade . Em seguida, faremos uma varredura de linha de cima para baixo na caixa.x xyxx

  2. Agora, vamos introduzir algumas definições: o segmento da unidade é um segmento horizontal imaginário na varredura cujos coordenadas são números inteiros e cujo comprimento é 1. Cada segmento durante o processo de varredura pode ser desmarcado (ou seja, um feixe de luz saindo do o topo da caixa pode alcançar esse segmento) ou marcado (caso oposto). Considere um segmento de unidade com , sempre desmarcado. Vamos também introduzir os conjuntos . Cada conjunto conterá uma sequência inteira de segmentos de unidades marcados consecutivos (se houver algum) com a seguinte marca não marcadax x x 1 = n x 2 = n + 1 S 0 = { 0 } , S 1 = { 1 } , , S n = { n } xxxxx1=nx2=n+1S0={0},S1={1},,Sn={n} x segmento.

  3. Precisamos de uma estrutura de dados capaz de operar nesses segmentos e conjuntos com eficiência. Usaremos uma estrutura de união de busca estendida por um campo que contém o índice máximo do segmento de unidades (índice do segmento não marcado ).x

  4. Agora podemos lidar com os segmentos com eficiência. Digamos que agora estamos considerando o ésimo segmento em ordem (chame de "consulta"), que começa em e termina em . Precisamos encontrar todos os segmentos não marcados da unidade que estão contidos no ésimo segmento (esses são exatamente os segmentos nos quais o feixe de luz terminará). Vamos fazer o seguinte: primeiro, encontramos o primeiro segmento não marcado dentro da consulta ( encontre o representante do conjunto no qual está contido e obtenha o índice máximo desse conjunto, que é o segmento não marcado por definição ). Então este índicex 1 x 2 x i x 1 x y x x + 1 x ix1x2 xix1xestá dentro da consulta, adicioná-lo para o resultado (o resultado para este segmento é ) e marcar este índice ( União conjuntos contendo e ). Em seguida, repita esse procedimento até encontrarmos todos os segmentos não marcados , ou seja, a próxima consulta Find nos fornece o índice .yxx+1xx2

Observe que cada operação de união de localização será realizada em apenas dois casos: ou começamos a considerar um segmento (que pode acontecer vezes) ou apenas marcamos um segmento de unidade (isso pode acontecer vezes). Assim, a complexidade geral é ( é uma função inversa de Ackermann ). Se algo não estiver claro, posso elaborar mais sobre isso. Talvez eu possa adicionar algumas fotos se tiver algum tempo.x n O ( ( n + m ) α ( n ) ) αmxnO((n+m)α(n))α

Agora cheguei ao "muro". Não consigo criar um algoritmo linear, embora pareça que deveria haver um. Então, eu tenho duas perguntas:

  • Existe um algoritmo de tempo linear (ou seja, ) resolvendo o problema de visibilidade do segmento horizontal?O(n+m)
  • Caso contrário, qual é a prova de que o problema de visibilidade é ?ω(n+m)
mnbvmar
fonte
Com que rapidez você classifica seus m segmentos?
babou
@babou, a pergunta especifica a classificação por contagem, que, como diz a pergunta, é executada em tempo linear ("tempo linear usando uma variação da classificação por contagem").
DW
Você tentou varrer da esquerda para a direita? Tudo o que você precisa é classificar e nas etapas e para caminhar para a direita. Então, no total . x 2 O ( m ) O ( m ) O ( m )x1x2O(m)O(m)O(m)
invalid_id
@invalid_id Sim, eu tentei. No entanto, nesse caso, a linha de varredura deve reagir adequadamente quando atingir o início do segmento (em outras palavras, adicionar o número igual à coordenada do segmento ao multiset), atingir o final do segmento (remover uma ocorrência de -coordenate) e produz o segmento ativo mais alto (valor máximo de saída no multiset). Não ouvi nenhuma estrutura de dados que nos permita fazer isso em tempo constante (amortizado). yyy
Mnbvmar
@mnbvmar talvez seja uma sugestão idiota, mas e quanto a uma matriz de tamanho , você varre e interrompe cada célula . Para cada célula que você conhece max é possível inseri-la na matriz, além disso, você pode acompanhar o máximo geral com uma variável. O ( n ) ynO(n)y
invalid_id

Respostas:

1
  1. Primeiro tipo tanto e coordenadas das linhas nos dois agrupamentos separados e . x 2 A B O ( m )x1x2ABO(m)
  2. Também mantemos um tamanho de matriz de bits auxiliar para acompanhar os segmentos ativos.n
  3. Comece a varrer da esquerda para a direita:
  4. para(i=0,i<n,i++)
  5. {
  6. ..se com valory C O ( 1 )x1=iyc O(1)
  7. .. {
  8. .... encontrar ( )max
  9. .... armazenar ( )O ( 1 )maxO(1)
  10. ..}
  11. ..se com valory C O ( 1 )x2=iyc O(1)
  12. .. {
  13. .... encontrar ( )max
  14. .... armazenar ( )O ( 1 )maxO(1)
  15. ..}
  16. }

find ( ) pode ser implementado usando uma matriz de bits com bits. Agora, sempre que removermos ou adicionarmos um elemento a , podemos atualizar esse número inteiro, definindo um pouco como true ou false, respectivamente. Agora você tem duas opções, dependendo da linguagem de programação usada e a suposição é relativamente pequena, ou seja, menor que o que é pelo menos 64 bits ou uma quantidade fixa desses números inteiros:n L n l o n g l o n g i n tmaxnLnlonglongint

  • Obter o bit menos significativo em tempo constante é suportado por alguns hardwares e gcc.
  • Ao converter em um número inteiro você obterá o máximo (não diretamente, mas você pode derivá-lo).O ( 1 )LO(1)

Eu sei que isso é um hack porque assume um valor máximo para e, portanto, pode ser visto como uma constante então ...nnn

ID Inválido
fonte
Pelo que vejo, supondo que você tenha um processador x86 de 64 bits, você pode lidar apenas com . E se estiver na ordem de milhões? nn64n
Mnbvmar #
Então você precisará de mais números inteiros. Com dois números inteiros, você pode manipular até 128, etc. Portanto, a etapa de localização máxima de fica oculta no número de inteiros necessários, que você ainda pode otimizar se for pequeno. Você mencionou na sua pergunta que é relativamente pequeno, então acho que não é da ordem de milhões. A propósito, long long int é sempre de pelo menos 64 bits por definição, mesmo em um processador de 32 bits. O ( m ) m nnO(m)mn
invalid_id
Claro que é verdade, o padrão C ++ define long long intcomo pelo menos o tipo inteiro de 64 bits. No entanto, não será que se for enorme e denotarmos o tamanho da palavra como (geralmente ), cada um levará tempo ? Em seguida, terminaríamos com total . w w = 64 O ( nnww=64findO(mnO(nw)O(mnw)
Mnbvmar 01/09
Sim, infelizmente para grandes valores de esse é o caso. Então agora eu me pergunto o quão grande será no seu caso e se é limitado. Se for realmente da ordem de milhões, esse truque não funcionará mais, mas se para valores baixos de , será rápido e praticamente . Portanto, a melhor opção de algoritmo é, como sempre, dependente da entrada. Por exemplo, para ordenação por inserção é normalmente mais rápida do que a ordenação por mesclagem, mesmo com um tempo de execução de comparação com . n c w n c O ( n + m ) n 100 O ( n 2 ) O ( n log n )nncwncO(n+m)n100O(n2)O(nlogn)
invalid_id
3
Estou confuso com a sua escolha de formatação. Você sabe que pode digitar código aqui, certo?
Raphael
0

Eu não tenho um algoritmo linear, mas este parece ser O (m log m).

Classifique os segmentos com base na primeira coordenada e altura. Isso significa que (x1, l1) sempre vem antes (x2, l2) sempre que x1 <x2. Além disso, (x1, l1) na altura y1 vem antes (x1, l2) na altura y2 sempre que y1> y2.

Para cada subconjunto com a mesma primeira coordenada, fazemos o seguinte. Seja o primeiro segmento (x1, L). Para todos os outros segmentos do subconjunto: se o segmento for maior que o primeiro, altere-o de (x1, xt) para (L, xt) e adicione-o ao subconjunto L na ordem correta. Caso contrário, solte-o. Finalmente, se o próximo subconjunto tiver uma primeira coordenada menor que L, divida (x1, L) em (x1, x2) e (x2, L). Adicione (x2, L) ao próximo subconjunto na ordem correta. Podemos fazer isso porque o primeiro segmento do subconjunto é mais alto e cobre o intervalo de (x1, L). Esse novo segmento pode ser o que cobre (L, x2), mas não saberemos isso até que olhemos para o subconjunto que tem a primeira coordenada L.

Depois de percorrermos todos os subconjuntos, teremos um conjunto de segmentos que não se sobrepõem. Para determinar qual é o valor Y para um determinado X, precisamos apenas percorrer os segmentos restantes.

Então, qual é a complexidade aqui: A classificação é O (m log m). O loop entre os subconjuntos é O (m). Uma pesquisa também é O (m).

Portanto, parece que esse algoritmo é independente de n.

Ninguém em particular
fonte