পূর্ণসংখ্যার তালিকা থেকে, প্রদত্ত মানের নিকটেতম সংখ্যাটি পান


158

পূর্ণসংখ্যার একটি তালিকা দেওয়া, আমি খুঁজে পেতে চাই যে ইনপুটটিতে দেওয়া সংখ্যার নিকটতম সংখ্যাটি:

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

এটি করার কোনও দ্রুত উপায় আছে?


2
সূচী ফিরে আসার বিষয়ে কী এই তালিকায় ঘটেছিল।
চার্লি পার্কার 20


1
পছন্দ করেছেন যদিও এই প্রশ্নের উত্তরগুলি অন্য প্রশ্নের উত্তরগুলির চেয়ে ভাল। সুতরাং আমি এইটির সদৃশ হিসাবে অন্যটিকে বন্ধ করতে ভোট দিতে যাচ্ছি।
জিন-

উত্তর:


326

যদি আমরা নিশ্চিত না হয়েছি যে তালিকাটি সাজানো হয়েছে তবে আমরা অন্তর্নির্মিত min()ফাংশনটি ব্যবহার করতে পারব , নির্দিষ্ট উপাদান থেকে ন্যূনতম দূরত্ব রয়েছে এমন উপাদানটি সন্ধান করতে।

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

মনে রাখবেন যে এছাড়াও, int- এ কী এর মাধ্যমে dicts সাথে কাজ করে মত {1: "a", 2: "b"}। এই পদ্ধতিতে ও (এন) সময় লাগে।


যদি তালিকাটি ইতিমধ্যে বাছাই করা থাকে, বা আপনি একবারে অ্যারে বাছাইয়ের মূল্য দিতে পারেন তবে @ লরিৎজসের উত্তরে বর্ণিত দ্বিখণ্ডিত পদ্ধতিটি ব্যবহার করুন যা কেবল ও (লগ এন) সময় নেয় (নোট তবে পরীক্ষার তালিকা ইতিমধ্যে বাছাই করা হয়েছে কিনা ও (এন) এবং বাছাই হয় হে (এন লগ এন))


13
জটিলতায় কথা বললে, এটি হ'ল O(n)যেখানে সামান্য হ্যাকিং bisectআপনাকে একটি বৃহত উন্নতি দেবে O(log n)(যদি আপনার ইনপুট অ্যারেটি সাজানো থাকে)।
mic_e

5
@ মমিক_ই: এটি কেবল লরিৎজের উত্তর
কেনেটিএম

3
সূচকে ফিরে আসা সম্পর্কে কী আছে যে তালিকায় এটি ঘটেছে?
চার্লি পার্কার 20

@ চর্লিপার্কার আপনার নিজস্ব বাস্তবায়ন তৈরি করুন min, items()তালিকার পরিবর্তে অভিধান ( ) এ এটি চালান , এবং শেষে মানটির পরিবর্তে কীটি ফিরিয়ে দিন।
ডাস্টিন ওরেপা

2
অথবা numpy.argminপরিবর্তে minমানটির পরিবর্তে সূচক পেতে ব্যবহার করুন ।

148

আমি take_closestপিইপি 8 নামকরণ কনভেনশনগুলির সাথে সামঞ্জস্য করতে ফাংশনটির নাম পরিবর্তন করব ।

দ্রুত-লেখার বিরোধী হিসাবে যদি আপনি দ্রুত-মৃত্যুদন্ড কার্যকর করতে চান, তবে খুব সংকীর্ণ ব্যবহারের ক্ষেত্রে বাদে আপনার পছন্দের অস্ত্র minহওয়া উচিত নয়minসমাধান তালিকায় প্রতি NUMBER পরীক্ষা করার প্রয়োজন এবং প্রতিটি সংখ্যার জন্য একটি গণনা করা যাক। 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করে মধ্যম মানেরটি দেখে কোনটি অর্ধেকের মধ্যে থাকতে হবে তা খুঁজে বের করে কাজ করে । এর অর্থ এটি ও (লগ এন) এর চলমান সময় হিসাবে সর্বোচ্চ ভোট প্রাপ্ত উত্তরের ও (এন) চলমান সময়ের বিপরীতে রয়েছে । যদি আমরা দুটি পদ্ধতির তুলনা করি এবং উভয়কে বাছাই করে সরবরাহ করি তবে myListএগুলি ফলাফল:

"পাইথন-মি টাইমিট-এস"
নিকটতম আমদানি থেকে_ক্লোজস্ট থেকে
এলোমেলো আমদানি র‌্যান্ড্যান্ড থেকে
a = পরিসর (-1000, 1000, 10) "" নিন_ক্লোজস্ট (এ, র্যান্ডিন্ট (-1100, 1100)) "

100000 লুপ, প্রতি লুপে 3: 2.22 ব্যবহারের সেরা

