पूर्णांक की एक सूची को देखते हुए, मैं यह जानना चाहता हूं कि कौन सी संख्या इनपुट में दिए गए नंबर के सबसे करीब है:
>>> myList = [4, 1, 88, 44, 3]
>>> myNumber = 5
>>> takeClosest(myList, myNumber)
...
4
क्या ऐसा करने का कोई त्वरित तरीका है?
पूर्णांक की एक सूची को देखते हुए, मैं यह जानना चाहता हूं कि कौन सी संख्या इनपुट में दिए गए नंबर के सबसे करीब है:
>>> myList = [4, 1, 88, 44, 3]
>>> myNumber = 5
>>> takeClosest(myList, myNumber)
...
4
क्या ऐसा करने का कोई त्वरित तरीका है?
जवाबों:
यदि हमें यकीन नहीं है कि सूची को क्रमबद्ध किया गया है, तो हम उस अंतर्निहित min()फ़ंक्शन का उपयोग कर सकते हैं , जो उस तत्व को खोजने के लिए है जो निर्दिष्ट संख्या से न्यूनतम दूरी है।
>>> min(myList, key=lambda x:abs(x-myNumber))
4
ध्यान दें कि यह int कीज़ के साथ dicts के साथ भी काम करता है, जैसे {1: "a", 2: "b"}। इस विधि में O (n) समय लगता है।
यदि सूची पहले से ही क्रमबद्ध है, या आप केवल एक बार सरणी को छांटने की कीमत का भुगतान कर सकते हैं, तो @ लॉरिट्स के उत्तर में सचित्र विधि का उपयोग करें जो केवल O (लॉग एन) समय लेता है (ध्यान दें कि यदि सूची पहले से ही क्रमबद्ध है तो जाँच लें) (n) और छँटाई O (n log n) है।)
O(n)जगह है , जहां थोड़ी सी हैकिंग bisectआपको बड़े पैमाने पर सुधार देगी O(log n)(यदि आपका इनपुट सरणी क्रमबद्ध है)।
min, इसे items()एक सूची के बजाय एक शब्दकोश ( ) पर चलाएं और अंत में मूल्य के बजाय कुंजी लौटाएं।
मैं take_closestPEP8 नामकरण सम्मेलनों के अनुरूप कार्य का नाम बदलूंगा।
यदि आपको त्वरित-टू-राइट के रूप में त्वरित-से-निष्पादन का मतलब है , तो एक बहुत ही संकीर्ण मामले में छोड़कर, आपकी पसंद का हथियार नहीं होना minचाहिए । समाधान सूची में हर संख्या की जांच करने की जरूरत हैmin और प्रत्येक संख्या के लिए एक गणना करते हैं। bisect.bisect_leftइसके बजाय का उपयोग करना लगभग हमेशा तेज होता है।
"लगभग" इस तथ्य से आता है कि bisect_leftकाम करने के लिए सूची को क्रमबद्ध करने की आवश्यकता होती है। उम्मीद है, आपका उपयोग मामला ऐसा है कि आप सूची को एक बार सॉर्ट कर सकते हैं और फिर इसे अकेले छोड़ सकते हैं। यदि नहीं, तब भी जब तक आपको हर बार कॉल करने से पहले सॉर्ट करने की आवश्यकता न होtake_closest , bisectमॉड्यूल संभवतः शीर्ष पर बाहर आ जाएगा। यदि आप संदेह में हैं, तो दोनों का प्रयास करें और वास्तविक दुनिया अंतर को देखें।
from bisect import bisect_left
def take_closest(myList, myNumber):
"""
Assumes myList is sorted. Returns closest value to myNumber.
If two numbers are equally close, return the smallest number.
"""
pos = bisect_left(myList, myNumber)
if pos == 0:
return myList[0]
if pos == len(myList):
return myList[-1]
before = myList[pos - 1]
after = myList[pos]
if after - myNumber < myNumber - before:
return after
else:
return before
बिसेक एक सूची को बार-बार रोकने और myNumberमध्य मान को देखकर पता लगाने का काम करता है कि कौन सा आधा होना है। इसका मतलब यह है कि O (n) के विपरीत चलने का समय O (लॉग एन) है के समय से चल रहा है उच्चतम जवाब मतदान । यदि हम दो तरीकों की तुलना करते हैं और दोनों को छँटाई के साथ आपूर्ति myListकरते हैं, तो ये परिणाम हैं:
$ अजगर-एम टाइमटाइम -s " निकटतम आयात से take_closest यादृच्छिक आयात रैंडिंट से a = श्रेणी (-1000, 1000, 10) "" take_closest (ए, रैंडिंट (-1100, 1100)) " 100000 लूप्स, सर्वश्रेष्ठ 3: 2.22 usec प्रति लूप $ अजगर-एम टाइमटाइम -s " with_min के निकटतम आयात से यादृच्छिक आयात रैंडिंट से a = रेंज (-1000, 1000, 10) "with_min (ए, रैंडिंट (-1100, 1100))" 10000 लूप्स, सर्वश्रेष्ठ 3: 43.9 usec प्रति लूप
तो इस विशेष परीक्षण में, bisectलगभग 20 गुना तेज है। लंबी सूचियों के लिए, अंतर अधिक होगा।
क्या होगा यदि हम उस छँटाई को दूर करने के लिए खेल मैदान को समतल करते हैं myListजिसे क्रमबद्ध किया जाना चाहिए? मान लीजिए कि हम हर बार take_closest कॉल की गई सूची की एक कॉपी को minहल करते हुए छोड़ देते हैं। उपरोक्त परीक्षण में 200-आइटम सूची का उपयोग करना,bisect समाधान अभी भी सबसे तेज़ है, हालांकि केवल लगभग 30%।
यह एक अजीब परिणाम है, यह देखते हुए कि सॉर्टिंग स्टेप O (n log (n)) है ! एकमात्र कारण minअभी भी खो रहा है कि छँटाई अत्यधिक इष्टतम सी कोड में की जाती है, जबकि minहर आइटम के लिए लंबो फ़ंक्शन को कॉल करने के साथ-साथ प्लोड करना पड़ता है। जैसा कि myListआकार में बढ़ता है, minसमाधान अंततः तेज होगा। ध्यान दें कि हमें minजीतने के लिए समाधान के लिए इसके पक्ष में सब कुछ स्टैक करना पड़ा ।
a=range(-1000,1000,2);random.shuffle(a)हैं तो आप पाएंगे कि takeClosest(sorted(a), b)यह धीमा हो जाएगा।
getClosestहर प्रकार के लिए एक से अधिक बार कॉल किया जा सकता है, यह तेज हो जाएगा, और सॉर्ट-वन्स यूज केस के लिए, यह एक नो-ब्रेनर है।
myListपहले से ही है, np.arrayतो इसके np.searchsortedस्थान पर उपयोग bisectतेज है।
>>> takeClosest = lambda num,collection:min(collection,key=lambda x:abs(x-num))
>>> takeClosest(5,[4,1,88,44,3])
4
एक लैम्ब्डा "अनाम" फ़ंक्शन (एक फ़ंक्शन जिसमें कोई नाम नहीं है) लिखने का एक विशेष तरीका है। आप इसे किसी भी नाम को निर्दिष्ट कर सकते हैं जिसे आप चाहते हैं क्योंकि लैम्बडा एक अभिव्यक्ति है।
उपरोक्त लिखने का "लंबा" तरीका होगा:
def takeClosest(num,collection):
return min(collection,key=lambda x:abs(x-num))
def closest(list, Number):
aux = []
for valor in list:
aux.append(abs(Number-valor))
return aux.index(min(aux))
यह कोड आपको सूची में निकटतम संख्या की संख्या का सूचकांक देगा।
केनीटीएम द्वारा दिया गया समाधान समग्र रूप से सबसे अच्छा है, लेकिन जिन मामलों में आप इसका उपयोग नहीं कर सकते हैं (जैसे कि ब्रीथॉन), यह कार्य कार्य करेगा
सूची में दर्ज करें और वर्तमान निकटतम संख्या की तुलना करें abs(currentNumber - myNumber):
def takeClosest(myList, myNumber):
closest = myList[0]
for i in range(1, len(myList)):
if abs(i - myNumber) < closest:
closest = i
return closest
if abs(myList[i] - myNumber) < abs(closest - myNumber): closest = myList[i];। हालांकि पहले से बेहतर है कि मूल्य की दुकान।
यह नोट करना महत्वपूर्ण है कि लॉजिट्स के बाइसेक्ट का उपयोग करने का सुझाव विचार वास्तव में MyList से MyNumber में निकटतम मूल्य नहीं पाता है। इसके बजाय, द्विविभाजित में अगले मूल्य पाता आदेश MyList में MyNumber के बाद। इसलिए ओपी के मामले में आपको वास्तव में 4 की स्थिति के बजाय 44 का स्थान मिलेगा।
>>> myList = [1, 3, 4, 44, 88]
>>> myNumber = 5
>>> pos = (bisect_left(myList, myNumber))
>>> myList[pos]
...
44
वह मान प्राप्त करने के लिए जो 5 से निकटतम है, आप सूची को एक सरणी में परिवर्तित करने की कोशिश कर सकते हैं और इस तरह से अंक से argmin का उपयोग कर सकते हैं।
>>> import numpy as np
>>> myNumber = 5
>>> myList = [1, 3, 4, 44, 88]
>>> myArray = np.array(myList)
>>> pos = (np.abs(myArray-myNumber)).argmin()
>>> myArray[pos]
...
4
मैं नहीं जानता कि यह कितना तेज़ होगा, हालांकि, मेरा अनुमान "बहुत नहीं" होगा।
np.searchsortedइसके बजाय उपयोग कर सकते हैं bisect_left। और @Kanat सही है - लॉरिट्ज़ समाधान करता है कोड है जो की पसंद है जो दो उम्मीदवारों के करीब है शामिल हैं।
गुस्तावो लीमा के जवाब पर विस्तार। एक ही चीज को पूरी तरह से नई सूची बनाए बिना किया जा सकता है। सूची में मूल्यों को FORलूप की प्रगति के रूप में अंतर के साथ बदला जा सकता है।
def f_ClosestVal(v_List, v_Number):
"""Takes an unsorted LIST of INTs and RETURNS INDEX of value closest to an INT"""
for _index, i in enumerate(v_List):
v_List[_index] = abs(v_Number - i)
return v_List.index(min(v_List))
myList = [1, 88, 44, 4, 4, -2, 3]
v_Num = 5
print(f_ClosestVal(myList, v_Num)) ## Gives "3," the index of the first "4" in the list.
अगर मैं @ लॉरिज के उत्तर में जोड़ सकता हूं
रन त्रुटि न होने के लिए, bisect_leftलाइन से पहले एक शर्त जोड़ना न भूलें :
if (myNumber > myList[-1] or myNumber < myList[0]):
return False
तो पूर्ण कोड की तरह दिखेगा:
from bisect import bisect_left
def takeClosest(myList, myNumber):
"""
Assumes myList is sorted. Returns closest value to myNumber.
If two numbers are equally close, return the smallest number.
If number is outside of min or max return False
"""
if (myNumber > myList[-1] or myNumber < myList[0]):
return False
pos = bisect_left(myList, myNumber)
if pos == 0:
return myList[0]
if pos == len(myList):
return myList[-1]
before = myList[pos - 1]
after = myList[pos]
if after - myNumber < myNumber - before:
return after
else:
return before