Meu gráfico é plano?

29

Sua tarefa é determinar se um gráfico é plano.

Um gráfico é plano se puder ser incorporado no plano ou, em outras palavras, se puder ser desenhado sem arestas de cruzamento.

Entrada: Você receberá um gráfico não direcionado em sua escolha dos seguintes formatos:

  • Lista de arestas, por exemplo [(0, 1), (0, 2), (0, 3)]

  • Mapa de adjacências, por exemplo {0: [1, 2, 3], 1:[0], 2:[0], 3:[0]}

  • Matriz adjacente, por exemplo [[0, 1, 1, 1], [1, 0, 0, 0], [1, 0, 0, 0], [1, 0, 0, 0]]

Os nomes dos nós podem ser números, seqüências de caracteres ou similares, mas o formato escolhido deve poder suportar um gráfico arbitrário. Nenhum código de colocação nos nomes dos nós. Não haverá auto-loops.

Escolha padrão de entrada, incluindo STDIN, argumentos de linha de comando e argumentos de função.

Saída: você deve retornar uma saída específica para todos os gráficos planares e uma saída específica diferente para todos os gráficos não planares.

Escolha padrão de saída, incluindo STDOUT, valor de retorno da função.

Exemplos:

Planar:

[]
[(0,1), (0,2), (0,3), (0,4), (0,5), (0,6)]
[(0,1), (0,2), (0,3), (1,2), (1,3), (2,3)]
[(0,2), (0,3), (0,4), (0,5), (1,2), (1,3), (1,4), (1,5), (2,3),
 (2,5), (3,4), (4,5)]

Não Planar:

[(0,1), (0,2), (0,3), (0,4), (1,2), (1,3), (1,4), (2,3), (2,4), (3,4)]
[(0,3), (0,4), (0,5), (1,3), (1,4), (1,5), (2,3), (2,4), (2,5)]
[(0,3), (0,4), (0,6), (1,3), (1,4), (1,5), (2,3), (2,4), (2,5), (5,6), 
 (7,8), (8,9), (7,9)]

Qualquer função que execute explicitamente o teste de planaridade ou faça referência específica a incorporamentos planares não é permitida.

Isso é código de golfe. Que ganhe o código mais curto.

isaacg
fonte
Boa pergunta !
É ótimo que esse seja um problema clássico e ainda existam várias abordagens possíveis, incluindo as que não são usadas no código para fins usuais.
lirtosiast
Um caso de teste para um gráfico não conectado com um componente conectado plano e um não-plano seria bom.
Peter Taylor
@ PeterTaylor Claro, vou adicionar um.
Isaacg
5
@RenaeLider Desculpe, mas eu não entendo. A questão não tem nada a ver com números de ponto flutuante - nem sequer usa números, na verdade, apenas rótulos.
Isaacg

Respostas:

14

Mathematica, 201 bytes

f@g_:=EdgeCount@g<9||!(h=g~IsomorphicGraphQ~CompleteGraph@#&)@5&&!h@{3,3}&&And@@(f@EdgeDelete[g,#]&&f@EdgeContract[g,#]&/@EdgeList@g);And@@(f@Subgraph[g,#]&/@ConnectedComponents[g=Graph[#<->#2&@@@#]])&

Isso avalia uma função sem nome, que recebe uma lista de arestas como

{{0, 3}, {0, 4}, {0, 5}, {1, 3}, {1, 4}, {1, 5}, {2, 3}, {2, 4}, {2, 5}}

Essa é uma abordagem recursiva terrivelmente ineficiente, baseada no teorema de Wagner :

Um gráfico finito é plano se, e somente se, não tiver K 5 ou K 3,3 como menor.

Aqui, K 5 é o gráfico completo com 5 vértices e K 3,3 é o gráfico bipartido completo com 3 vértices em cada grupo. Um gráfico A é um menor do gráfico B se puder ser obtido de B por uma sequência de deleções e contrações de arestas.

Portanto, esse código apenas verifica se o gráfico é isomórfico para K 5 ou K 3,3 e, caso contrário, ele se chama recursivamente uma vez para cada possível exclusão ou contração da aresta.

O problema é que excluir ou contrair arestas em um componente de um gráfico desconectado nunca se livrará de todos os vértices existentes, portanto nunca encontraremos os isomorfismos desejados. Portanto, aplicamos essa pesquisa a cada componente conectado do gráfico de entrada separadamente.

Isso funciona muito rápido para as entradas fornecidas, mas se você adicionar mais algumas arestas, rapidamente demorará catastroficamente (e também consumirá muita memória).

Aqui está uma versão recuada de f(a função sem nome depois de gerar um objeto gráfico a partir da entrada:

f@g_ := 
  EdgeCount@g < 9 || 
  ! (h = g~IsomorphicGraphQ~CompleteGraph@# &)@5 && 
  ! h@{3, 3} &&
  And @@ (f@EdgeDelete[g, #] && f@EdgeContract[g, #] & /@ EdgeList@g)

E esta é a função sem nome que converte a entrada em um gráfico e chama fcada componente conectado:

And @@ (
  f @ Subgraph[g, #] & /@ ConnectedComponents[
    g=Graph[# <-> #2 & @@@ #]
  ]
)&

Posso salvar alguns bytes alterando a condição de finalização de EdgeCount@g<9para g==Graph@{}, mas isso aumentará significativamente o tempo de execução. O segundo caso de teste leva 30 segundos e o último ainda não foi concluído.

Martin Ender
fonte
Você pode tentar remover a função nomeada usando o #0que se refere à função pura mais interna.
precisa
@ LegionMammal978 Eu sei, mas realmente não salva nada, porque então preciso de parênteses e também preciso atribuir manualmente #a uma variável g.
Martin Ender