আমি বিভিন্ন পারফরম্যান্স নোট সহ সহজ উত্তরটি ব্যাখ্যা করতে চাই। 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)))
বরাবর অগ্রসর. তবে আপনি যদি দূরত্বের তুলনা করে, পরিসীমা চেক ইত্যাদি করেন, আমি কিছু কার্যকর সম্পাদনা পর্যবেক্ষণ যোগ করতে চাই।
আসুন দুটি ক্ষেত্রে নেওয়া যাক: দূরত্ব অনুসারে বাছাই করা বা আইটেমগুলিতে একটি সীমাবদ্ধতা বা সীমার সীমাবদ্ধতা পূরণ করে to
# 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
সংক্ষেপে: যতক্ষণ না আমরা X ^ 2 এর পরিবর্তে X এর এককে দূরত্বের প্রয়োজন হয়, ততক্ষণ আমরা গণনার সবচেয়ে শক্ত অংশটি নির্মূল করতে পারি।
# 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)
দুর্দান্ত, উভয় ফাংশন আর কোনও ব্যয়বহুল স্কোয়ার শিকড় করবে না। এটি আরও দ্রুত হবে। আমরা জেনারেটরে রূপান্তর করে ইন-অ্যারেঞ্জও উন্নত করতে পারি:
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)
এটি বিশেষত কার্যকর হতে পারে যদি আপনি পরিসীমা চেকগুলি চেন করতে পারেন ('এক্স এর কাছাকাছি এবং Y এর Nm এর মধ্যে এমন জিনিসগুলি সন্ধান করুন', কারণ আপনাকে আবার দূরত্ব গণনা করতে হবে না)।
তবে যদি আমরা সত্যিই একটি বৃহত তালিকা অনুসন্ধান 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 ...
এবং আবারও, ডিস্ট_স্কুয়ার ফলন বিবেচনা করুন। আমাদের হটডগ উদাহরণটি তখন পরিণত হয়:
# 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))