मैं विभिन्न प्रदर्शन नोट्स के साथ सरल उत्तर पर विस्तार करना चाहता हूं। np.linalg.norm आपकी आवश्यकता से अधिक शायद करेगा:
dist = numpy.linalg.norm(a-b)
सबसे पहले - यह फ़ंक्शन एक सूची पर काम करने और सभी मानों को वापस करने के लिए डिज़ाइन किया गया है, उदाहरण के लिए pA
बिंदुओं के सेट से दूरी की तुलना करने के लिए sP
:
sP = set(points)
pA = point
distances = np.linalg.norm(sP - pA, ord=2, axis=1.) # 'distances' is a list
कई बातें याद रखें:
- पायथन फ़ंक्शन कॉल महंगे हैं।
- [नियमित] पायथन नाम लुक्स को कैश नहीं करता है।
इसलिए
def distance(pointA, pointB):
dist = np.linalg.norm(pointA - pointB)
return dist
यह दिखने में उतना मासूम नहीं है।
>>> dis.dis(distance)
2 0 LOAD_GLOBAL 0 (np)
2 LOAD_ATTR 1 (linalg)
4 LOAD_ATTR 2 (norm)
6 LOAD_FAST 0 (pointA)
8 LOAD_FAST 1 (pointB)
10 BINARY_SUBTRACT
12 CALL_FUNCTION 1
14 STORE_FAST 2 (dist)
3 16 LOAD_FAST 2 (dist)
18 RETURN_VALUE
सबसे पहले - हर बार जब हम इसे कॉल करते हैं, तो हमें "एनपी" के लिए एक ग्लोबल लुकअप करना पड़ता है, "लिनलैग" के लिए एक स्कोप्ड लुकअप और "नॉर्थ" के लिए एक स्कॉप्ड लुकअप, और केवल फंक्शन को कॉल करने के ओवरहेड दर्जनों अजगर के बराबर हो सकता है निर्देश।
अंत में, हमने परिणाम को स्टोर करने और वापसी के लिए इसे फिर से लोड करने के लिए दो ऑपरेशन बर्बाद किए ...
पहले सुधार पर पास: लुकअप को तेज़ करें, स्टोर को छोड़ें
def distance(pointA, pointB, _norm=np.linalg.norm):
return _norm(pointA - pointB)
हम कहीं अधिक सुव्यवस्थित हैं:
>>> dis.dis(distance)
2 0 LOAD_FAST 2 (_norm)
2 LOAD_FAST 0 (pointA)
4 LOAD_FAST 1 (pointB)
6 BINARY_SUBTRACT
8 CALL_FUNCTION 1
10 RETURN_VALUE
फ़ंक्शन कॉल ओवरहेड अभी भी कुछ काम करने के लिए, हालांकि। और आप यह निर्धारित करने के लिए मानदंड करना चाहेंगे कि क्या आप स्वयं गणित को बेहतर कर सकते हैं:
def distance(pointA, pointB):
return (
((pointA.x - pointB.x) ** 2) +
((pointA.y - pointB.y) ** 2) +
((pointA.z - pointB.z) ** 2)
) ** 0.5 # fast sqrt
कुछ प्लेटफार्मों पर, **0.5
की तुलना में तेज है math.sqrt
। आपकी माइलेज भिन्न हो सकती है।
**** उन्नत प्रदर्शन नोट्स।
आप दूरी की गणना क्यों कर रहे हैं? यदि एकमात्र उद्देश्य इसे प्रदर्शित करना है,
print("The target is %.2fm away" % (distance(a, b)))
के साथ कदम। लेकिन अगर आप दूरी की तुलना कर रहे हैं, रेंज चेक आदि कर रहे हैं, तो मैं कुछ उपयोगी प्रदर्शन टिप्पणियों को जोड़ना चाहूंगा।
आइए, दो मामलों को लें: किसी सूची में दूरी के आधार पर छँटाई या किसी सीमा में कमी को पूरा करने वाली वस्तुओं की सूची बनाना।
# Ultra naive implementations. Hold onto your hat.
def sort_things_by_distance(origin, things):
return things.sort(key=lambda thing: distance(origin, thing))
def in_range(origin, range, things):
things_in_range = []
for thing in things:
if distance(origin, thing) <= range:
things_in_range.append(thing)
पहली चीज़ जो हमें याद रखने की ज़रूरत है कि हम दूरी की गणना करने के लिए पाइथागोरस का उपयोग कर रहे हैं ( dist = sqrt(x^2 + y^2 + z^2)
) इसलिए हम बहुत अधिक sqrt
कॉल कर रहे हैं। गणित 101:
dist = root ( x^2 + y^2 + z^2 )
:.
dist^2 = x^2 + y^2 + z^2
and
sq(N) < sq(M) iff M > N
and
sq(N) > sq(M) iff N > M
and
sq(N) = sq(M) iff N == M
संक्षेप में: जब तक हमें वास्तव में एक्स ^ 2 के बजाय एक्स की एक इकाई में दूरी की आवश्यकता होती है, हम गणना के सबसे कठिन हिस्से को समाप्त कर सकते हैं।
# Still naive, but much faster.
def distance_sq(left, right):
""" Returns the square of the distance between left and right. """
return (
((left.x - right.x) ** 2) +
((left.y - right.y) ** 2) +
((left.z - right.z) ** 2)
)
def sort_things_by_distance(origin, things):
return things.sort(key=lambda thing: distance_sq(origin, thing))
def in_range(origin, range, things):
things_in_range = []
# Remember that sqrt(N)**2 == N, so if we square
# range, we don't need to root the distances.
range_sq = range**2
for thing in things:
if distance_sq(origin, thing) <= range_sq:
things_in_range.append(thing)
महान, दोनों कार्य अब कोई महंगा वर्गमूल नहीं करते हैं। यह बहुत तेज हो जाएगा। हम इसे जनरेटर में परिवर्तित करके in_range को भी सुधार सकते हैं:
def in_range(origin, range, things):
range_sq = range**2
yield from (thing for thing in things
if distance_sq(origin, thing) <= range_sq)
यह विशेष रूप से लाभ है अगर आप कुछ कर रहे हैं जैसे:
if any(in_range(origin, max_dist, things)):
...
लेकिन अगर आप अगली चीज़ जो करने जा रहे हैं, उसके लिए एक दूरी की आवश्यकता होती है,
for nearby in in_range(origin, walking_distance, hotdog_stands):
print("%s %.2fm" % (nearby.name, distance(origin, nearby)))
पैदावार tuples पर विचार करें:
def in_range_with_dist_sq(origin, range, things):
range_sq = range**2
for thing in things:
dist_sq = distance_sq(origin, thing)
if dist_sq <= range_sq: yield (thing, dist_sq)
यह विशेष रूप से उपयोगी हो सकता है यदि आप श्रृंखला रेंज जांच कर सकते हैं ('ऐसी चीजें खोजें जो एक्स के पास हैं और एन के वाई के भीतर हैं', क्योंकि आपको फिर से दूरी की गणना नहीं करनी है)।
लेकिन क्या होगा यदि हम वास्तव में बड़ी सूची खोज रहे हैं things
और हम उनमें से बहुत से लोगों के प्रत्याशित होने का अनुमान लगा रहे हैं?
वास्तव में एक बहुत ही सरल अनुकूलन है:
def in_range_all_the_things(origin, range, things):
range_sq = range**2
for thing in things:
dist_sq = (origin.x - thing.x) ** 2
if dist_sq <= range_sq:
dist_sq += (origin.y - thing.y) ** 2
if dist_sq <= range_sq:
dist_sq += (origin.z - thing.z) ** 2
if dist_sq <= range_sq:
yield thing
यह उपयोगी है या नहीं यह 'चीजों' के आकार पर निर्भर करेगा।
def in_range_all_the_things(origin, range, things):
range_sq = range**2
if len(things) >= 4096:
for thing in things:
dist_sq = (origin.x - thing.x) ** 2
if dist_sq <= range_sq:
dist_sq += (origin.y - thing.y) ** 2
if dist_sq <= range_sq:
dist_sq += (origin.z - thing.z) ** 2
if dist_sq <= range_sq:
yield thing
elif len(things) > 32:
for things in things:
dist_sq = (origin.x - thing.x) ** 2
if dist_sq <= range_sq:
dist_sq += (origin.y - thing.y) ** 2 + (origin.z - thing.z) ** 2
if dist_sq <= range_sq:
yield thing
else:
... just calculate distance and range-check it ...
और फिर, dist_sq उपज पर विचार करें। हमारा हॉटडॉग उदाहरण तब बनता है:
# Chaining generators
info = in_range_with_dist_sq(origin, walking_distance, hotdog_stands)
info = (stand, dist_sq**0.5 for stand, dist_sq in info)
for stand, dist in info:
print("%s %.2fm" % (stand, dist))