Tôi muốn giải thích về câu trả lời đơn giản với các ghi chú hiệu suất khác nhau. np.linalg.norm sẽ làm nhiều hơn bạn cần:
dist = numpy.linalg.norm(a-b)
Thứ nhất - chức năng này được thiết kế để hoạt động trên một danh sách và trả về tất cả các giá trị, ví dụ để so sánh khoảng cách từ pA
đến tập hợp các điểm sP
:
sP = set(points)
pA = point
distances = np.linalg.norm(sP - pA, ord=2, axis=1.) # 'distances' is a list
Ghi nhớ một số điều:
- Các cuộc gọi hàm Python rất tốn kém.
- [Thường xuyên] Python không tra cứu tên bộ đệm.
Vì thế
def distance(pointA, pointB):
dist = np.linalg.norm(pointA - pointB)
return dist
không ngây thơ như vẻ ngoài của nó
>>> 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
Đầu tiên - mỗi khi chúng ta gọi nó, chúng ta phải thực hiện tra cứu toàn cầu cho "np", một tìm kiếm có phạm vi cho "linalg" và một tìm kiếm có phạm vi cho "định mức" và chi phí đơn giản là gọi hàm có thể tương đương với hàng chục con trăn hướng dẫn.
Cuối cùng, chúng tôi đã lãng phí hai thao tác để lưu trữ kết quả và tải lại để trả lại ...
Vượt qua đầu tiên ở cải tiến: làm cho việc tra cứu nhanh hơn, bỏ qua cửa hàng
def distance(pointA, pointB, _norm=np.linalg.norm):
return _norm(pointA - pointB)
Chúng tôi nhận được sắp xếp hợp lý hơn nhiều:
>>> 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
Tuy nhiên, chức năng gọi qua chức năng vẫn lên tới một số công việc. Và bạn sẽ muốn làm điểm chuẩn để xác định xem bạn có thể tự mình làm toán tốt hơn không:
def distance(pointA, pointB):
return (
((pointA.x - pointB.x) ** 2) +
((pointA.y - pointB.y) ** 2) +
((pointA.z - pointB.z) ** 2)
) ** 0.5 # fast sqrt
Trên một số nền tảng, **0.5
là nhanh hơn math.sqrt
. Số dặm của bạn có thể thay đổi.
**** Ghi chú hiệu suất nâng cao.
Tại sao bạn tính khoảng cách? Nếu mục đích duy nhất là hiển thị nó,
print("The target is %.2fm away" % (distance(a, b)))
di chuyển dọc. Nhưng nếu bạn đang so sánh khoảng cách, thực hiện kiểm tra phạm vi, v.v., tôi muốn thêm một số quan sát hiệu suất hữu ích.
Chúng ta hãy thực hiện hai trường hợp: sắp xếp theo khoảng cách hoặc loại bỏ danh sách thành các mục đáp ứng ràng buộc phạm vi.
# 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)
Điều đầu tiên chúng ta cần nhớ là chúng ta đang sử dụng Pythagoras để tính khoảng cách ( dist = sqrt(x^2 + y^2 + z^2)
) vì vậy chúng ta đang thực hiện rất nhiều sqrt
cuộc gọi. Toán 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
Tóm lại: cho đến khi chúng tôi thực sự yêu cầu khoảng cách trong một đơn vị X chứ không phải X ^ 2, chúng tôi có thể loại bỏ phần khó nhất trong các phép tính.
# 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)
Tuyệt vời, cả hai chức năng không còn làm bất kỳ căn bậc hai đắt tiền. Điều đó sẽ nhanh hơn nhiều. Chúng tôi cũng có thể cải thiện in_range bằng cách chuyển đổi nó thành trình tạo:
def in_range(origin, range, things):
range_sq = range**2
yield from (thing for thing in things
if distance_sq(origin, thing) <= range_sq)
Điều này đặc biệt có lợi ích nếu bạn đang làm một cái gì đó như:
if any(in_range(origin, max_dist, things)):
...
Nhưng nếu điều tiếp theo bạn sẽ làm đòi hỏi một khoảng cách,
for nearby in in_range(origin, walking_distance, hotdog_stands):
print("%s %.2fm" % (nearby.name, distance(origin, nearby)))
xem xét năng suất bộ:
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)
Điều này có thể đặc biệt hữu ích nếu bạn có thể kiểm tra phạm vi chuỗi ('tìm những thứ ở gần X và trong Nm của Y', vì bạn không phải tính lại khoảng cách).
Nhưng nếu chúng ta đang tìm kiếm một danh sách thực sự lớn things
và chúng ta dự đoán rất nhiều trong số chúng không đáng để xem xét thì sao?
Thực sự có một tối ưu hóa rất đơn giản:
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
Điều này có hữu ích hay không sẽ phụ thuộc vào kích thước của 'vật'.
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 ...
Và một lần nữa, hãy xem xét mang lại dist_sq. Ví dụ hotdog của chúng tôi sau đó trở thành:
# 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))