Implementei Dijkstra's algorithmpara minha pesquisa um modelo econômico, usando Python. Em minha pesquisa, estou investigando duas funções e as diferenças entre elas. Cada função leva como entrada dois parâmetros: F(a,b)e Z(a,b).
Cada célula da matriz é definida como: $$M[a][b]=|F(a,b)-Z(a,b)|$$
O objetivo disso é encontrar o caminho de diferença mínima entre as equações que serão corretas para cada entrada a
As implementações online do algoritmo de Dijkstra usaram arestas ponderadas, enquanto eu tenho vértices ponderados.
function Dijkstra(Graph, source):
create vertex set Q
for each vertex v in Graph:
dist[v] ← INFINITY
prev[v] ← UNDEFINED
add v to Q
dist[source] ← 0
while Q is not empty:
u ← vertex in Q with min dist[u]
remove u from Q
for each neighbor v of u: // only v that are still in Q
alt ← dist[u] + length(u, v)
if alt < dist[v]:
dist[v] ← alt
prev[v] ← u
return dist[], prev[]
matriz de distância onde cada célula contém distância da fonte ao vértice (i, j)
matriz anterior onde cada célula contém seu pai. Rastreando de (98,98), posso encontrar o caminho mais curto.
MAX_DISTANCE = 99999
RANGE_ARR = [x for x in range(1, 1001)]
def dijkstra_get_min(Q, dist):
min = MAX_DISTANCE + 1
u = None
for vertex in Q:
if dist[vertex[0], vertex[1]] <= min:
min = dist[vertex[0], vertex[1]]
u = vertex
return u
def dijkstra(graph, src=(0, 0)):
dist = np.array([np.array([0 for x in RANGE_ARR], dtype=float) for y in RANGE_ARR])
prev = np.array([np.array([(0, 0) for x in RANGE_ARR], dtype='i,i') for y in RANGE_ARR])
Q = []
for i in RANGE_ARR_0:
for j in RANGE_ARR_0:
dist[i, j] = MAX_DISTANCE
prev[i, j] = (0, 0)
Q.append((i, j))
dist[0][0] = 0
while Q:
u = dijkstra_get_min(Q, dist)
Q.remove(u)
moves = [x for x in ( (u[0], u[1] + 1), (u[0] + 1, u[1]), (u[0] + 1, u[1] + 1) ) if x in Q]
for v in moves:
alt = dist[u[0]][u[1]] + graph[v[0]][v[1]]
if alt < dist[v[0]][v[1]]:
dist[v[0], v[1]] = alt
prev[v[0], v[1]] = u
return dist, prev
Alguma opinião sobre a sua correção?
Seu código parece geralmente correto, mas ignora srce pesquisa apenas na direção positiva. Além disso, pode ser limpo e otimizado significativamente.
Alguns comentários gerais primeiro:
min) e tente aderir aos padrões de estilo de codificação .numpymenos que realmente use seus recursos embutidos. Usar numpy.arraypara acesso direto é geralmente mais lento do que list/ set/ ... porque os valores são convertidos em objetos Python completos em cada acesso.Não faça suposições sobre os recursos de seus dados. Especificamente, evite:
MAX_DISTANCE = 99999
RANGE_ARR = [x for x in range(1, 1001)]
Eles falham para gráficos com distância> 99999 ou mais de 1000 elementos. Calcule-os para sua entrada ou use limites superiores verdadeiros.
Como os números têm um "máximo" bem definido, podemos usar isso com segurança:
INFINITY = float('int')
Como a entrada graphé uma matriz nxn, podemos apenas consultar seu tamanho.
# inside `def dijkstra(graph, source):`
indices = range(len(graph))
Vamos começar com vertex in Q with min dist[u]/ dijkstra_get_min. Seu algoritmo é adequado, mas podemos explorar que o builtin do Python minjá permite pesos personalizados. O for vertex in Q:torna-se o principal argumento para min, if dist[vertex[0], vertex[1]] <= min:torna-se o peso key.
def dijkstra_get_min(vertices, distances):
return min(vertices, key=lambda vertex: distance[vertex[0]][vertex[1]])
O Dijkstraalgoritmo consiste em duas partes - inicialização e pesquisa. Seu código fica mais claro se dividirmos essas duas partes - sua linha dist[0][0] = 0é a transição de uma para a outra.
def dijkstra(graph, src=(0, 0)):
# dist, prev, Q
distances, prev_nodes, unvisited = dijkstra_initial(len(graph))
# set starting point
distances[src[0]][src[1]] = 0
dijkstra_search(graph, distances, prev_nodes, unvisited)
return distances, prev_nodes
O objetivo da inicialização é que todos os pontos tenham o mesmo valor. Isso significa que podemos criar diretamente as matrizes com seu valor final. Além disso, como o algoritmo não usa o "nó anterior", podemos inicializá-lo para um marcador barato.
def dijkstra_initial(size):
distances = [[INFINITY] * size for _ in range(size)]
prev_nodes = [[None] * size for _ in range(size)]
unvisited = {(x, y) for x in range(size) for y in range(size)}
# dist, prev, Q
return distances, prev_nodes, unvisited
Em vez de rastrear os nós visitados como uma lista ( [..., ...]), usamos um set ( {..., ...}). Um conjunto é desordenado e suporta testes de associação O (1), em comparação com testes de associação da lista O (n). Isso o torna mais adequado para a contabilidade de nós visitados / não visitados.
Para pesquisar no gráfico, visitaremos os vizinhos repetidamente. Esta é uma parte importante que pode ser facilmente executada de forma errada - a menos que a implementação do Graph forneça, pode valer a pena implementar explicitamente.
def neighbours(node):
x, y = node
return [
(x + x_offset, y + y_offset)
for x_offset in (-1, 0, 1)
for y_offset in (-1, 0, 1)
if not (x_offset == y_offset == 0) # reject node itself
]
O núcleo do algoritmo permanece logicamente o mesmo: ajustamos alguns nomes para serem mais falantes (por exemplo u-> node, v-> neighbour). Usamos neighboursa expressão preparada em vez da expressão longa.
def dijkstra_search(graph, distances, prev_nodes, unvisited):
while unvisited:
node = dijkstra_get_min(unvisited, dist)
unvisited.remove(node)
for neighbour in neighbours(node):
if neighbour not in unvisited:
continue
alt = distances[node[0]][node[1]] + graph[neighbour[0]][neighbour[1]]
if alt < distances[neighbour[0]][neighbour[1]]:
distances[neighbour[0]][neighbour[1]] = alt
prev_nodes[neighbour[0]][neighbour[1]] = node
Neste ponto, o código deve ser mais rápido e fácil de manter. A falha mais gritante que ainda temos é o manuseio explícito das dimensões. Em vez de acessar manualmente cada dimensão, seria melhor se pudéssemos acessar diretamente os pontos.
# currently
distances[neighbour[0]][neighbour[1]]
# desirable
distances[neighbour]
Isso pode ser "consertado" usando dicionários ( {point: value, ...}) em vez de listas aninhadas ( [[value, ...], ...]). Uma desvantagem imediata é que isso troca memória por simplicidade.
No entanto, pode ser usado para realmente reduzir o uso de memória - os dicionários podem ser naturalmente esparsos, permitindo-nos simplesmente não armazenar campos indeterminados. Uma vez que qualquer nó visitado se torna irrelevante para distâncias, podemos até mesmo limpar os distancesnós que já foram processados.
[x for x in range(1, 1001)]pode ser escrito da mesma forma list(range(1, 1001)).
Seria bom dar 1001um nome a isso também.
Da mesma forma, [0 for x in RANGE_ARR]pode ser escrito como [0] * len(RANGE_ARR). Multiplicar qualquer tipo de sequência por um inteiro repete os elementos dentro da sequência. Como um bônus, a partir de um rápido benchmarking que acabei de fazer, também é mais de 10 vezes mais rápido:
from timeit import timeit
N = int(1e6)
TRIALS = int(1e3)
print(timeit(lambda: [0] * N, number=TRIALS), "seconds")
print(timeit(lambda: [0 for x in range(N)], number=TRIALS), "seconds")
2.9889957 seconds
38.1463017 seconds
Esteja ciente de que você não deve usar isso quando o tipo de elemento for mutável (como [[0]] * 5). Multiplicar uma sequência cria várias referências ao mesmo objeto; não faz cópias.
Parece que Qdeveria ser um conjunto. Você não se preocupa com a ordem e a única coisa que a utiliza é para rastrear a associação de um conjunto. Os conjuntos serão significativamente mais rápidos aqui. As únicas duas mudanças necessárias são:
Q = set()
. . .
Q.add((i, j))
A única mudança que posso ver é que dijkstra_get_mintecnicamente depende da ordem de Q. Se dois elementos com os mesmos valores mínimos estão em Q, seu algoritmo escolhe a última instância. Como os conjuntos podem usar uma ordem diferente, isso pode alterar o vértice que é retornado.
Parece que MAX_DISTANCEse destina a ser um número arbitrariamente grande e todo o resto será menor. Você pode querer tentar usar np.infpara isso. Ao codificar o limite superior, você corre o risco de o problema "crescer" mais tarde e potencialmente exceder esse máximo; causando comportamento errôneo.
Jana Duggar foi aberta sobre sua busca pelo amor. Aqui está tudo o que ela disse sobre o assunto e sua janela de cinco anos para o casamento.
O astro de 'Outlander', Sam Heughan, revelou recentemente o que vai levar do set para relembrar Jamie Fraser, o papel que o tornou uma estrela.
'She Loves You' foi escrita em uma hora, gravada em um dia, e foi a música dos Beatles com uma de suas melhores apresentações de sua carreira.
Dolly Parton e sua avó Bessie tiveram um relacionamento especial. Vovó Parton estava muito doente, mas isso não impediu Dolly de pregar uma peça nela.
Você pode achar que o carvalho ou a nogueira são madeiras resistentes, mas quando se trata da madeira mais dura do mundo, elas nem chegam perto.
O oceano é repleto de beleza, mas também esconde algumas das criaturas marinhas mais assustadoras do planeta. Muitos desses animais espreitam nas profundezas do oceano, no mundo escuro e de alta pressão do fundo do mar.
Se você está enfrentando criaturas hostis ou se preparando para cenários PvP, conhecer os melhores encantamentos de espada no Minecraft pode te dar uma grande vantagem. Encantar espadas permite causar mais dano, aumentar a quantidade de itens obtidos de criaturas e prolongar a durabilidade da sua espada.
Quando as pessoas falam sobre países socialistas, geralmente imaginam o controle total do governo e a ausência de propriedade privada. Mas, na prática, as economias socialistas variam muito.
“Children” traz uma participação especial de grande nome e algumas notícias devastadoras
Este RAV4 está em excelentes condições e está preparado para um jogo de cadeiras musicais.
Imagens de drone capturaram bombeiros parecendo lutar para apagar o incêndio.
Eyes of Wakanda está diretamente relacionado ao MCU, além de atualizações sobre X-Men '97, What If..., Demolidor e muito mais.
O anel de noivado de Kate Middleton pertenceu à Princesa Diana antes do Príncipe William pedi-la em casamento. Descubra tudo o que há para saber sobre a peça histórica aqui.
John Cleese se casou com sua esposa, Jennifer Wade, em 2012. Aqui está tudo o que você precisa saber sobre a esposa de John Cleese, Jennifer Wade.
Patton Oswalt se casou com sua esposa, a também atriz Meredith Salenger, em 2017. Aqui está tudo o que você precisa saber sobre a esposa de Patton Oswalt, Meredith Salenger.
Mena Suvari é casada com o marido Michael Hope desde 2018. Aqui está tudo o que você precisa saber sobre o marido de Mena Suvari.
Isso me atinge De repente, como tantas vezes acontece É o meio do caminho tudo de novo <Não, não aquele no Pacífico, seu marrom, aquele no carnaval> Todas as manchetes em voz alta…..
Em 2022, com o lançamento do GPT-3, a habilidade de escrita parece se tornar menos importante. Com a IA, pessoas com redação ruim também podem gerar funcionários de qualidade.
No mundo acelerado e competitivo de hoje, a carreira desempenha um papel significativo no crescimento pessoal, na estabilidade financeira e na satisfação geral com a vida. No entanto, muitos indivíduos encontram-se presos em um ciclo implacável de estagnação e insatisfação, definhando gradualmente em suas vidas profissionais.
Na semana passada, notei um comunicado de imprensa, enviado via PressGazette (um site de notícias da mídia britânica). O artigo anunciava que a Acast, a empresa de publicidade e hospedagem de podcast Scandi, lideraria um consórcio de "provavelmente os editores mais influentes" em podcasting.