Algorithme de tri et implémentation en Python

introduction

J'ai résumé quelques algorithmes de tri.

J'ai décrit chaque point, un exemple d'implémentation en Python et chaque mesure de vitesse.

Je l'ai implémenté sans utiliser autant que possible les fonctions intégrées, donc ce n'est pas très rapide, mais je voulais faire quelque chose de plus rapide que sort () et sorted ().

Algorithme de tri à gérer

L'algorithme de tri à gérer est

Nom Temps de calcul moyen Pire temps de calcul utilisation de la mémoire Stabilité
Tri à bulles O(n^2) O(n^2) O(1) Stable
Tri sélectif O(n^2) O(n^2) O(1) Instable
Insérer un tri O(n^2) O(n^2) O(1) Stable
Tri par fusion O(n\log n) O(n\log n) O(n) Stable
Tri rapide O(n\log n) O(n^2) O(\log n) Instable
Compter le tri O(nk) O(n^2 k) O(nk) Instable

D'autres algorithmes de tri bien connus incluent le tri de tas, le tri de shell et le tri de base, mais cette fois je les ai oubliés. Je veux l'ajouter un jour.

Le tri de tas est assez facile à implémenter avec le module heapq de Python, mais on a l'impression que c'est différent de faire sort () ...

La stabilité signifie que lorsque des données telles que [1-b, 2-a, 1-a, 2-b] sont triées en utilisant des nombres comme clés, l'ordre avant le tri est enregistré, et [1-b, Nous reconnaissons que quelque chose comme 1-a, 2-a, 2-b] est défini comme stable.

Chaque point et implémentation

Tri à bulles

bubble_sort.py


def bubble_sort(arr):
    change = True
    while change:
        change = False
        for i in range(len(arr) - 1):
            if arr[i] > arr[i + 1]:
                arr[i], arr[i + 1] = arr[i + 1], arr[i]
                change = True
    return arr

Tri sélectif

--Sélectionnez le minimum ou le maximum dans le tableau et déplacez-le vers la fin. --Simple et facile à comprendre.

J'ai l'impression de pouvoir utiliser min () ...

Afin de réduire l'utilisation de la mémoire à $ O (1) $, il est implémenté en le remplaçant au lieu de le mettre dans une nouvelle liste.

sellect_sort.py


def sellect_sort(arr):
    for ind, ele in enumerate(arr):
        min_ind = min(range(ind, len(arr)), key=arr.__getitem__)
        arr[ind], arr[min_ind] = arr[min_ind], ele
    return arr

Insérer un tri

Si vous n'utilisez pas la recherche de 2 minutes

insert_sort.py


def insert_sort(arr):
    for i in range(1, len(arr)):
        j = i - 1
        ele = arr[i]
        while arr[j] > ele and j >= 0:
            arr[j + 1] = arr[j]
            j -= 1
        arr[j + 1] = ele
    return arr

Lors de l'utilisation de la recherche de 2 minutes,

insert_sort_bin.py


import sys
sys.setrecursionlimit(10 ** 7)

def binary_search(arr, low, hig, ele):
    if low == hig:
        if arr[low] > ele:
            return low
        else:
            return low + 1
    elif low > hig:
        return low
    
    mid = (low + hig) // 2
    if arr[mid] < ele:
        return binary_search(arr, mid + 1, hig, ele)
    elif arr[mid] > ele:
        return binary_search(arr, low, mid - 1, ele)
    else:
        return mid

def insert_sort_bin(arr):
    for i in range(1, len(arr)):
        ele = arr[i]
        ind = binary_search(arr, 0, i - 1, ele)
        arr[:] = arr[:ind] + [ele] + arr[ind:i] + arr[i + 1:]
    return arr

À propos, lors de l'utilisation de bisect, qui est un module de recherche de 2 minutes,

insert_sort_bin_buildin.py


import bisect
def insert_sort_bin_buildin(arr):
    for i in range(1, len(arr)):
        bisect.insort(arr, arr.pop(i), 0, i)
    return arr

Tri par fusion

merge_sort.py


def merge_sort(arr):
    if len(arr) <= 1:
        return arr
    
    mid = len(arr) // 2
    #Faites la séparation ici
    left = arr[:mid]
    right = arr[mid:]
    
    #Fractionner récursivement
    left = merge_sort(left)
    right = merge_sort(right)
    
    #Lorsque le retour est retourné, combinez et passez le suivant combiné
    return merge(left, right)


def merge(left, right):
    merged = []
    l_i, r_i = 0, 0
    
    #Tout ce que vous avez à faire est de regarder par la gauche pour fusionner les tableaux triés.
    while l_i < len(left) and r_i < len(right):
        #ici=La stabilité est maintenue en attachant
        if left[l_i] <= right[r_i]:
            merged.append(left[l_i])
            l_i += 1
        else:
            merged.append(right[r_i])
            r_i += 1
    
    #Si l'une des instructions while ci-dessus devient False, cela se terminera, alors étendez trop
    if l_i < len(left):
        merged.extend(left[l_i:])
    if r_i < len(right):
        merged.extend(right[r_i:])
    return merged

