2011-02-10 38 views
19

Tôi đã rối tung xung quanh với Python đang cố gắng thực hành các thuật toán sắp xếp của tôi và phát hiện ra điều gì đó thú vị.Quicksort sắp xếp các số lớn hơn nhanh hơn?

tôi có ba phần khác nhau của dữ liệu:

  • x = số lượng các số để sắp xếp
  • y = phạm vi các số là (tất cả ints tạo ra ngẫu nhiên)
  • z = tổng thời gian thực hiện để loại

Thời gian:
x = 100000 và
y = (0,100000) sau đó
z = ,94182094911 giây

Thời gian:
x = 100000 và
y = (0100) sau đó
z = 12,4218382537 giây

Thời gian:
x = 100000 và
y = (0, 10) sau đó
z = 110.267447809 giây

Bất kỳ ý tưởng nào?

Code:

import time 
import random 
import sys 

#-----Function definitions 

def quickSort(array): #random pivot location quicksort. uses extra memory. 
    smaller = [] 
    greater = [] 
    if len(array) <= 1: 
     return array 
    pivotVal = array[random.randint(0, len(array)-1)] 
    array.remove(pivotVal) 
    for items in array: 
     if items <= pivotVal: 
      smaller.append(items) 
     else: 
      greater.append(items) 
    return concat(quickSort(smaller), pivotVal, quickSort(greater)) 

def concat(before, pivot, after): 
    new = [] 
    for items in before: 
     new.append(items) 
    new.append(pivot) 
    for things in after: 
     new.append(things) 
    return new 

#-----Variable definitions 
list = [] 
iter = 0 
sys.setrecursionlimit(20000) 
start = time.clock() #start the clock 

#-----Generate the list of numbers to sort 
while(iter < 100000): 
    list.append(random.randint(0,10)) #modify this to change sorting speed 
    iter = iter + 1 
timetogenerate = time.clock() - start #current timer - last timer snapshot 

#-----Sort the list of numbers 
list = quickSort(list) 
timetosort = time.clock() - timetogenerate #current timer - last timer snapshot 

#-----Write the list of numbers 
file = open("C:\output.txt", 'w') 
for items in list: 
    file.write(str(items)) 
    file.write("\n") 
file.close() 
timetowrite = time.clock() - timetosort #current timer - last timer snapshot 

#-----Print info 
print "time to start: " + str(start) 
print "time to generate: " + str(timetogenerate) 
print "time to sort: " + str(timetosort) 
print "time to write: " + str(timetowrite) 
totaltime = timetogenerate + timetosort + start 
print "total time: " + str(totaltime) 

------------------- sửa đổi mã mới --------------- -------------

def quickSort(array): #random pivot location quicksort. uses extra memory. 
    smaller = [] 
    greater = [] 
    equal = [] 
    if len(array) <= 1: 
     return array 
    pivotVal = array[random.randint(0, len(array)-1)] 
    array.remove(pivotVal) 
    equal.append(pivotVal) 
    for items in array: 
     if items < pivotVal: 
      smaller.append(items) 
     elif items > pivotVal: 
      greater.append(items) 
     else: 
      equal.append(items) 
    return concat(quickSort(smaller), equal, quickSort(greater)) 

def concat(before, equal, after): 
    new = [] 
    for items in before: 
     new.append(items) 
    for items in equal: 
     new.append(items) 
    for items in after: 
     new.append(items) 
    return new 
+1

Bạn có gặp phải hành vi này sau khi bạn chạy từng cài đặt nhiều lần và lấy trung bình kết quả không? – Davidann

+1

Ngoài ra: không nên mở ("C: \ output.txt", 'w') 'be' mở ("C: \\ output.txt", 'w') '? – Mikel

+0

@David Kết quả khá nhất quán.Điều này áp dụng cho các phạm vi (0,10) (0,100) (0,10000) – anon58192932

Trả lời

34

Tôi nghĩ điều này phải làm với sự lựa chọn trục xoay. Tùy thuộc vào cách bước phân vùng của bạn hoạt động, nếu bạn có nhiều giá trị trùng lặp, thuật toán của bạn có thể bị thoái hóa thành hành vi bậc hai khi phải đối mặt với nhiều bản sao. Ví dụ: giả sử bạn đang cố gắng phân phối nhanh luồng này:

[0 0 0 0 0 0 0 0 0 0 0 0 0] 

Nếu bạn không cẩn thận với cách bạn thực hiện bước phân đoạn, điều này có thể thoái hóa nhanh chóng. Ví dụ: giả sử bạn chọn trục xoay làm 0 đầu tiên, hãy để bạn với mảng

[0 0 0 0 0 0 0 0 0 0 0 0] 

để phân vùng. Thuật toán của bạn có thể nói rằng các giá trị nhỏ hơn là những mảng

[0 0 0 0 0 0 0 0 0 0 0 0] 

Và các giá trị lớn hơn là những mảng

[] 

Đây là trường hợp gây quicksort thoái hóa đến O (n), vì mỗi cuộc gọi đệ quy chỉ thu hẹp kích thước của đầu vào bằng một (cụ thể là, bằng cách kéo phần tử trục).

tôi nhận thấy rằng trong mã của bạn, bước phân vùng của bạn không thực sự làm điều này:

for items in array: 
    if items <= pivotVal: 
     smaller.append(items) 
    else: 
     greater.append(items) 

Cho một dòng đó là một bó toàn bộ các bản sao của cùng một nguyên tố, điều này sẽ đặt tất cả chúng vào một mảng để phân loại đệ quy.

