ด้วยโซลูชั่นมากมายที่นำเสนอฉันประหลาดใจที่ไม่มีใครเสนอสิ่งที่ฉันต้องการพิจารณาเห็นได้ชัด (สำหรับองค์ประกอบที่ไม่ใช่ hashable แต่เทียบเคียง) - [ itertools.groupby
] [1] itertools
นำเสนอฟังก์ชั่นที่รวดเร็วใช้ซ้ำได้และให้คุณมอบหมายตรรกะที่ยุ่งยากบางอย่างให้กับส่วนประกอบไลบรารีมาตรฐานที่ผ่านการทดสอบ ลองพิจารณาตัวอย่าง:
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]
แน่นอนว่านี่อาจจะเขียนสั้นกระชับกว่า แต่ฉันตั้งใจจะให้ความชัดเจนสูงสุด ข้อความทั้งสองprint
สามารถไม่แสดงข้อคิดเห็นเพื่อให้เห็นเครื่องจักรทำงานได้ดีขึ้น ตัวอย่างเช่นด้วยการพิมพ์ไม่ใส่เครื่องหมายข้อคิดเห็น:
print most_common(['goose', 'duck', 'duck', 'goose'])
ส่งเสียง:
SL: [('duck', 1), ('duck', 2), ('goose', 0), ('goose', 3)]
item 'duck', count 2, minind 1
item 'goose', count 2, minind 0
goose
ดังที่คุณเห็นSL
เป็นรายการของคู่แต่ละรายการตามด้วยดัชนีของรายการในรายการเดิม (เพื่อใช้เงื่อนไขหลักที่ว่าหากรายการ "ทั่วไป" ที่มีจำนวนสูงสุดเท่ากันคือ> 1 ผลลัพธ์จะต้อง จะเกิดขึ้นเร็วที่สุด)
groupby
จัดกลุ่มตามรายการเท่านั้น (ผ่านoperator.itemgetter
) ฟังก์ชั่นเสริมเรียกหนึ่งครั้งต่อการจัดกลุ่มในระหว่างการmax
คำนวณรับและปลดกลุ่มภายใน - สิ่งอันดับสองรายการ(item, iterable)
ที่รายการของ iterable นั้นยังเป็นสิ่งอันดับสองรายการ(item, original index)
[[รายการของSL
]]
จากนั้นฟังก์ชั่นเสริมจะใช้ลูปเพื่อกำหนดทั้งจำนวนของรายการในการทำซ้ำของกลุ่มและดัชนีดั้งเดิมขั้นต่ำ มันจะส่งกลับค่าเหล่านั้นว่าเป็น "คีย์คุณภาพ" ที่รวมกันซึ่งมีการเปลี่ยนแปลงเครื่องหมายดัชนีขั้นต่ำดังนั้นการmax
ดำเนินการจะพิจารณารายการเหล่านั้น "ดีกว่า" ที่เกิดขึ้นก่อนหน้านี้ในรายการเดิม
รหัสนี้อาจจะง่ายมากถ้ามันกังวลเล็ก ๆ น้อย ๆน้อยเกี่ยวกับปัญหาใหญ่-O ในเวลาและพื้นที่เช่น ...
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]
แนวคิดพื้นฐานที่เหมือนกันเพียงแค่แสดงอย่างเรียบง่ายและกะทัดรัดมากขึ้น ... แต่อนิจจาพื้นที่เสริม O (N) พิเศษ (เพื่อรวบรวมรายการที่น่าสนใจของกลุ่ม) และเวลา O (N กำลังสอง) (เพื่อรับL.index
ทุกรายการ) . ในขณะที่การออปติไมซ์ก่อนวัยอันควรเป็นรากของความชั่วร้ายทั้งหมดในการเขียนโปรแกรมจงใจเลือกวิธี O (N กำลังสอง) เมื่อ O (N log N) หนึ่งใช้ได้พร้อมใช้งานมากเกินไปต่อการขยายขีดความสามารถ! -)
ในที่สุดสำหรับผู้ที่ต้องการ "oneliners" เพื่อความชัดเจนและประสิทธิภาพรุ่นโบนัส 1 ซับที่มีชื่อ mangled เหมาะสม :-)
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]