"পাইথন-মি টাইমিট-এস"
_মিনের সাথে নিকটতম আমদানি থেকে
এলোমেলো আমদানি র‌্যান্ড্যান্ড থেকে
a = পরিসর (-1000, 1000, 10) "" সাথে_মিনি (একটি, র্যান্ডেন্ট (-1100, 1100)) "

10000 লুপ, লুপ প্রতি 3: 43.9 ব্যবহারকারীর মধ্যে সেরা

সুতরাং এই বিশেষ পরীক্ষায়, bisectপ্রায় 20 গুণ দ্রুত হয়। দীর্ঘ তালিকার জন্য, পার্থক্য আরও বেশি হবে।

আমরা যদি পূর্বের শর্তটি myListবাছাই করতে হবে তা সরিয়ে প্লেয়িং ফিল্ডকে সমতল করি ? আসুন আমরা সমাধানটি অবিচ্ছিন্ন অবস্থায় ছেড়ে যাওয়ার সময় তালিকার অনুলিপিটি প্রতিবারই সাজানো বলি । উপরের পরীক্ষায় 200-আইটেমের তালিকাটি ব্যবহার করে, সমাধানটি এখনও সবচেয়ে দ্রুত, যদিও কেবল 30% দ্বারা।take_closestminbisect

এটি একটি অদ্ভুত ফলাফল, বিবেচনা করে বাছাইকরণ পদক্ষেপটি হে (এন লগ (এন)) ! minএখনও হারানোর একমাত্র কারণ হ'ল বাছাইটি অত্যন্ত অনুকূল সি কোডে করা হয়, যখন minপ্রতিটি আইটেমের জন্য ল্যাম্বডা ফাংশন কল করার পাশাপাশি প্লড করতে হয়। হিসাবে myListআকার বৃদ্ধি, minসমাধান অবশেষে দ্রুততর হবে। নোট করুন যে minজয়ের সমাধানের জন্য আমাদের সবকিছু তার পক্ষে ছিল ।


2
বাছাইয়ের জন্য নিজেই ও (এন লগ এন) প্রয়োজন, সুতরাং এন বড় হওয়ার সাথে সাথে এটি ধীর হবে। উদাহরণস্বরূপ, আপনি যদি ব্যবহার করেন তবে a=range(-1000,1000,2);random.shuffle(a)এটি takeClosest(sorted(a), b)ধীর হয়ে উঠবে।
কেনেটিএম

3
@ কেনিটিএম আমি আপনাকে এটি প্রদান করব এবং আমি আমার উত্তরে এটি উল্লেখ করব। তবে যতক্ষণ getClosestনা প্রতিটি ধরণের জন্য একাধিকবার ডাকা যেতে পারে, এটি দ্রুত হবে এবং সারণে-একবার ব্যবহারের ক্ষেত্রে এটি কোনও মস্তিষ্কের নয়।
লরিজ ভি ভি থালালো

সূচকে ফিরে আসা সম্পর্কে কী আছে যে তালিকায় এটি ঘটেছে?
চার্লি পার্কার 20

যদি 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

অবশ্যই এটি স্থায়ী হিসাবে ইতিমধ্যে নিকটতম সূচক ফেরত। ওপিটির প্রয়োজনীয়তা পূরণের জন্য দ্বিতীয় শেষ লাইনটি নিকটতম = মাইলিস্ট [i] পড়তে হবে না
পাওলা লিভিংস্টোন

2

এটি লক্ষণীয় গুরুত্বপূর্ণ যে বাইসেক্ট ব্যবহারের জন্য লরিৎজের পরামর্শ ধারণাটি মাইলিস্ট থেকে মাইম্বরের নিকটতম মানটি খুঁজে পায় না। পরিবর্তে, দ্বিখণ্ডিত করা পরবর্তী মান খুঁজে বের করে অর্ডার MyList মধ্যে MyNumber পরে। সুতরাং ওপির ক্ষেত্রে আপনি 4 অবস্থানের পরিবর্তে 44 অবস্থান ফিরে পাবেন returned

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

5 এর নিকটতম মানটি পেতে আপনি তালিকাটিকে একটি অ্যারেতে রূপান্তর করতে এবং এর মতো ন্যাক্পি থেকে আরগমিন ব্যবহার করার চেষ্টা করতে পারেন।

>>> 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
লরিৎসের ফাংশনটি সঠিকভাবে কাজ করে। আপনি কেবল দ্বিখণ্ডিত-বামটি ব্যবহার করছেন তবে লরিৎসকে একটি ক্লোজস্ট (...) ফাংশন দেওয়ার পরামর্শ দিয়েছিল যা অতিরিক্ত পরীক্ষা করে।
কানাত

আপনি যদি NumPy ব্যবহার করতে চলেছেন তবে আপনি এর np.searchsortedপরিবর্তে ব্যবহার করতে পারেন bisect_left। এবং কানাট ঠিক বলেছেন - লরিটসের সমাধানে এমন দুটি কোড রয়েছে যা দুটি প্রার্থীর মধ্যে কাছাকাছি কোনটি বেছে নেয়।
জন ওয়াই

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
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.