2010-02-18 6 views
6

में एक सूची से एन न्यूनतम या अधिकतम तत्व प्राप्त करने का तेज़ तरीका वर्तमान में मेरे पास एक लम्बा फ़ंक्शन f का उपयोग करके क्रमबद्ध एक लंबी सूची है। मैं फिर पहले पांच तत्वों से यादृच्छिक तत्व चुनता हूं। कुछ ऐसा:पायथन

f = lambda x: some_function_of(x, local_variable) 
my_list.sort(key=f) 
foo = choice(my_list[:4]) 

प्रोफाइलर के मुताबिक यह मेरे कार्यक्रम में एक बाधा है। मैं चीजों को कैसे गति दे सकता हूं? क्या तत्वों को पुनर्प्राप्त करने के लिए एक तेज़, अंतर्निहित तरीका है (सिद्धांत में पूरी सूची को सॉर्ट करने की आवश्यकता नहीं है)। धन्यवाद।

+0

'some_function_of' महंगा है? – SilentGhost

+1

http://stackoverflow.com/questions/2243542/how-to- कुशलतापूर्वक-get-the-k-bigger-elements-of-a-list-in-python – fortran

उत्तर

8

heapq.nlargest या heapq.nsmallest का उपयोग करें।

उदाहरण के लिए

:

import heapq 

elements = heapq.nsmallest(4, my_list, key=f) 
foo = choice(elements) 

यह हे ले जाएगा (N + KlogN) समय (जहां कश्मीर तत्वों की संख्या लौटे है और N सूची के आकार है) है, जो हे की तुलना में तेजी है (NlogN) सामान्य प्रकार के लिए जब के 0 एन

+0

हम्म। अब तक यह वास्तव में मामूली धीमी है। एन 8000 है और के 5 है। –

+0

यह संभव है कि बाधा कुछ nfunction_of पर एन कॉल है और तुलना तुलना में बहुत तेज है, इस मामले में उस कार्य को बेहतर करने के अलावा आप इतना कुछ नहीं कर सकते हैं। एक और संभावना यह है कि डेटा लगभग पहले से ही क्रमबद्ध है, इस मामले में पायथन का प्रकार बहुत तेज होगा। – interjay

+0

आप शायद सही हैं। अब के लिए heapq.nsmallest के साथ रहना होगा क्योंकि यह इरादा व्यक्त करता है। धन्यवाद। –

1

से थोड़ा सा सापेक्ष है तो यह औसत पर रैखिक समय (ओ (एन)) में वास्तव में संभव है।

def partition(seq, pred, start=0, end=-1): 
    if end == -1: end = len(seq) 
    while True: 
     while True: 
      if start == end: return start 
      if not pred(seq[start]): break 
      start += 1 
     while True: 
      if pred(seq[end-1]): break 
      end -= 1 
      if start == end: return start 
     seq[start], seq[end-1] = seq[end-1], seq[start] 
     start += 1 
     end -= 1 

जो एक nth_element एल्गोरिथ्म द्वारा इस्तेमाल किया जा सकता:

nth_element(my_list, 4, key=f) 
:

def nth_element(seq_in, n, key=lambda x:x): 
    start, end = 0, len(seq_in) 
    seq = [(x, key(x)) for x in seq_in] 

    def partition_pred(x): return x[1] < seq[end-1][1] 

    while start != end: 
     pivot = (end + start) // 2 
     seq[pivot], seq[end - 1] = seq[end - 1], seq[pivot] 
     pivot = partition(seq, partition_pred, start, end) 
     seq[pivot], seq[end - 1] = seq[end - 1], seq[pivot] 
     if pivot == n: break 
     if pivot < n: start = pivot + 1 
     else: end = pivot 

    seq_in[:] = (x for x, k in seq) 

इन को देखते हुए, बस के साथ अपने दूसरे (प्रकार) लाइन की जगह

आप एक विभाजन एल्गोरिथ्म की जरूरत है

+0

जिस तरह से मैं कुंजी तर्क को समझता हूं जिसे सॉर्ट फ़ंक्शंस में जोड़ा गया है, यह है कि इसका उपयोग आंतरिक रूप से डीएसयू (सजाने-क्रमबद्ध) को कार्यान्वित करने के लिए किया जाता है, जैसे कि संभावित रूप से महंगे कुंजी फ़ंक्शन को केवल किसी एक तत्व के लिए बुलाया जाता है सूची। मुझे लगता है कि आपकी विधि एक ही सूची तत्व के लिए कई बार महत्वपूर्ण कार्य को कॉल करेगी। – PaulMcG

+0

@ पॉल अच्छा बिंदु - मैंने इसे अब डीएसयू का उपयोग करने के लिए संपादित किया है। –