Với rất nhiều giải pháp được đề xuất, tôi ngạc nhiên không ai đề xuất những gì tôi coi là một giải pháp rõ ràng (đối với các yếu tố không thể băm nhưng có thể so sánh được) - [ itertools.groupby
] [1]. itertools
cung cấp chức năng nhanh, có thể tái sử dụng và cho phép bạn ủy thác một số logic phức tạp cho các thành phần thư viện tiêu chuẩn được kiểm tra tốt. Xem xét ví dụ:
import itertools
import operator
def most_common(L):
# get an iterable of (item, iterable) pairs
SL = sorted((x, i) for i, x in enumerate(L))
# print 'SL:', SL
groups = itertools.groupby(SL, key=operator.itemgetter(0))
# auxiliary function to get "quality" for an item
def _auxfun(g):
item, iterable = g
count = 0
min_index = len(L)
for _, where in iterable:
count += 1
min_index = min(min_index, where)
# print 'item %r, count %r, minind %r' % (item, count, min_index)
return count, -min_index
# pick the highest-count/earliest item
return max(groups, key=_auxfun)[0]
Điều này có thể được viết chính xác hơn, tất nhiên, nhưng tôi đang hướng đến sự rõ ràng tối đa. Hai print
tuyên bố có thể không được chú ý để thấy rõ hơn máy móc đang hoạt động; ví dụ, với các bản in không bị lỗi:
print most_common(['goose', 'duck', 'duck', 'goose'])
phát ra:
SL: [('duck', 1), ('duck', 2), ('goose', 0), ('goose', 3)]
item 'duck', count 2, minind 1
item 'goose', count 2, minind 0
goose
Như bạn thấy, SL
là một danh sách các cặp, mỗi cặp một mục theo sau là chỉ mục của mục trong danh sách ban đầu (để thực hiện điều kiện chính là, nếu các mục "phổ biến nhất" có cùng số lượng cao nhất là> 1, kết quả phải là người xuất hiện sớm nhất).
groupby
nhóm theo mục chỉ (thông qua operator.itemgetter
). Hàm phụ trợ, được gọi một lần cho mỗi nhóm trong quá trình max
tính toán, nhận và giải nén bên trong một nhóm - một tuple với hai mục (item, iterable)
trong đó các mục của iterable cũng là các bộ dữ liệu hai mục, (item, original index)
[[các mục của SL
]].
Sau đó, hàm phụ trợ sử dụng một vòng lặp để xác định cả số lượng mục trong vòng lặp của nhóm và chỉ mục gốc tối thiểu; nó trả về những cái đó là "khóa chất lượng" kết hợp, với chỉ số tối thiểu được thay đổi để max
hoạt động sẽ xem xét "tốt hơn" những mục đã xảy ra trước đó trong danh sách ban đầu.
Mã này có thể đơn giản hơn nhiều nếu nó lo lắng hơn một chút về các vấn đề lớn về thời gian và không gian, ví dụ ...
def most_common(L):
groups = itertools.groupby(sorted(L))
def _auxfun((item, iterable)):
return len(list(iterable)), -L.index(item)
return max(groups, key=_auxfun)[0]
cùng một ý tưởng cơ bản, chỉ được thể hiện đơn giản và gọn gàng hơn ... nhưng, than ôi, một không gian phụ O (N) bổ sung (để thể hiện các lần lặp của nhóm vào danh sách) và thời gian O (N bình phương) (để có được L.index
mọi mục) . Mặc dù tối ưu hóa sớm là gốc rễ của mọi tội lỗi trong lập trình, nhưng việc cố tình chọn cách tiếp cận O (N bình phương) khi phương pháp O (N log N) có sẵn chỉ là đi quá nhiều so với khả năng mở rộng! -)
Cuối cùng, đối với những người thích "oneliners" cho sự rõ ràng và hiệu suất, một phiên bản 1 phần thưởng có tên được xáo trộn phù hợp :-).
from itertools import groupby as g
def most_common_oneliner(L):
return max(g(sorted(L)), key=lambda(x, v):(len(list(v)),-L.index(x)))[0]