Làm thế nào để liên tục tìm thấy tất cả các thực thể trong một bán kính hiệu quả?


14

Tôi có một số lượng lớn các thực thể (đơn vị). Trên mỗi bước, mỗi đơn vị cần biết vị trí của tất cả các đơn vị gần nó (khoảng cách nhỏ hơn sau đó được đưa ra R không đổi ). Tất cả các đơn vị di chuyển liên tục. Đây là trong 3D.

Trung bình, sẽ có 1% tổng số đơn vị gần bất kỳ đơn vị nào khác với các ràng buộc nhất định.

Làm thế nào tôi có thể làm điều này một cách hiệu quả, mà không cần bruteforcing?


7
Bạn sẽ muốn một số loại hệ thống phân vùng không gian: en.wikipedia.org/wiki/Space_partitioning
Tetrad

Câu trả lời:


15

Sử dụng một trong các thuật toán phân vùng không gian phổ biến, chẳng hạn như Quadtree, Octree, cây BSP hoặc thậm chí là một Hệ thống lưới đơn giản. Mỗi người có ưu và nhược điểm riêng cho từng kịch bản cụ thể. Bạn có thể đọc thêm về chúng trong những cuốn sách này .

Nói chung (hoặc vì vậy tôi đã nghe nói, tôi không quá quen thuộc với lý do đằng sau điều này), Quadtree hoặc Octree phù hợp hơn với môi trường ngoài trời, trong khi cây BSP phù hợp với cảnh trong nhà hơn. Và sự lựa chọn giữa việc sử dụng Quadtree hay Octree phụ thuộc vào mức độ phẳng của thế giới của bạn. Nếu có ít biến thể trong trục Y sử dụng Octree sẽ gây lãng phí. Một Octree về cơ bản là một Quadtree với một chiều bổ sung.

Cuối cùng, đừng coi thường sự đơn giản của giải pháp Grid. Nhiều người bỏ qua rằng một lưới đơn giản đôi khi có thể đủ (và thậm chí hiệu quả hơn) cho các vấn đề của họ và thay vào đó là một giải pháp phức tạp hơn.

Sử dụng lưới bao gồm đơn giản là phân chia thế giới thành các vùng cách đều nhau và lưu trữ các thực thể trong khu vực thích hợp của thế giới. Sau đó, được đưa ra một vị trí, việc tìm kiếm các thực thể lân cận sẽ là vấn đề lặp lại trên các khu vực giao nhau với bán kính tìm kiếm của bạn.

Giả sử thế giới của bạn dao động từ (-1000, -1000) đến (1000, 1000) trong mặt phẳng XZ. Chẳng hạn, bạn có thể chia nó thành một lưới 10x10, như vậy:

var grid = new List<Entity>[10, 10];

Sau đó, bạn đặt các thực thể vào các ô thích hợp của chúng trong lưới. Chẳng hạn, một thực thể có XZ (-1000, -1000) sẽ rơi vào ô (0,0) trong khi một thực thể có XZ (1000, 1000) sẽ rơi vào ô (9, 9). Sau đó được cung cấp một vị trí và bán kính trên thế giới, bạn có thể xác định các ô nào được giao nhau bởi "vòng tròn" này và chỉ lặp lại trên các ô đó, với một nhân đôi đơn giản cho.

Dù sao, nghiên cứu tất cả các lựa chọn thay thế và chọn một trong những dường như phù hợp với trò chơi của bạn tốt hơn. Tôi thừa nhận tôi vẫn chưa đủ hiểu biết về chủ đề này để quyết định thuật toán nào là tốt nhất cho bạn.

Chỉnh sửa Tìm thấy điều này trên một diễn đàn khác và nó có thể giúp bạn quyết định:

Các lưới hoạt động tốt nhất khi phần lớn các đối tượng nằm gọn trong một ô vuông và phân bố khá đồng nhất. Ngược lại, tứ giác hoạt động khi các đối tượng có kích thước thay đổi hoặc được nhóm trong các khu vực nhỏ.

Đưa ra mô tả mơ hồ của bạn về vấn đề, tôi cũng đang dựa vào giải pháp lưới (nghĩa là, các đơn vị giả định là nhỏ và phân phối khá đồng nhất).


Cảm ơn đã trả lời chi tiết. Vâng, có vẻ như giải pháp Grid đơn giản là đủ tốt cho tôi.
OCyril

0

Tôi đã viết điều này một thời gian trở lại. Hiện đã có trên một trang web thương mại, nhưng bạn có thể lấy nguồn để sử dụng cá nhân miễn phí. Nó có thể là quá mức cần thiết và nó được viết bằng Java, nhưng nó được ghi chép lại tốt nên không quá khó để cắt và viết lại bằng ngôn ngữ khác. Về cơ bản, nó sử dụng một Octree, với các tinh chỉnh để xử lý các đối tượng thực sự lớn và đa luồng.

