Trong câu hỏi của bạn, bạn đã yêu cầu cách nhanh nhất để làm điều đó. Như đã được chứng minh nhiều lần, đặc biệt là với Python, trực giác không phải là một hướng dẫn đáng tin cậy: bạn cần phải đo lường.
Dưới đây là một thử nghiệm đơn giản của việc triển khai khác nhau:
import sys
from collections import Counter, defaultdict
from itertools import groupby
from operator import itemgetter
from timeit import timeit
L = [1,2,45,55,5,4,4,4,4,4,4,5456,56,6,7,67]
def max_occurrences_1a(seq=L):
"dict iteritems"
c = dict()
for item in seq:
c[item] = c.get(item, 0) + 1
return max(c.iteritems(), key=itemgetter(1))
def max_occurrences_1b(seq=L):
"dict items"
c = dict()
for item in seq:
c[item] = c.get(item, 0) + 1
return max(c.items(), key=itemgetter(1))
def max_occurrences_2(seq=L):
"defaultdict iteritems"
c = defaultdict(int)
for item in seq:
c[item] += 1
return max(c.iteritems(), key=itemgetter(1))
def max_occurrences_3a(seq=L):
"sort groupby generator expression"
return max(((k, sum(1 for i in g)) for k, g in groupby(sorted(seq))), key=itemgetter(1))
def max_occurrences_3b(seq=L):
"sort groupby list comprehension"
return max([(k, sum(1 for i in g)) for k, g in groupby(sorted(seq))], key=itemgetter(1))
def max_occurrences_4(seq=L):
"counter"
return Counter(L).most_common(1)[0]
versions = [max_occurrences_1a, max_occurrences_1b, max_occurrences_2, max_occurrences_3a, max_occurrences_3b, max_occurrences_4]
print sys.version, "\n"
for vers in versions:
print vers.__doc__, vers(), timeit(vers, number=20000)
Các kết quả trên máy tính của tôi:
2.7.2 (v2.7.2:8527427914a2, Jun 11 2011, 15:22:34)
[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)]
dict iteritems (4, 6) 0.202214956284
dict items (4, 6) 0.208412885666
defaultdict iteritems (4, 6) 0.221301078796
sort groupby generator expression (4, 6) 0.383440971375
sort groupby list comprehension (4, 6) 0.402786016464
counter (4, 6) 0.564319133759
Vì vậy, có vẻ như là giải pháp Counter
không phải là nhanh nhất. Và, trong trường hợp này ít nhất, groupby
là nhanh hơn. defaultdict
là tốt nhưng bạn phải trả một chút cho sự tiện lợi của nó; nhanh hơn một chút để sử dụng số dict
thông thường với số get
.
Điều gì sẽ xảy ra nếu danh sách lớn hơn nhiều? Thêm L *= 10000
để thử nghiệm trên và giảm số lần lặp lại 200:
dict iteritems (4, 60000) 10.3451900482
dict items (4, 60000) 10.2988479137
defaultdict iteritems (4, 60000) 5.52838587761
sort groupby generator expression (4, 60000) 11.9538850784
sort groupby list comprehension (4, 60000) 12.1327362061
counter (4, 60000) 14.7495789528
Bây giờ defaultdict
là người chiến thắng rõ ràng. Vì vậy, có lẽ chi phí của phương pháp 'get' và sự mất mát của việc thêm vào tại chỗ cho biết thêm (một kiểm tra của mã được tạo ra còn lại như là một bài tập).
Nhưng với dữ liệu thử nghiệm đã sửa đổi, số lượng giá trị mặt hàng duy nhất không thay đổi nên có lẽ dict
và defaultdict
có lợi thế hơn so với các triển khai khác. Vậy điều gì sẽ xảy ra nếu chúng ta sử dụng danh sách lớn hơn nhưng tăng đáng kể số lượng các mặt hàng độc đáo? Thay thế khởi của L với:
LL = [1,2,45,55,5,4,4,4,4,4,4,5456,56,6,7,67]
L = []
for i in xrange(1,10001):
L.extend(l * i for l in LL)
dict iteritems (2520, 13) 17.9935798645
dict items (2520, 13) 21.8974409103
defaultdict iteritems (2520, 13) 16.8289561272
sort groupby generator expression (2520, 13) 33.853593111
sort groupby list comprehension (2520, 13) 36.1303369999
counter (2520, 13) 22.626899004
Bây giờ Counter
rõ ràng nhanh hơn so với các giải pháp groupby
nhưng vẫn là chậm hơn so với các phiên bản của iteritems
dict
và defaultdict
.
Điểm của những ví dụ này không phải là để tạo ra giải pháp tối ưu. Vấn đề là thường không có một giải pháp chung tối ưu. Ngoài ra còn có các tiêu chí hiệu suất khác.Các yêu cầu bộ nhớ sẽ khác nhau đáng kể trong số các giải pháp và, khi kích thước của đầu vào tăng lên, các yêu cầu bộ nhớ có thể trở thành yếu tố quan trọng trong việc lựa chọn thuật toán.
Tóm lại: tất cả đều phụ thuộc và bạn cần đo lường.
Bạn nói rằng bạn có thể giải quyết nó. Nó cũng sẽ mang tính giáo dục cho người khác nếu bạn có thể cung cấp giải pháp của riêng bạn như là một điểm khởi đầu. –