Tri rapide

――Déterminez une valeur de référence et divisez-la en deux séquences, grande et petite selon la valeur de référence.

quick_sort.py


def quick_sort(arr):
    left = []
    right = []
    if len(arr) <= 1:
        return arr
    
    #Aléatoire car cela ne dépend pas de l'état des données.choice()Peut être utilisé.
    # ref = random.choice(arr)
    ref = arr[0]
    ref_count = 0
    
    for ele in arr:
        if ele < ref:
            left.append(ele)
        elif ele > ref:
            right.append(ele)
        else:
            ref_count += 1
    left = quick_sort(left)
    right = quick_sort(right)
    return left + [ref] * ref_count + right

Compter le tri

Le tri par compartiment est un algorithme de tri très similaire au tri par comptage. Le tri par compartiment est comme un algorithme de tri qui crée plusieurs boîtes avec une certaine plage, y met des valeurs, trie chaque boîte, puis les combine.

Le tri par comptage peut également maintenir la stabilité en créant une boîte pour chaque valeur et en l'ajoutant à celle-ci, mais il s'agit de l'implémentation car l'idée est un compartiment plutôt qu'un comptage. Si vous utilisez le module collections.defaultdict, vous pouvez l'implémenter assez facilement et de manière stable.

count_sort.py


def count_sort(arr):
    max_num = max(arr)
    min_num = min(arr)
    count = [0] * (max_num - min_num + 1)
    for ele in arr:
        count[ele - min_num] += 1
 
    return [ele for ele, cnt in enumerate(count, start=min_num) for __ in range(cnt)]

Comparaison de vitesse

Ensemble de données à utiliser

Généré avec numpy.random.

L'ensemble de données créé est

--data1: 100 nombres aléatoires uniformes compris entre 0 et 100 --data2: 10000 nombres aléatoires uniformes dans la plage 0-1000 --data3: 10000 nombres aléatoires uniformes compris entre 0 et 100 --data4: 100 nombres aléatoires de distribution normale standard avec moyenne 10 et écart type 5 --data5: 10000 nombres aléatoires de distribution normale standard dans la plage de la moyenne 100 et de l'écart type 50 --data6: 100 nombres aléatoires uniformes dans la plage 0-100 déjà triés dans l'ordre inverse --data7: déjà trié par 100 nombres aléatoires uniformes d'entiers compris entre 0 et 100, seuls les 10 premiers sont foirés

make_data.py


data_rand_1 = list(randint(0, 10 ** 2, 10 ** 2))
data_rand_2 = list(randint(0, 10 ** 4, 10 ** 4))
data_rand_3 = list(randint(0, 10 ** 2, 10 ** 4))
data_rand_4 = [int(normal(10, 5)) for __ in range(10 ** 2)]
data_rand_5 = [int(normal(10 ** 2, 50)) for __ in range(10 ** 4)]
data_rand_6 = sorted(randint(0, 10 ** 2, 10 ** 2), reverse=True)
data_rand_7 = sorted(randint(0, 10 ** 2, 10 ** 2))
data_rand_7[0: 10] = choice(data_rand_7[0: 10], 10, replace=False)

Unknown.png

Unknown-1.png

Unknown-2.png

Unknown-3.png

Unknown-4.png

Unknown-5.png

Unknown-6.png

Mesure de vitesse

Le code ci-dessous a été utilisé pour mesurer 100 fois et la moyenne a été calculée. Je ne pouvais pas bien le mesurer avec la commande timeit du notebook Jupyter.

L'unité est le μs.

Des mesures ont été effectuées pour chaque algorithme afin de réduire au maximum les erreurs. Cela dépend peut-être des spécifications du processeur et de la mémoire, mais si l'implémentation était telle que tous les algorithmes pouvaient être mesurés en même temps à l'aide d'une boucle, les performances chuteraient considérablement.

time.py


datas = [data_rand_1, data_rand_2, data_rand_3,
             data_rand_4, data_rand_5, data_rand_6, data_rand_7]