Tôi tìm thấy một Octree cung cấp sự kết hợp tốt nhất của tính linh hoạt và hiệu quả. Tôi đã bắt đầu với một lưới, nhưng không thể định kích thước các ô vuông đúng cách và các mảng lớn của các ô vuông trống đã sử dụng hết không gian và sức mạnh tính toán mà không có gì. (Và đó là chỉ trong 2 chiều.) Tay cầm Mã của tôi truy vấn từ nhiều chủ đề, có thêm một rất nhiều sự phức tạp, nhưng các tài liệu hướng dẫn này sẽ giúp bạn làm việc xung quanh rằng nếu bạn không cần nó.


0

Để tăng hiệu quả của bạn, hãy cố gắng từ chối một cách tầm thường 99% "đơn vị" không ở gần đơn vị mục tiêu bằng cách sử dụng kiểm tra hộp giới hạn rất rẻ. Và tôi hy vọng bạn có thể làm điều này mà không cần cấu trúc dữ liệu của bạn theo không gian. Vì vậy, nếu tất cả các đơn vị của bạn được lưu trữ trong một cấu trúc dữ liệu phẳng, bạn có thể cố gắng chạy đua từ đầu đến cuối và kiểm tra trước tiên là đơn vị hiện tại bên ngoài hộp giới hạn của đơn vị quan tâm.

Xác định một hộp giới hạn quá khổ cho đơn vị quan tâm sao cho nó có thể từ chối một cách an toàn các mặt hàng không có cơ hội được coi là "gần" nó. Việc kiểm tra loại trừ khỏi hộp giới hạn có thể được thực hiện rẻ hơn so với kiểm tra bán kính. Tuy nhiên, trên một số hệ thống nơi thử nghiệm này, nó đã được tìm thấy không phải là trường hợp. Hai người thực hiện gần như bằng nhau. Điều này được chỉnh sửa sau nhiều cuộc tranh luận dưới đây.

Đầu tiên: clip hộp giới hạn 2D.

// returns true if the circle supplied is completely OUTSIDE the bounding box, rectClip
bool canTrivialRejectCircle(Vertex2D& vCentre, WorldUnit radius, Rect& rectClip) {
  if (vCentre.x + radius < rectClip.l ||
    vCentre.x - radius > rectClip.r ||
    vCentre.y + radius < rectClip.b ||
    vCentre.y - radius > rectClip.t)
    return true;
  else
    return false;
}

So với một cái gì đó như thế này (trong 3D):

BOOL bSphereTest(CObject3D* obj1, CObject3D* obj2 )
{
  D3DVECTOR relPos = obj1->prPosition - obj2->prPosition;
  float dist = relPos.x * relPos.x + relPos.y * relPos.y + relPos.z * relPos.z;
  float minDist = obj1->fRadius + obj2->fRadius;
  return dist <= minDist * minDist;
}.

Nếu đối tượng không bị từ chối tầm thường thì bạn thực hiện kiểm tra va chạm chính xác và tốn kém hơn. Nhưng bạn chỉ đang tìm kiếm sự gần gũi nên thử nghiệm hình cầu phù hợp với điều đó, nhưng chỉ cho 1% đối tượng sống sót sau sự từ chối tầm thường.

Bài viết này hỗ trợ hộp từ chối tầm thường. http://www.h3xed.com/programming/bounding-box-vs-bounding-circle-collision-detection-performance-as3

Nếu phương pháp tuyến tính này không cung cấp cho bạn hiệu suất bạn cần, thì cấu trúc dữ liệu phân cấp có thể được yêu cầu, chẳng hạn như các áp phích khác đã được nói đến. Cây R rất đáng để xem xét. Họ hỗ trợ những thay đổi năng động. Họ là BTrees của thế giới không gian.

Tôi chỉ không muốn bạn gặp phải tất cả những rắc rối khi giới thiệu sự phức tạp như vậy nếu bạn có thể tránh được. Ngoài ra, chi phí để giữ cho cấu trúc dữ liệu phức tạp này được cập nhật khi các đối tượng di chuyển xung quanh nhiều lần trong một giây?

Hãy nhớ rằng lưới là một cấu trúc dữ liệu không gian sâu một cấp. Giới hạn này có nghĩa là nó không thực sự có khả năng mở rộng. Khi thế giới phát triển về kích thước, số lượng tế bào bạn cần bao phủ cũng vậy. Cuối cùng, số lượng tế bào đó trở thành một vấn đề hiệu suất. Tuy nhiên, đối với một thế giới có kích thước nhất định, nó sẽ giúp bạn tăng hiệu suất lớn hơn so với việc không phân vùng không gian.


1
OP đặc biệt nói rằng anh ấy muốn tránh một cách tiếp cận vũ phu, đó chính xác là những gì bạn mô tả trong đoạn đầu tiên của bạn. Ngoài ra, làm thế nào để bạn tìm ra một kiểm tra hộp giới hạn rẻ hơn so với kiểm tra hình cầu giới hạn?! Thật tồi tệ.
thịt

