पूर्णांक की सूची से, दिए गए मान के निकटतम संख्या प्राप्त करें


158

पूर्णांक की एक सूची को देखते हुए, मैं यह जानना चाहता हूं कि कौन सी संख्या इनपुट में दिए गए नंबर के सबसे करीब है:

>>> myList = [4, 1, 88, 44, 3]
>>> myNumber = 5
>>> takeClosest(myList, myNumber)
...
4

क्या ऐसा करने का कोई त्वरित तरीका है?


2
सूची में यह हुआ कि सूचकांक को वापस करने के बारे में क्या।
चार्ली पार्कर


1
@ sancho.s अच्छी तरह से देखा। यद्यपि इस प्रश्न के उत्तर उस अन्य प्रश्न के प्रश्नों से बेहतर हैं। इसलिए मैं इस एक के डुप्लिकेट के रूप में दूसरे को बंद करने के लिए वोट करने जा रहा हूं।
जीन-फ्रांकोइस कॉर्बेट

जवाबों:


326

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

>>> min(myList, key=lambda x:abs(x-myNumber))
4

ध्यान दें कि यह int कीज़ के साथ dicts के साथ भी काम करता है, जैसे {1: "a", 2: "b"}। इस विधि में O (n) समय लगता है।


यदि सूची पहले से ही क्रमबद्ध है, या आप केवल एक बार सरणी को छांटने की कीमत का भुगतान कर सकते हैं, तो @ लॉरिट्स के उत्तर में सचित्र विधि का उपयोग करें जो केवल O (लॉग एन) समय लेता है (ध्यान दें कि यदि सूची पहले से ही क्रमबद्ध है तो जाँच लें) (n) और छँटाई O (n log n) है।)


13
जटिलता में बोलते हुए, यह वह O(n)जगह है , जहां थोड़ी सी हैकिंग bisectआपको बड़े पैमाने पर सुधार देगी O(log n)(यदि आपका इनपुट सरणी क्रमबद्ध है)।
mic_e

5
@mic_e: यह सिर्फ लॉरिट्ज का जवाब है
kennytm

3
सूची में यह हुआ कि सूचकांक को वापस करने के बारे में क्या है?
चार्ली पार्कर

@CharlieParker अपना स्वयं का कार्यान्वयन बनाएं min, इसे items()एक सूची के बजाय एक शब्दकोश ( ) पर चलाएं और अंत में मूल्य के बजाय कुंजी लौटाएं।
डस्टिन Oprea

2
या का उपयोग numpy.argminकरने के बजाय minमूल्य के बजाय सूचकांक पाने के लिए।

148

मैं 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जीतने के लिए समाधान के लिए इसके पक्ष में सब कुछ स्टैक करना पड़ा ।


2
सॉर्टिंग के लिए ओ (एन लॉग एन) की आवश्यकता होती है, इसलिए एन बड़े होने पर धीमी हो जाएगी। उदाहरण के लिए, यदि आप उपयोग करते a=range(-1000,1000,2);random.shuffle(a)हैं तो आप पाएंगे कि takeClosest(sorted(a), b)यह धीमा हो जाएगा।
kennytm

3
@KennyTM मैं आपको वह अनुदान दूंगा, और मैं इसे अपने उत्तर में बताऊंगा। लेकिन जब तक getClosestहर प्रकार के लिए एक से अधिक बार कॉल किया जा सकता है, यह तेज हो जाएगा, और सॉर्ट-वन्स यूज केस के लिए, यह एक नो-ब्रेनर है।
लॉरिट्ज वी। थुलो

सूची में यह हुआ कि सूचकांक को वापस करने के बारे में क्या है?
चार्ली पार्कर

यदि myListपहले से ही है, np.arrayतो इसके np.searchsortedस्थान पर उपयोग bisectतेज है।
माइकल हॉल

8
>>> 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))

2
हालांकि, ध्यान दें कि लैम्ब्डा को नामों को सौंपना पीईपी 8 के अनुसार हतोत्साहित करता है ।
हेवलन

6
def closest(list, Number):
    aux = []
    for valor in list:
        aux.append(abs(Number-valor))

    return aux.index(min(aux))

यह कोड आपको सूची में निकटतम संख्या की संख्या का सूचकांक देगा।

केनीटीएम द्वारा दिया गया समाधान समग्र रूप से सबसे अच्छा है, लेकिन जिन मामलों में आप इसका उपयोग नहीं कर सकते हैं (जैसे कि ब्रीथॉन), यह कार्य कार्य करेगा


5

सूची में दर्ज करें और वर्तमान निकटतम संख्या की तुलना करें 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

1
आप सूचकांक भी वापस कर सकते हैं।
चार्ली पार्कर

1
! गलत है! होना चाहिए if abs(myList[i] - myNumber) < abs(closest - myNumber): closest = myList[i];। हालांकि पहले से बेहतर है कि मूल्य की दुकान।
lk_vc

निश्चित रूप से फ़ंक्शन के रूप में यह पहले से ही निकटतम के सूचकांक लौटाता है। इसके लिए ओपी की आवश्यकताओं को पूरा करने के लिए दूसरी अंतिम पंक्ति को निकटतम नहीं पढ़ा जाना चाहिए = myList [i]
पाउला लिविंगस्टोन

2

यह नोट करना महत्वपूर्ण है कि लॉजिट्स के बाइसेक्ट का उपयोग करने का सुझाव विचार वास्तव में 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

मैं नहीं जानता कि यह कितना तेज़ होगा, हालांकि, मेरा अनुमान "बहुत नहीं" होगा।


2
लॉरिट्ज का कार्य सही ढंग से काम करता है। आप बस केवल bisect_left का उपयोग कर रहे हैं, लेकिन लॉरिट्ज ने एक फ़ंक्शन टेकक्लोस्ट (...) का सुझाव दिया है जो अतिरिक्त जांच करता है।
कनाट

यदि आप NumPy का उपयोग करने जा रहे हैं, तो आप np.searchsortedइसके बजाय उपयोग कर सकते हैं bisect_left। और @Kanat सही है - लॉरिट्ज़ समाधान करता है कोड है जो की पसंद है जो दो उम्मीदवारों के करीब है शामिल हैं।
जॉन वाई

1

गुस्तावो लीमा के जवाब पर विस्तार। एक ही चीज को पूरी तरह से नई सूची बनाए बिना किया जा सकता है। सूची में मूल्यों को 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.

1

अगर मैं @ लॉरिज के उत्तर में जोड़ सकता हूं

रन त्रुटि न होने के लिए, 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
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.