File size: 4,547 Bytes
d916065
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
# Natural Language Toolkit: List Sorting
#
# Copyright (C) 2001-2023 NLTK Project
# Author: Steven Bird <[email protected]>
# URL: <https://www.nltk.org/>
# For license information, see LICENSE.TXT

"""

This module provides a variety of list sorting algorithms, to

illustrate the many different algorithms (recipes) for solving a

problem, and how to analyze algorithms experimentally.

"""
# These algorithms are taken from:
# Levitin (2004) The Design and Analysis of Algorithms

##################################################################
# Selection Sort
##################################################################


def selection(a):
    """

    Selection Sort: scan the list to find its smallest element, then

    swap it with the first element.  The remainder of the list is one

    element smaller; apply the same method to this list, and so on.

    """
    count = 0

    for i in range(len(a) - 1):
        min = i

        for j in range(i + 1, len(a)):
            if a[j] < a[min]:
                min = j

            count += 1

        a[min], a[i] = a[i], a[min]

    return count


##################################################################
# Bubble Sort
##################################################################


def bubble(a):
    """

    Bubble Sort: compare adjacent elements of the list left-to-right,

    and swap them if they are out of order.  After one pass through

    the list swapping adjacent items, the largest item will be in

    the rightmost position.  The remainder is one element smaller;

    apply the same method to this list, and so on.

    """
    count = 0
    for i in range(len(a) - 1):
        for j in range(len(a) - i - 1):
            if a[j + 1] < a[j]:
                a[j], a[j + 1] = a[j + 1], a[j]
                count += 1
    return count


##################################################################
# Merge Sort
##################################################################


def _merge_lists(b, c):
    count = 0
    i = j = 0
    a = []
    while i < len(b) and j < len(c):
        count += 1
        if b[i] <= c[j]:
            a.append(b[i])
            i += 1
        else:
            a.append(c[j])
            j += 1
    if i == len(b):
        a += c[j:]
    else:
        a += b[i:]
    return a, count


def merge(a):
    """

    Merge Sort: split the list in half, and sort each half, then

    combine the sorted halves.

    """
    count = 0
    if len(a) > 1:
        midpoint = len(a) // 2
        b = a[:midpoint]
        c = a[midpoint:]
        count_b = merge(b)
        count_c = merge(c)
        result, count_a = _merge_lists(b, c)
        a[:] = result  # copy the result back into a.
        count = count_a + count_b + count_c
    return count


##################################################################
# Quick Sort
##################################################################


def _partition(a, l, r):
    p = a[l]
    i = l
    j = r + 1
    count = 0
    while True:
        while i < r:
            i += 1
            if a[i] >= p:
                break
        while j > l:
            j -= 1
            if j < l or a[j] <= p:
                break
        a[i], a[j] = a[j], a[i]  # swap
        count += 1
        if i >= j:
            break
    a[i], a[j] = a[j], a[i]  # undo last swap
    a[l], a[j] = a[j], a[l]
    return j, count


def _quick(a, l, r):
    count = 0
    if l < r:
        s, count = _partition(a, l, r)
        count += _quick(a, l, s - 1)
        count += _quick(a, s + 1, r)
    return count


def quick(a):
    return _quick(a, 0, len(a) - 1)


##################################################################
# Demonstration
##################################################################


def demo():
    from random import shuffle

    for size in (10, 20, 50, 100, 200, 500, 1000):
        a = list(range(size))

        # various sort methods
        shuffle(a)
        count_selection = selection(a)
        shuffle(a)
        count_bubble = bubble(a)
        shuffle(a)
        count_merge = merge(a)
        shuffle(a)
        count_quick = quick(a)

        print(
            ("size=%5d:  selection=%8d,  bubble=%8d,  " "merge=%6d,  quick=%6d")
            % (size, count_selection, count_bubble, count_merge, count_quick)
        )


if __name__ == "__main__":
    demo()