Có, tôi biết anh ta muốn tránh sức mạnh vũ phu sẽ tránh được bằng cách nỗ lực giới thiệu cấu trúc dữ liệu phân cấp cho ứng dụng của mình. Nhưng đó có thể là rất nhiều nỗ lực. Nếu anh ta chưa muốn làm điều đó, anh ta có thể thử cách tiếp cận tuyến tính vốn là vũ phu nhưng có thể không thực hiện quá tệ nếu danh sách của anh ta không lớn. Tôi sẽ cố gắng chỉnh sửa mã ở trên để đưa vào chức năng từ chối tầm thường trong hộp giới hạn 2D của tôi. Tôi không nghĩ rằng tôi đã sai.
Ciaran

Liên kết đến GDnet bị hỏng, nhưng bài kiểm tra hình cầu chính tắc rất đơn giản, rất rẻ và không có chi nhánh:inside = (dot(p-p0, p-p0) <= r*r)
Lars Viklund

Tôi đã dán mã lên trên thay thế. Nó trông bất cứ điều gì nhưng giá rẻ so với hộp giới hạn.
Ciaran

1
@Ciaran Khá thành thật, bài viết đó có vẻ thực sự xấu. Trước hết, nó không thực hiện các thử nghiệm với dữ liệu thực tế, mà sử dụng cùng một giá trị lặp đi lặp lại. Không phải cái gì bạn sẽ gặp trong một kịch bản thực sự. Và không, theo bài báo, BB chỉ nhanh hơn khi không có xung đột (ví dụ: kiểm tra thất bại ở ifcâu lệnh đầu tiên ). Cũng không thực tế lắm. Nhưng thành thật mà nói, nếu bạn bắt đầu tối ưu hóa những thứ như thế này, thì bạn chắc chắn bắt đầu không đúng chỗ.
bummzack

0

Tôi phải làm cho câu trả lời này vì tôi không có điểm để bình luận hoặc phản đối. Đối với 99% những người hỏi câu hỏi này, một hộp giới hạn là giải pháp, như được mô tả bởi Ciaran. Trong một ngôn ngữ được biên dịch, nó sẽ từ chối 100.000 đơn vị không liên quan trong chớp mắt. Có rất nhiều chi phí liên quan đến các giải pháp phi vũ phu; với số lượng nhỏ hơn (dưới 1000), chúng sẽ đắt hơn về thời gian xử lý so với kiểm tra lực lượng vũ phu. Và họ sẽ mất nhiều thời gian hơn để lập trình.

Tôi không chắc chắn "một số lượng rất lớn" trong câu hỏi có nghĩa là gì, hoặc những người khác đang tìm kiếm câu trả lời ở đây sẽ có ý nghĩa gì. Tôi nghi ngờ những con số của tôi ở trên là bảo thủ và có thể nhân với 10; Cá nhân tôi khá thành kiến ​​với các kỹ thuật vũ phu và rất khó chịu về việc chúng hoạt động tốt như thế nào. Nhưng tôi sẽ không muốn ai đó, giả sử, 10.000 đơn vị lãng phí thời gian với một giải pháp ưa thích khi một vài dòng mã nhanh sẽ thực hiện thủ thuật. Họ luôn có thể nhận được ưa thích sau này nếu họ cần.

Ngoài ra, tôi sẽ lưu ý rằng kiểm tra hình cầu giới hạn yêu cầu nhân mà hộp giới hạn không. Phép nhân, theo bản chất của nó, mất nhiều lần miễn là cộng và so sánh. Có ràng buộc là sự kết hợp giữa ngôn ngữ, HĐH và phần cứng trong đó việc kiểm tra hình cầu sẽ nhanh hơn kiểm tra hộp, nhưng ở hầu hết các địa điểm và thời gian kiểm tra hộp phải nhanh hơn, ngay cả khi hình cầu từ chối một vài đơn vị không liên quan hộp chấp nhận. (Và khi hình cầu nhanh hơn, một bản phát hành mới của trình biên dịch / trình thông dịch / trình tối ưu hóa rất có khả năng thay đổi điều đó.)


Mặc dù câu trả lời của bạn không có gì sai, bạn không trả lời câu hỏi. Nó được đặc biệt yêu cầu một cách tiếp cận "không bruteforce". Ngoài ra, bạn dường như lặp lại những gì Ciaran đã viết và chúng tôi đã có một cuộc thảo luận dài về AABB so với các bài kiểm tra vòng tròn. Sự khác biệt hiệu suất chỉ đơn giản là không liên quan. Tốt hơn nên chọn một khối lượng giới hạn phù hợp với hầu hết các ứng cử viên va chạm của bạn, vì nó sẽ làm giảm số lượng thử nghiệm pha hẹp thực tế .. sẽ có tác động lớn hơn đến hiệu suất tổng thể.
bummzack
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.