2012-06-27 17 views
8

Tôi đang làm việc theo số my gEDA fork và muốn loại bỏ hệ thống dựa trên nền đơn giản hiện tại có lợi cho chỉ mục không gian thực .Tôi cần chỉ mục không gian trong C

Thuật toán hiệu quả tìm thấy điểm là không đủ: tôi cần tìm các đối tượng có mức độ khác 0. Hãy suy nghĩ về các đối tượng có hình chữ nhật bị ràng buộc, mà khá nhiều nắm bắt mức độ chi tiết tôi cần trong chỉ mục. Với hình chữ nhật tìm kiếm, tôi cần có khả năng tìm kiếm hiệu quả tất cả các đối tượng có hình chữ nhật giới hạn ở bên trong hoặc hình chữ nhật tìm kiếm.

Chỉ mục không thể chỉ đọc: gschem là một chương trình thu thập sơ đồ và toàn bộ điểm của nó là di chuyển mọi thứ xung quanh biểu đồ sơ đồ. Vì vậy, mọi thứ sẽ được a'changing. Vì vậy, trong khi tôi có thể đủ khả năng chèn đắt hơn một chút so với tìm kiếm, nó không thể là quá đắt hơn nhiều và việc xóa cũng phải vừa rẻ vừa hợp lý. Nhưng yêu cầu quan trọng nhất là hành vi tiệm cận: tìm kiếm phải là O (log n) nếu nó không thể là O (1). Chèn/xóa nên tốt nhất là O (log n), nhưng O (n) sẽ không sao. Tôi chắc chắn không muốn bất cứ điều gì> O (n) (cho mỗi hành động; rõ ràng là O (n log n) được mong đợi cho một hoạt động tất cả các đối tượng).

Tùy chọn của tôi là gì? Tôi không cảm thấy đủ thông minh để đánh giá the various options. Lý tưởng nhất là sẽ có một số thư viện C mà sẽ làm tất cả những thứ thông minh cho tôi, nhưng tôi sẽ thực hiện một cách máy móc một thuật toán tôi có thể hoặc có thể không hoàn toàn hiểu được nếu tôi có. gEDA sử dụng glib bằng cách này, nếu điều đó giúp đưa ra đề xuất.

Chú thích:

Chuẩn gEDA chia một sơ đồ thành một số cố định (hiện 100) của "gạch" mà phục vụ để tăng tốc độ tìm kiếm cho các đối tượng trong một hình chữ nhật bounding. Điều này rõ ràng là đủ tốt để làm cho hầu hết các sơ đồ đủ nhanh để tìm kiếm, nhưng cách nó được thực hiện gây ra các vấn đề khác: quá nhiều hàm đòi hỏi một con trỏ tới một đối tượng toàn cầu không thực tế. Hình dạng hình khối cũng được cố định: có thể đánh bại hệ thống ốp lát này hoàn toàn bằng cách kéo (và có thể phóng to) đến một vùng được bao phủ bởi chỉ một ô.

Câu trả lời hợp pháp sẽ là giữ các yếu tố của hệ thống ốp lát, nhưng để khắc phục các điểm yếu của nó: dạy nó kéo dài toàn bộ không gian và chia nhỏ khi cần thiết. Nhưng tôi muốn người khác thêm hai xu của họ trước khi tôi tự quyết định rằng đây là cách tốt nhất.

+0

Ngoài ra, hãy xem http://www.boost.org/doc/libs/1_61_0/libs/geometry/doc/html/geometry/spatial_indexes/rtree_examples/quick_start.html Và chúc bạn may mắn nếu dự án của bạn vẫn tiếp tục! –

Trả lời

2

Cấu trúc dữ liệu đẹp cho kết hợp các điểm và đường thẳng sẽ là một cây R hoặc một trong các dẫn xuất của nó (ví dụ: R * -Tree hoặc R-Tree Hilbert). Do bạn muốn chỉ số này hoạt động và có thể tuần tự hóa, tôi nghĩ sử dụng SQLite's R*-Tree module sẽ là một cách tiếp cận hợp lý.

Nếu bạn có thể chịu đựng C++, libspatialindex có triển khai R-tree trưởng thành và linh hoạt hỗ trợ chèn/xóa và tuần tự động.

+0

Uhm, tôi không cần chỉ mục để có thể tuần tự hóa theo ý nghĩa của việc cần ghi nó vào và tải nó từ đĩa. Bạn không chắc chắn nếu đó là những gì bạn có ý nghĩa ở đây. –

+0

Ah, tôi nghĩ bạn sẽ tìm cách lưu nó ra đĩa với định dạng tệp cho tốc độ; có lẽ đó chỉ là tiền thưởng. – user7116

+0

Không, chắc chắn không tiết kiệm được thông tin có nguồn gốc. Định dạng tập tin là văn bản có thể chỉnh sửa (nếu một chút baroque), vì vậy tôi không muốn giới thiệu một yêu cầu cho các biên tập viên của con người phải lo lắng về một chỉ mục không gian đã lưu. –