Tất nhiên, điều này có vẻ giống như một trường hợp vô lý - làm thế nào là điều này ở tất cả các kết nối để giảm số lượng các giá trị trong mảng? - nhưng nó thực sự xuất hiện khi bạn sắp xếp rất nhiều yếu tố không khác biệt. Đặc biệt, sau một vài lần phân vùng, bạn có thể nhóm lại với nhau tất cả các phần tử bằng nhau, điều này sẽ đưa bạn vào trường hợp này.

Để thảo luận về cách ngăn chặn điều này xảy ra, có một cuộc trò chuyện thực sự tuyệt vời by Bob Sedgewick and Jon Bentley về cách sửa đổi bước phân vùng để hoạt động nhanh khi có sự hiện diện của các phần tử trùng lặp. Nó được kết nối với Dijkstra's Dutch national flag problem, và các giải pháp của họ thực sự thông minh.

Một tùy chọn hoạt động là phân đoạn đầu vào thành ba nhóm - ít hơn, bằng và lớn hơn. Khi bạn đã phá vỡ đầu vào theo cách này, bạn chỉ cần sắp xếp các nhóm ít hơn và lớn hơn; các nhóm bằng nhau đã được sắp xếp. Liên kết trên để nói chuyện cho thấy làm thế nào để làm điều này nhiều hơn hoặc ít hơn tại chỗ, nhưng kể từ khi bạn đã sử dụng một cách nhanh chóng out-of-place sửa chữa nên được dễ dàng. Đây là nỗ lực của tôi tại đó:

for items in array: 
    if items < pivotVal: 
     smaller.append(items) 
    elif items == pivotVal: 
     equal.append(items) 
    else: 
     greater.append(items) 

Tôi chưa bao giờ viết một dòng Python trong cuộc sống của mình, vì vậy đây có thể là cú pháp hoàn toàn bất hợp pháp. Nhưng tôi hy vọng ý tưởng là rõ ràng! :-)

+1

OK. Các yếu tố lặp đi lặp lại đang giữ danh sách "lớn hơn" và "nhỏ hơn" không cân xứng, đó là chính xác khi hiệu suất của quicksort bắt đầu giảm. – anon58192932

+2

Python của bạn là chủ yếu là chính xác, nhưng cú pháp chính xác là 'elif' thay vì' else if'. –

+0

Mã của tôi đã được sửa đổi và tôi đã xác nhận kết quả. 110 giây giảm xuống còn 4 giây trong trường hợp (0,10). – anon58192932

2

Những điều chúng ta biết:

  1. Thời gian phức tạp cho sắp xếp nhanh chóng của mảng có thứ tự là O(n*logn).
  2. Nếu mảng đã được sắp xếp, nó sẽ giảm xuống còn O(n^2).
  3. hai câu đầu tiên là không rời rạc, tức là gần một mảng là để được sắp xếp, gần gũi hơn là phức tạp của loại nhanh chóng để O(n^2), và đổi lại chúng tôi xáo trộn nó sự phức tạp tiếp cận O(n*logn)

Bây giờ, chúng ta hãy xem thử nghiệm của bạn:

  • Trong cả ba trường hợp, bạn đã sử dụng cùng một số yếu tố. Vì vậy, số n mà bạn đặt tên x luôn là 100000.
  • Trong thử nghiệm đầu tiên của bạn, bạn đã sử dụng các số từ 0 đến 100000, nên lý tưởng với trình tạo số ngẫu nhiên hoàn hảo, bạn sẽ nhận được các số khác nhau trong danh sách tương đối không có thứ tự phù hợp với trường hợp phức tạp O(n*logn).
  • Trong thử nghiệm thứ ba của bạn, bạn đã sử dụng các số từ 0 đến 10 trong danh sách lớn 100000 thành phần. Nó có nghĩa là có khá nhiều bản sao trong danh sách của bạn, làm cho nó gần hơn với một danh sách được sắp xếp hơn trong thử nghiệm đầu tiên. Vì vậy, trong trường hợp đó phức tạp thời gian là gần gũi hơn với O(n^2).

Và với cùng một đủ lớn n bạn có thể nói rằng n*logn > n^2, mà bạn thực sự xác nhận bởi thử nghiệm của mình.

+0

Tôi đồng ý với hầu hết điều đó nhưng nếu tôi có thể tôi không đồng ý một chút. Dữ liệu được tạo ngẫu nhiên và do đó không gần bất kỳ loại cấu trúc được sắp xếp nào. Đúng là phạm vi nhỏ hơn nhiều so với trường hợp (0,10). Tạo một danh sách thứ ba, "bằng", mà quicksort không cần phải phân loại đệ quy, giải quyết vấn đề của tôi. Cảm ơn bạn đã dành thời gian và phản hồi của bạn. – anon58192932

+0

Quan niệm sai lầm này về việc giảm tốc độ xuống O (N^2) với các mảng được sắp xếp là sai. Nó chỉ đúng với một quicksort rất ngây thơ mà luôn luôn chọn các yếu tố đầu tiên hoặc cuối cùng như trục. –

1

Thuật toán quicksort có điểm yếu đã biết - chậm hơn khi dữ liệu được sắp xếp chủ yếu. Khi bạn có 100000 từ 0 đến 10, chúng sẽ gần hơn với 'số lượng được sắp xếp' nhiều hơn 100000 số trong phạm vi từ 0 đến 100000.

Các vấn đề liên quan