Verifique se adicionar uma aresta a um DAG resulta em um ciclo


7

No começo: é um problema de concurso de programação, mas não de um problema em andamento. Infelizmente, não posso fornecer nenhum link para esta tarefa, porque ela não está disponível publicamente. Era de um concurso de programação local polonês em 2011, organizado pela Escola de Ciência da Computação de Varsóvia.

Eu tenho um gráfico com V (1V5105) vértices sem arestas e uma lista de E (1E5105)bordas direcionadas. Noisegundo i-a aresta está sendo adicionada a um gráfico. Quero saber em que segundo haverá um ciclo em um gráfico.

A solução mais óbvia seria executar o DFS após adicionar cada borda, mas seria necessário O((V+E)2)Tempo. Outra solução seria manter o gráfico classificado topologicamente e, ao adicionar uma aresta, eu poderia colocá-lo de maneira a não perturbar a ordem topológica. Isso levaria no máximoO(V2)Tempo. Eu fiz algumas pesquisas no Google e parece que esse é o algoritmo online mais rápido.

Como conheço todas as bordas de antemão, posso usar o algoritmo offline. O algoritmo offline mais rápido que consigo pensar é na pesquisa binária. Se depoisk- o segundo segundo gráfico contém um ciclo; obviamente, ele terá um ciclo em qualquer outro segundo lk. Para que eu possa fazer pesquisa binária para encontrar as menoresk executando o DFS O(logE) vezes, cada um deles O(V+E) tempo, então a complexidade total dessa solução seria O((V+E)logE).

É bastante rápido, mas gostaria de saber se existe algum algoritmo offline mais rápido. Uma coisa que consigo pensar é atribuir a cada peso de borda igual ao momento em que essa borda foi adicionada a um gráfico. Então a tarefa seria equivalente a encontrar um ciclo com o peso máximo mínimo da borda. Não sei se isso leva a algum lugar.


2
Você pode procurar "detecção de ciclo on-line" no Google Scholar, embora meu palpite seja que esses algoritmos serão mais lentos que sua abordagem de pesquisa binária.
DW

2
Provavelmente, você pode alternar o algoritmo de exclusão de folha iterativa (Kahn's?) E excluir a borda máxima. ou seja, (1) continue excluindo nós de folhas até que você não encontre nenhum; depois (2) exclua a borda com o número mais alto e repita até ficar vazio. O último (2) é a aresta desejada.
KWillets

2
OK, encaminhamos o comentário anterior para uma resposta - demorou um pouco para procurar Kahn e ver se as etapas adicionais fazem sentido.
KWillets

Respostas:


2

Algoritmo do KWillets

O KWillets parece ter o melhor algoritmo . Basicamente, você alterna iterativamente entre as duas etapas a seguir:

  1. Para cada fonte (vértice sem aresta de saída), remova a fonte e todas as arestas que saem dela. Repita até que não haja mais fontes.

  2. Remova a aresta com o número mais alto, ou seja, a aresta mais recente (de todas as arestas restantes). Volte para a etapa 1.

Quando o gráfico estiver vazio, desfaça a última etapa 1 e a etapa 2 e o gráfico mais antigo com um ciclo. Em outras palavras, pegue a última aresta removida na última etapa 2 antes que o gráfico fique vazio, e o gráfico que contém essa aresta e todas as arestas anteriores é o primeiro que contém um ciclo.

Como sua lista de arestas já está classificada com o aumento do tempo, isso pode ser implementado em O(V+E)Tempo. Você manterá a lista de arestas em uma lista duplamente vinculada, com ponteiros entre esses nós e a aresta correspondente no gráfico. Etapa 1 requerO(1) tempo por nó ou borda excluída, se você mantiver uma lista de trabalho separada de todos os vértices de origem e sempre que excluir uma borda, verificará se isso criou um novo vértice de origem.

Pesquisa binária

É possível fazer algumas pequenas otimizações no seu método de pesquisa binária, embora elas não melhorem o tempo de execução assintótica: o tempo de execução assintótica ainda será O((V+E)logE).

Suponha que você analise o gráfico no momento ke descubra que ele contém um ciclo. Decomponha o gráfico em componentes fortemente conectados. Agora você pode excluir permanentemente todos os nós isolados (ou seja, nós que estão em um SCC de tamanho 1); eles não podem fazer parte de um ciclo. Você pode continuar a pesquisa binária a partir daqui.

Além disso, quando você se decompõe em componentes fortemente conectados, para cada componente fortemente conectado, faça o seguinte. Conte o número de nós no SCC, digamosn0deles. Classifique as arestas no SCC com o aumento do tempo. Veja a hora don0th deles, diga o tempo j0. Então a primeira vez que houver um ciclo dentro desse componente fortemente conectado deve ser o tempoj0ou mais tarde. Faça isso para cada componente fortemente conectado e realize o mais cedo possível. Então, como uma otimização, não há necessidade de considerar nenhum momento anterior à pesquisa binária.

No entanto, essas duas otimizações provavelmente não fornecerão mais do que uma pequena aceleração de fator constante, na melhor das hipóteses.


Isso é bom - Eu precisava de algum tempo para obter os detalhes juntos, mas eu sou muito ocupado ATM, e eu aprecio uma segunda opinião :)
KWillets

@KWillets, seu algoritmo é muito inteligente! Coisa boa.
DW

6

O algoritmo de Kahn é conhecido pela classificação topológica, mas também pode ser usado para encontrar componentes fortemente conectados. Eu o uso para verificação de ciclo, onde uma classificação topológica real não é necessária. Nesta variante, os nós são descartados à medida que são visitados, em vez de serem adicionados à saída, e a terminação do erro indica pelo menos um ciclo.

Ele funciona removendo os nós de raiz ou folha (tendo 0 grau in ou out 0). Do gráfico, um de cada vez, até que não sejam encontrados mais nós desse tipo. Como esse processo não pode cortar um ciclo (todos os nós de um ciclo têm indegree e outdegree> 0), ele termina com um gráfico não vazio se houver pelo menos um ciclo.

Se cortarmos a aresta máxima restante e em cada um desses pontos de terminação, podemos continuar o processo para ver se E \ e ainda contém um ciclo, e assim por diante, iterativamente, até que cortemos arestas suficientes para tornar E acíclico. O último corte de borda k é o primeiro k que torna E [1 ... k] cíclico.

O algoritmo de Kahn realiza esse processo de corte linear de maneira linear, usando uma fila de trabalho de nós a serem excluídos; ele é inicializado com as folhas do gráfico inteiro (pelo menos um deve existir, se for acíclico) e, conforme cada uma é excluída, seu (s) nó (s) pai (s) é verificado para ver se, por sua vez, se tornam folhas e, se for o caso eles são adicionados à fila de trabalho. Ao verificar a vizinhança de cada exclusão, ele funciona linearmente através do gráfico, sem a necessidade de estruturas de indexação além da fila FIFO.

Podemos fazer a mesma verificação de folga do vizinho quando cortamos a borda máxima e adicionar as folhas criadas à fila de trabalho ou, se nenhuma for criada, continuar cortando.

Para tornar o processo de corte máximo linear, precisamos encontrar a aresta máxima não excluída por meio de uma varredura descendente da matriz de arestas, mas cada varredura começa onde a anterior parou e o algoritmo faz apenas uma única passagem pela matriz inteira, de modo que a parte seja O (E) no total. No geral, acredito que esse método é O (V + E), o mesmo que o de Kahn.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.