[PYTHON] Ich habe die Berechnungszeit von "X in Liste" (lineare Suche / dichotome Suche) und "X in Menge" untersucht.

** (* Hinzugefügt am 13. Mai 2020: Wenn die Anzahl der Elemente in der Liste geändert wird) **

Hintergrund

Bei ABC167 bei Atcoder habe ich neulich TLE mit dem folgenden Problem gefeuert. ・ ABC167D-Teleporter

Teleportiere die Städte der Reihe nach, halte an, wenn die Schleife auftritt, und die verbleibende Anzahl von Malen Mir wurde klar, dass ich Mod verwenden sollte, um es zu finden.

Werden Sie eine Schleife ⇔ Erreichen Sie dieselbe Stadt erneut ⇔ Bereits in der Liste der besuchten Städte

Ich dachte, es wäre gut zu denken, und es dauerte lange, bis "X in list" in Python verarbeitet wurde.

Immerhin wurde mir klar, dass ich stattdessen "X in Set" verwenden sollte. Ich habe mich gefragt, ob sich die Geschwindigkeit so stark ändern würde, also habe ich die Ergebnisse meiner Forschung zusammengefasst.

Was ist der eingestellte Typ?

Zitiert aus Pythons Offizieller Dokumentation

Das Set-Objekt ist eine ungeordnete Sammlung eindeutiger Hash-Objekte. Häufige Verwendungszwecke sind Attributionstests, Deduplizierung aus Sequenzen, Produktmengen, Summenmengen, Differenzmengen und arithmetische Berechnungen wie symmetrische Differenzen (exklusive logische Summen).

Aggregate unterstützen wie andere Sammlungen x in set, len (set) für x in set. Da die Sammlung ungeordnet ist, zeichnet die Menge weder die Reihenfolge des Einfügens noch die Position der Elemente auf. Daher unterstützen Sets keine Indizierung, Aufteilung oder anderes sequentielles Verhalten.

Darüber hinaus werden die Funktionen in hier leicht verständlich zusammengefasst.

  • Das gleiche Element enthält nur ein </ strong>.
  • listähnliche Operationen wie x in set, len (set), für x in set </ strong>
  • Elemente können später hinzugefügt / gelöscht werden (Tupel kann nicht geändert werden, Liste kann geändert werden, so dass es näher an der Liste ist)
  • Eine aggregierte Berechnung ist möglich (Berechnungen wie in A, aber nicht in B, in A und B usw. können einfach und schnell </ strong> sein)
  • Da die Reihenfolge nicht erhalten bleibt, ist die erste, die Sie eingeben, nicht immer die erste
  • Die Operation von "x in set" ist im Vergleich zu list </ strong> superschnell

Kurz gesagt, es scheint sich um einen Datentyp zu handeln, der in einem sortierten Zustand ohne Duplizierung gespeichert wird. Aus diesem Grund ist die Dichotomie für die Suche nach Mengen immer verfügbar und scheint schneller zu sein.

Messbedingung

Um den Typ "Liste" und den Typ "Menge" zu untersuchen, habe ich die Suche in den folgenden vier Fällen untersucht.

Nummer Datentyp Datenreihenfolge Suchmethode
1 list zufällig Lineare Suche
2 list Sortiert Lineare Suche
3 list Sortiert Halbierungssuche
4 set (Sortiert) (Halbierungssuche)

Um andere Bedingungen gleich zu machen

  • Die Anzahl der Elemente in der Liste beträgt $ 10 ^ 6 $ und es gibt keine Duplizierung.
  • Das zu durchsuchende Element $ t $ erfüllt $ 1 \ le t \ le 10 ^ 6 $. (Absolut gefunden)
  • Generiere $ 10 ^ 2 $, $ 10 ^ 3 $, $ 10 ^ 4 $ nach dem Zufallsprinzip und suche nach jedem.

Ich habe es so gemacht.

Quellcode

main.py


import time
import random
from collections import deque

num = [i+1 for i in range(1000000)]
num_shaffle = random.sample(num, len(num))
num_set = set(num_shaffle)
#Wonach schauen
target = [random.randint(1, 1000000) for i in range(10000)]

#Listentyp lineare Suche oder Set-Typ
def search_seaquence(L):
    each_time = deque([])

    for i in range(10000):
        #von Anfang an verstrichen_Messen Sie den Zeitunterschied zur Zeit
        start = time.time()
        target[i] in L
        elapsed_time = time.time() - start
        each_time.append(elapsed_time)

    return list(each_time)