for ind, data in enumerate(datas):
    print("---- DataSet : {0} ----".format(ind + 1))
    times = []
    for i in range(100):
        start = time.time()
        #Algorithme de tri que vous souhaitez mesurer
        sorted(data)
        elapsed_time = int((time.time() - start) * (10 ** 6))
        times.append(elapsed_time)

    print(sum(times) // 100)
type Nombre aléatoire uniforme 大きいNombre aléatoire uniforme 狭くて大きいNombre aléatoire uniforme distribution normale 大きいdistribution normale Trié dans l'ordre inverse Presque trié
Tri intégré Python 16 3488 2938 10 2447 12 6
Tri à bulles 25 167789 163781 21 154426 26 10
Tri sélectif 418 4025181 3889431 379 3552541 433 422
Insérer un tri 24 58281 53705 19 47764 25 17
Insérer un tri(Recherche en 2 minutes) 627 1382480 1338027 334 1278530 347 339
Tri par fusion 593 67221 61657 329 59483 270 233
Tri rapide 142 25911 12009 56 12044 469 542
Compter le tri 95 5597 2164 24 1607 57 51

Prise en compte du résultat pour le moment

Recherche d'un algorithme plus rapide que le tri intégré

Pour les plages étroites, le tri par comptage semble être plus rapide que le tri intégré.

Je pense que si nous pouvons mettre en œuvre avec succès le tri par seau, qui est un tri relatif au nombre, nous pouvons construire un algorithme plus rapide.

Pour le moment, combinez le tri rapide et le tri par comptage pour insérer une grande quantité de données.

Les données préparées sont big_data = list (randint (0, 10 ** 3, 10 ** 6)).

quick_sort_with_count.py


def quick_sort_with_count(arr):
    left = []
    right = []
    if len(arr) <= 1:
        return arr
    ref = choice(arr)
    ref_count = 0

    for ele in arr:
        if ele < ref:
            left.append(ele)
        elif ele > ref:
            right.append(ele)
        else:
            ref_count += 1

    if len(left) <= 1000:
        left = count_sort(left)
    else:
        left = quick_sort_with_count(left)

   if len(right) <= 1000:
        right = count_sort(right)
    else:
        right = quick_sort_with_count(right)

    return left + [ref] * ref_count + right
type résultat
Tri intégré Python 464109
Tri rapide 2069006
Compter le tri 250043
Tri rapide+Compter le tri 3479010

En conclusion

Je n'ai plus de temps pour le moment, alors c'est tout. Si vous avez le temps, prenez un ensemble de données à granularité fine et comparez et validez les tris et dénombrements intégrés de Python.

On dit généralement que le tri par fusion, le tri rapide, etc. sont rapides, mais pour le moment, il semble que ce ne soit pas si rapide car les appels récursifs, etc. prennent du temps en Python.

Si vous connaissez la plage et le type de valeurs, le tri par comptage peut être utile. Wikipédia en japonais n'a même pas d'article, et certains documents anglais sont confondus avec le tri par seau, mais ...

Page de référence

Recommended Posts

Algorithme de tri et implémentation en Python
Implémentation du tri original en Python
Module d'implémentation de file d'attente et Python "deque"
Implémentation RNN en python
Tri de base en Python
Implémentation ValueObject en Python
Algorithme génétique en python
Algorithme en Python (méthode Bellman-Ford, Bellman-Ford)
Implémentation SVM en python
Algorithme en Python (Dijkstra)
Explication de la distance d'édition et de l'implémentation en Python
Algorithme en Python (jugement premier)
Techniques de tri en Python
Reproduire la méthode de division mutuelle euclidienne en Python
Algorithme en Python (dichotomie)
Pile et file d'attente en Python
Implémentation de réseau neuronal en python
Implémenter l'algorithme de Dijkstra en python
Unittest et CI en Python
Description et implémentation de Maxout (Python)
Implémentation du tri rapide en Python
Fusion de la mise en œuvre du tri / analyse du montant du calcul et de l'expérimentation en Python
Symboles logiques appris dans le mariage (et exemples d'implémentation en Python)
Algorithme en Python (recherche de priorité de largeur, bfs)
Différence entre list () et [] en Python
Explication et implémentation de l'algorithme ESIM
Différence entre == et est en python
Implémentation de l'estimation des paramètres HMM en python
Implémentation de distribution normale mixte en python
Manipuler des fichiers et des dossiers en Python
À propos de Python et Cython dtype
Algorithme Python
Ecrire des algorithmes A * (A-star) en Python
Développons un algorithme d'investissement avec Python 2
Affectations et modifications des objets Python
Implémentation du jeu de vie en Python
Algorithme en Python (recherche de priorité en profondeur, dfs)
Vérifiez et déplacez le répertoire en Python
Chiffrement avec Python: IND-CCA2 et RSA-OAEP
Hashing de données en R et Python
Synthèse de fonctions et application en Python
Implémentation d'un algorithme simple en Python 2
Exporter et exporter des fichiers en Python
Implémentation de la méthode Dyxtra par python
Algorithme (arborescence de segments) en Python (s'entraîner)
Inverser le pseudonyme plat et le katakana en Python2.7
Exécutez un algorithme simple en Python
Lire et écrire du texte en Python
[GUI en Python] Menu PyQt5 et barre d'outils-
Créer et lire des paquets de messages en Python
Résolution de l'introduction d'AOJ aux algorithmes et aux structures de données en Python -Partie1-
Résolution de l'introduction d'AOJ aux algorithmes et aux structures de données en Python -Partie2-
Résolution de l'introduction d'AOJ aux algorithmes et aux structures de données en Python -Partie4-
Résolution de l'introduction d'AOJ aux algorithmes et aux structures de données en Python -Partie3-
Chevauchement d'expressions régulières en Python et Java
PRML Chapitre 8 Algorithme Somme des produits Implémentation Python
Différence d'authenticité entre Python et JavaScript
Notes utilisant cChardet et python3-chardet dans Python 3.3.1.
Les modules et packages en Python sont des "espaces de noms"