0

Điều này nghe giống như một ứng dụng phù hợp với quadtree (giả sử bạn chỉ quan tâm đến 2D.) quadtree là phân cấp (tốt cho tìm kiếm) và độ phân giải không gian là động (cho phép độ phân giải cao hơn ở những khu vực cần).

Tôi luôn cán quadtrees của riêng tôi, nhưng đây là một thư viện xuất hiện hợp lý: http://www.codeproject.com/Articles/30535/A-Simple-QuadTree-Implementation-in-C

-1

Nó rất dễ dàng để làm. Thật khó để làm nhanh. Âm thanh như một vấn đề tôi làm việc trên nơi có một danh sách rộng lớn của min, giá trị tối đa và đưa ra một giá trị nó đã phải trả lại bao nhiêu min, cặp tối đa chồng chéo giá trị đó. Bạn chỉ có nó trong hai chiều. Vì vậy, bạn làm điều đó với hai cây cho mỗi hướng. Sau đó thực hiện giao lộ trên các kết quả. Điều này thực sự nhanh chóng.

#include <iostream> 
#include <fstream> 
#include <map> 

using namespace std; 

typedef unsigned int UInt; 

class payLoad { 
public: 
    UInt starts; 
    UInt finishes; 
    bool isStart; 
    bool isFinish; 
    payLoad() 
    { 
     starts = 0; 
     finishes = 0; 
     isStart = false; 
     isFinish = false; 
    } 
}; 

typedef map<UInt,payLoad> ExtentMap; 

//============================================================================== 
class Extents 
{ 
    ExtentMap myExtentMap; 

public: 

    void ReadAndInsertExtents (const char* fileName) 
    { 
     UInt start, finish; 
     ExtentMap::iterator EMStart; 
     ExtentMap::iterator EMFinish; 

     ifstream efile (fileName); 
     cout << fileName << " filename" << endl; 

     while (!efile.eof()) { 
      efile >> start >> finish; 
      //cout << start << " start " << finish << " finish" << endl; 
      EMStart = myExtentMap.find(start); 
      if (EMStart==myExtentMap.end()) { 
       payLoad pay; 
       pay.isStart = true; 
       myExtentMap[start] = pay; 
       EMStart = myExtentMap.find(start); 
       } 
      EMFinish = myExtentMap.find(finish); 
      if (EMFinish==myExtentMap.end()) { 
       payLoad pay; 
       pay.isFinish = true; 
       myExtentMap[finish] = pay; 
       EMFinish = myExtentMap.find(finish); 
      } 
      EMStart->second.starts++; 
      EMFinish->second.finishes++; 
      EMStart->second.isStart = true; 
      EMFinish->second.isFinish = true; 

//   for (EMStart=myExtentMap.begin(); EMStart!=myExtentMap.end(); EMStart++) 
//    cout << "| key " << EMStart->first << " count " << EMStart->second.value << " S " << EMStart->second.isStart << " F " << EMStart->second.isFinish << endl; 

     } 

     efile.close(); 

     UInt count = 0; 
     for (EMStart=myExtentMap.begin(); EMStart!=myExtentMap.end(); EMStart++) 
     { 
       count += EMStart->second.starts - EMStart->second.finishes; 
       EMStart->second.starts = count + EMStart->second.finishes; 
     } 

//  for (EMStart=myExtentMap.begin(); EMStart!=myExtentMap.end(); EMStart++) 
//   cout << "||| key " << EMStart->first << " count " << EMStart->second.starts << " S " << EMStart->second.isStart << " F " << EMStart->second.isFinish << endl; 

    } 

    void ReadAndCountNumbers (const char* fileName) 
    { 
     UInt number, count; 
     ExtentMap::iterator EMStart; 
     ExtentMap::iterator EMTemp; 

     if (myExtentMap.empty()) return; 

     ifstream nfile (fileName); 
     cout << fileName << " filename" << endl; 

     while (!nfile.eof()) 
     { 
      count = 0; 
      nfile >> number; 
      //cout << number << " number "; 

      EMStart = myExtentMap.find(number); 
      EMTemp = myExtentMap.end(); 

      if (EMStart==myExtentMap.end()) {   // if we don't find the number then create one so we can find the nearest number. 
       payLoad pay; 
       myExtentMap[ number ] = pay; 
       EMStart = EMTemp = myExtentMap.find(number); 
       if ((EMStart!=myExtentMap.begin()) && (!EMStart->second.isStart)) 
       { 
        EMStart--; 
       } 
      } 

      if (EMStart->first < number) { 
       while (!EMStart->second.isFinish) { 
        //cout << "stepped through looking for end - key" << EMStart->first << endl; 
        EMStart++; 
        } 
       if (EMStart->first >= number) { 
        count = EMStart->second.starts; 
        //cout << "found " << count << endl; 
        } 
      } 
      else if (EMStart->first==number) { 
       count = EMStart->second.starts; 
       } 

      cout << count << endl; 

      //cout << "| count " << count << " key " << EMStart->first << " S " << EMStart->second.isStart << " F " << EMStart->second.isFinish<< " V " << EMStart->second.value << endl; 

      if (EMTemp != myExtentMap.end()) 
      { 
       myExtentMap.erase(EMTemp->first); 
      } 
     } 
     nfile.close();  
    } 

}; 