#Listentyp-Halbierungssuche
def search_half(L):
    each_time = deque([])

    for i in range(10000):
        low = 0
        high = len(num) - 1
        start = time.time()
        
        while low <= high:
            mid = int((low + high) // 2)
            guess = L[mid]
            if guess == target[i]:
                break
            elif guess > target[i]:
                high = mid - 1
            else:
                low = mid + 1

        elapsed_time = time.time() - start
        each_time.append(elapsed_time)
    return list(each_time)


def cal_time(LIS, mode):
    print(mode)
    #Gesamtzeit
    sum_time = sum(LIS)
    print ("sum_time:{0}".format(sum_time) + "[sec]")

    return 0


#Zufällige Liste / lineare Suche
random_list = search_seaquence(num_shaffle)
cal_time(random_list, 'mode: random list')

#Sequenzliste / lineare Suche
seaquence_list_seq = search_seaquence(num)
cal_time(seaquence_list_seq, 'mode: seaquence list seaquence')

#Sequenzliste / halbierte Suche
seaquence_list_half = search_half(num)
cal_time(seaquence_list_half, 'mode: seaquence list half')

#Suche einstellen
set_seek = search_seaquence(num_set)
cal_time(set_seek, 'mode: set')

Ergebnis

Suchmethode 10^2Gesamt(sec) 10^3Gesamt(sec) 10^4Gesamt(sec)
Zufällige Liste / lineare Suche 5.14 4.01 \times 10 4.65 \times 10^2
Sortierte Liste / lineare Suche 1.13 8.36 1.29 \times 10^2
Sortierte Liste / Dichotomie 3.46 \times 10^{-3} 2.03 \times 10^{-2} 1.16 \times 10^{-1}
Typ einstellen 8.44 \times 10^{-5} 9.92 \times 10^{-4} 5.45 \times 10^{-3}

Irgendwie versuche ich, daraus ein Diagramm zu machen. list_order.png

(** Doppelter logarithmischer Graph **.)

Wie erwartet ist das richtig, aber es ist nicht so unterschiedlich, je nach Bestellung. Ich denke, die Reihenfolge für die Dichotomie war $ O (\ log n) $, aber es fühlte sich ganz anders an. Es war überraschend, dass der Typ "set" für $ 10 ^ 4 $ selbst in derselben Dichotomie etwa 20-mal schneller ist. (Vielleicht ist es ein Problem mit meinem Code)

[Hinzufügung] Wenn die Anzahl der Elemente im Suchziel geändert wird

Als ich darüber nachdachte, stellte ich fest, dass der Effekt der Reihenfolge nur sichtbar ist, wenn ich die Anzahl der Elemente im Suchziel (list / set) für die Suche geändert habe. Außerdem wird die Anzahl der Elemente in der Suchzielliste auf $ 10 ^ 4, 10 ^ 5, 10 ^ 6 $ festgelegt, während das Suchziel auf $ 10 ^ 4 $ festgelegt ist. Ich habe auch untersucht, wann es geändert wurde.

Suchmethode\Anzahl der Listenelemente 10^4Gesamt(sec) 10^5Gesamt(sec) 10^6Gesamt(sec)
Zufällige Liste / lineare Suche 1.68 2.58 \times 10 5.37 \times 10^2
Sortierte Liste / lineare Suche 9.26 \times 10^{-1} 1.29 \times 10 1.34 \times 10^2
Sortierte Liste / Dichotomie 8.93 \times 10^{-2} 1.75 \times 10^{-1} 1.76 \times 10^{-1}
Typ einstellen 5.62 \times 10^{-3} 4.02 \times 10^{-3} 1.01 \times 10^{-2}

image.png

(** Doppelter logarithmischer Graph **.)

Es ist genau wie bestellt. Ich bin froh. Es scheint, dass der Grund, warum die Zeit bei der zweiminütigen Suche eher verkürzt wird, darin besteht, dass das Suchziel zufällig festgelegt wird. Auf diese Weise können Sie die Stärke der Protokollreihenfolge sehen.

Zusammenfassung

Verwenden Sie nicht "X in Liste", wenn Sie nach doppelten Daten suchen (Gebot) Immerhin ist die zweiminütige Suche schnell

Es kann auch verwendet werden, wenn nach bestimmten Daten aus einer großen Datenmenge gesucht wird. Wenn es sich jedoch um einen "Set" -Typ handelt, gehen die Indexinformationen verloren. Als ich diese Informationen wollte, stellte ich fest, dass es notwendig war, die "Liste" separat zu führen.

Recommended Posts