https://atcoder.jp/contests/soundhound2018-summer-qual/tasks/soundhound2018_summer_qual_d
Als ich die frühere Frage dieses AtCoders gelöst habe, konnte ich AC mit der Dyxtra-Methode ausführen, aber mit Python (PyPy) ist das Ausführungszeitlimit (2000 ms) gerade noch erreicht. Als ich nach früheren Einsendungen suchte, fand ich eine schnelle Antwort von 789 ms, während es viele über 1000 ms gab.
https://atcoder.jp/contests/soundhound2018-summer-qual/submissions/7721902
Es führt so etwas wie die kürzeste Routenberechnung nach Breitenprioritätssuche für einen Baum durch. Wenn der Graph jedoch einen geschlossenen Pfad anstelle eines Baums hat, scheint die Zeitberechnung $ O (V ^ 2) $ zu sein (wenn es sich um einen Baum handelt). $ O (V) = O (E) $). Die Antwort ist jedoch tatsächlich schnell. In Anbetracht der Möglichkeit, dass der Berechnungsbetrag falsch geschätzt wird, habe ich die folgenden drei Methoden verglichen.
Referenz: <a href = https://ja.wikipedia.org/wiki/%E3%83%80%E3%82%A4%E3%82%AF%E3%82%B9%E3%83%88%E3 % 83% A9% E6% B3% 95> Wikipedia-Artikel Pseudocode
Wenn man nur den Zeitaufwand berücksichtigt, scheint dijkstra1 </ b> in vielen Fällen die schnellste zu sein.
Verwenden Sie ein verkettetes ungerichtetes Diagramm ($ V $ für Eckpunkte, $ E $ für Seiten, keine Mehrfachseiten, keine Selbstschleife). Erstellen Sie zunächst eine gerade Straße, die alle Punkte einmal in der Reihenfolge vom Startpunkt aus durchläuft, wie in der folgenden Abbildung gezeigt, um die Verbindung sicherzustellen (die Reihenfolge der Scheitelpunkte auf dem Weg ist zufällig). Die anderen Seiten werden zufällig zwischen Eckpunkten gestreckt, die noch keine Seiten haben. Das Gewicht der Seiten wird zufällig von $ 1 $ bis $ 10 ^ 7 $ für jede Seite bestimmt.
$V \in \{ 10^3,~ 10^4,~ 10^5,~ 10^6,~ 10^7 \} $ $E \in \{ 10^3,~ 10^4,~ 10^5,~ 3\cdot 10^5,~ 10^6,~ 10^7 \} $ (Der Grund, warum ich $ 3 \ cdot 10 ^ 5 $ in die Anzahl der Seiten gesetzt habe, ist, dass ich die Einschränkungen des Wettbewerbsprofis übernommen habe)
Die Zeit wurde für $ (V, E) $ gemessen, das $ V-1 \ leqq E \ leqq \ frac {V (V-1)} {2} $ erfüllt.
worstcase.py
def dijkstra0(a, in_connect_list, v, money_kind):
out_shortest_list = [10**15 for i in range(v)] #Abstand von der Spitze a
out_shortest_list[a] = 0
searching_list = [a]
while searching_list != []:
new_search_list = []
for i in searching_list:
for j in in_connect_list[i]:
c = j[0]
p = j[money_kind]
if out_shortest_list[c] > p + out_shortest_list[i]:
out_shortest_list[c] = p + out_shortest_list[i]
new_search_list.append(c)
searching_list = list(set(new_search_list)) #Änderung vom ursprünglichen Code
return out_shortest_list
def dijkstra1(s, M):
D = [10**15] * v #Abstand von Eckpunkten s
D[s] = 0
V = [0] * v #D an dieser Spitze[i]If wird als kürzeste Entfernung bestimmt 1
Q = [] #Prioritätswarteschlange
for v in range(v):
heapq.heappush(Q, [D[v], v])
le = len(Q)
while le > 0:
q = heapq.heappop(Q)
u = q[1]
du = q[0]
if V[u] == 0:
V[u] = 1
le -= 1
for i in range(len(M[u])):
v = M[u][i][0]
luv = M[u][i][1]
if V[v] == 0:
alt = du + luv
if D[v] > alt:
D[v] = alt
heapq.heappush(Q, [alt, v])
return D
def dijkstra2(s, M):
D = [10**15] * v #Abstand von Eckpunkten s
D[s] = 0
Q = [i for i in range(v)]
while len(Q) > 0:
mi_ind = -1
mi = 10**15
for i in Q:
if D[i] < mi:
mi = D[i]
mi_ind = i
Q.pop(Q.index(mi_ind))
for i in range(len(M[mi_ind])):
v = M[mi_ind][i][0]
luv = M[mi_ind][i][1]
D[v] = min(D[v], D[mi_ind] + luv)
return D
from random import randint, random, sample
import heapq
import time
V = [10**3, 10**4, 10**5, 10**6, 10**7]
E = [10**3, 10**4, 10**5, 3 * 10**5, 10**6, 10**7]
for v in V:
for e in E:
if v-1 <= e <= v*(v-1)//2: #Bedingungen für die Verkettung und keine Mehrfachseiten
T = []
for _ in range(10): #Nehmen Sie den 10-mal gemessenen Durchschnitt
N_shuf = [0] + sample([i for i in range(1, v)], v-1)
M = [[] for i in range(v)]
A = [min(N_shuf[i],N_shuf[i+1]) * v
+ max(N_shuf[i],N_shuf[i+1]) for i in range(v-1)]
while len(A) < e:
e_add = e - len(A)
ADD = [0] * e_add
i = 0
while i < e_add:
v0 = randint(0, v-1)
v1 = randint(0, v-1)
if v0 != v1:
ADD[i] = min(v0,v1) * v + max(v0,v1)
i += 1
A += ADD
A = list(set(A)) #Entfernen Sie mehrere Seiten
for ii in A:
i = ii // v
j = ii % v
M[i].append([j, randint(1, 10**7)])
M[j].append([i, randint(1, 10**7)])
t0 = time.time()
D = dijkstra0(0, M, v, 1)
# D = dijkstra1(0, M)
# D = dijkstra2(0, M)
t1 = time.time() - t0
T.append(t1)
print(v, e, sum(T) / 10)
Der Durchschnitt von 10 Mal war die Einheit Sekunden und 2 gültige Zahlen.
0.00060 | 0.0074 | 0.14 | 0.40 | |||
0.0069 | 0.29 | 0.96 | 3.1 | 25 | ||
0.13 | 1.0 | 4.4 | 53 | |||
1.6 | 75 | |||||
18 |
0.0016 | 0.0059 | 0.070 | 0.23 | |||
0.020 | 0.16 | 0.41 | 1.1 | 8.9 | ||
0.36 | 1.3 | 2.6 | 15 | |||
4.4 | 37 | |||||
53 |
0.017 | 0.023 | 0.11 | 0.29 | |||
1.9 | 2.1 | 2.2 | 3.1 | 14 | ||
180 | - | - | - | |||
- | - | |||||
- |
\ -: Nicht gemessen, da es länger als ein paar hundert Sekunden gedauert hat
Die grundlegendste Dyxtra-Methode ist offensichtlich langsamer, wenn $ V $ zunimmt. Die Methode mit dijkstra0 </ b> und der Prioritätswarteschlange ( dijkstra1 </ b>) scheint jedoch im Bereich der Tabelle keinen großen Unterschied zu machen. Wenn die Bedingungen wahrscheinlich in einem Wettbewerbsprofi liegen ($ V \ leqq 10 ^ 5, ~ E \ leqq 3 \ cdot 10 ^ 5 $), wird entweder eine bestanden oder in einigen Fällen dijkstra0 </ b> ist besser. Es scheint auch schnell zu sein (Zeitvergleiche sind in der folgenden Tabelle angegeben). Ein weiteres Merkmal von dijkstra0 </ b> ist, dass es schneller sein kann, wenn Sie $ E $ reparieren und $ V $ erhöhen. Anscheinend ist dijkstra0 </ b> gut in spärlichen, baumartigen (= $ V $ ist groß, aber $ E $ ist klein) Graphen. Wenn es sich in der Nähe eines Baumes befindet, ist es fast $ O (V) $.
Tabelle unten: Zeitbedarfsverhältnis ( dijkstra0 </ b> / dijsktra1 </ b>)
0.38 | 1.3 | 2.0 | 1.7 | |||
0.35 | 1.8 | 2.4 | 2.8 | 2.8 | ||
0.37 | 0.82 | 1.7 | 3.6 | |||
0.36 | 2.0 | |||||
0.34 |
Das folgende Diagramm kann als der schlimmste Fall von dijkstra0 </ b> betrachtet werden. Die Anzahl der Aktualisierungen der kürzesten Entfernung beträgt $ (V-1) $ mal für den oberen linken Eckpunkt, $ (V-2) $ mal für den rechten Eckpunkt, ..., $ 1 $ für den oberen rechten Eckpunkt. Da die Gesamtzahl der Aktualisierungen $ \ frac {V (V-1)} {2} $ mal beträgt, erhöht sich die Berechnungszeit fast proportional zu $ V ^ 2 $. Schreiben Sie den anderen Code als den Funktionsteil der Dyxtra-Methode wie folgt um: $ V \ in \ {10 ^ 3, ~ 5 \ cdot 10 ^ 3, ~ 10 ^ 4, ~ 5 \ cdot 10 ^ 4 \} $ Ich habe es mit verglichen.
worstcase.py
from random import randint, random, sample
import heapq
import time
for v in [1000, 5000, 10000, 50000]:
T = []
for _ in range(10):
N_shuf = [0] + sample([i for i in range(1, v)], v-1)
M = [[] for i in range(v)]
for i in range(1, v):
M[0].append([N_shuf[i], 10**7 - i * 2])
M[N_shuf[i]].append([0, 10**7 - i * 2])
for i in range(1, v-1):
M[N_shuf[i+1]].append([N_shuf[i], 1])
M[N_shuf[i]].append([N_shuf[i+1], 1])
t0 = time.time()
D = dijkstra0(0, M, v, 1)
# D = dijkstra1(0, M)
# D = dijkstra2(0, M)
t1 = time.time() - t0
T.append(t1)
print(v, sum(T) / 10)
dijkstra0 | dijkstra1 | dijkstra2 | ||
---|---|---|---|---|
1000 | 1997 | 0.11 | 0.0024 | 0.017 |
5000 | 9997 | 3.1 | 0.016 | 0.45 |
10000 | 19997 | 12 | 0.033 | 1.8 |
50000 | 99997 | 740 | 0.25 | 48 |
Wenn in dijkstra0 </ b> $ V $ mit 10 multipliziert wird, beträgt die Berechnungszeit das 100- bis 200-fache (dies ist auch bei dijkstra2 </ b> der Fall). Das Ergebnis ist völlig anders. dijkstra1 </ b>, also $ O ((E + V) \ log {V}) $, ist immer noch stabil und schnell. Es kann Fälle geben, in denen dieser schlimmste Fall nicht wie zu Beginn des Problems im Testfall enthalten ist, es jedoch besser erscheint, dijkstra1 </ b> für Wettbewerbsprofis zu verwenden. Im Falle der zeitlichen Begrenzung ist es besser, eine schnelllebige Implementierung zu implementieren, die andere Teile als die Dyxtra-Methode enthält.
--2020 / 04/20 Geänderter Artikeltitel (früherer Titel: O (V ^ 2) ist schneller als Dyxtra? Suche nach Breite mit Priorität für Diagramme mit geschlossenen Pfaden), teilweise überarbeiteter Text
Recommended Posts