//============================================================================== 

int main (int argc, char* argv[]) { 
    Extents exts; 

    exts.ReadAndInsertExtents ("..//..//extents.txt"); 
    exts.ReadAndCountNumbers ("..//../numbers.txt"); 

    return 0; 
} 

tệp kiểm tra kéo dài là 1.5MB của:

0 200000 
1 199999 
2 199998 
3 199997 
4 199996 
5 199995 
.... 
99995 100005 
99996 100004 
99997 100003 
99998 100002 
99999 100001 

Những con số tập tin giống như:

102731 
104279 
109316 
104859 
102165 
105762 
101464 
100755 
101068 
108442 
107777 
101193 
104299 
107080 
100958 
..... 

Thậm chí đọc hai tập tin từ đĩa, mức độ là 1.5MB và số là 780k và số lượng thực sự lớn các giá trị và tra cứu, điều này chạy trong một phần nhỏ của một giây. Nếu trong bộ nhớ nó sẽ chớp nhanh.

+0

Trong giai đoạn trung gian của thể dục dụng cụ mã, tôi đã làm nó theo cách bạo lực, đã loại bỏ gạch và chỉ đơn giản là lặp qua danh sách tất cả các đối tượng. Đã được nó đáng chú ý chậm, ngay cả trên sơ đồ khiêm tốn. –

+0

chỉ cần thêm mã ở trên ... điều này làm một chiều ... bạn sẽ làm điều đó cho cả hai cho 2d và sau đó cắt các kết quả. Bạn sẽ cần phải thêm bản thân xóa nhưng nó được xây dựng trên cơ sở dữ liệu C++ std đã có chức năng này. – AnthonyLambert

+0

std :: bản đồ không phải là một danh sách, nó là một cái cây! Tôi nghi ngờ bạn đã downvoted vì một danh sách * * không phải là một cấu trúc dữ liệu thích hợp cho loại vấn đề này. Sử dụng std :: map như bạn có thích hợp hơn rất nhiều (nhớ rằng tôi cần phải làm điều này trong C, không phải C++), và những gì bạn đã làm ở đây có thể được lập luận là một chỉ mục không gian trong ngụy trang! –

2

Âm thanh nhu cầu của bạn rất giống với những gì được sử dụng trong các thuật toán phát hiện va chạm cho trò chơi và mô phỏng vật lý. Có một số thư viện C++ nguồn mở xử lý điều này trong 2-D (Box2D) hoặc 3-D (Bullet physics). Mặc dù câu hỏi của bạn dành cho C, bạn có thể thấy tài liệu và triển khai của chúng hữu ích.

Thông thường này được chia thành một two phases:

  1. Một giai đoạn rộng nhanh mà xấp xỉ đối tượng bằng hộp bounding trục thẳng hàng của họ (AABB), và xác định các cặp AABBs đó chạm hoặc chồng chéo lên nhau.
  2. Một giai đoạn hẹp chậm hơn tính toán các điểm chồng chéo hình học cho các cặp đối tượng có AABB chạm hoặc chồng lên nhau.

Động cơ vật lý cũng sử dụng tính mạch lạc không gian để giảm thêm cặp đối tượng được so sánh, nhưng tối ưu hóa này có thể sẽ không giúp ứng dụng của bạn.

Vĩ mô thường được triển khai bằng thuật toán O (N log N) như Sweep and prune. Bạn có thể tăng tốc độ này bằng cách sử dụng nó kết hợp với cách tiếp cận ngói hiện tại (one of Nvidia's GPUGems mô tả cách tiếp cận lai này). Giai đoạn hẹp là khá tốn kém cho mỗi cặp, và có thể là quá mức cần thiết cho nhu cầu của bạn. GJK algorithm thường được sử dụng cho các đối tượng lồi trong bước này, mặc dù các thuật toán nhanh hơn tồn tại cho các trường hợp chuyên biệt hơn (ví dụ: hộp/vòng tròn và va chạm hộp/hình cầu).

+0

Cảm ơn bạn đã thêm sắc thái. Theo như câu hỏi của tôi là có liên quan, giai đoạn thứ hai, chậm hơn hẹp không tồn tại. Gần như tất cả mọi thứ bởi nó là AABB (và cảm ơn cho thuật ngữ thuật ngữ) là khá alright. –

Các vấn đề liên quan