2011-01-15 56 views
18

Tôi đang làm việc trên một công cụ gọi là Quickfort cho trò chơi Dwarf Fortress. Quickfort biến các bảng tính thành định dạng csv/xls thành một loạt lệnh cho Dwarf Fortress để thực hiện để vẽ một "bản thiết kế" trong trò chơi.Tìm tập hợp các hình chữ nhật tiếp giáp lớn nhất để che phủ nhiều khu vực

Tôi hiện đang cố gắng giải quyết tối ưu vấn đề về âm mưu khu vực cho bản phát hành 2.0 của công cụ này.

Hãy xem xét "kế hoạch chi tiết" sau đây xác định các lệnh vẽ sơ đồ cho lưới 2 chiều. Mỗi ô trong lưới nên được đào ra ("d"), được chuyển kênh ("c") hoặc bị bỏ sót ("."). Bất kỳ số lượng các lệnh vẽ âm mưu khác nhau có thể có mặt trong việc sử dụng thực tế.

. d . d c c 
d d d d c c 
. d d d . c 
d d d d d c 
. d . d d c 

Để giảm thiểu số lượng hướng dẫn mà cần phải được gửi đến Fortress Dwarf, tôi muốn tìm các thiết lập của hình chữ nhật tiếp giáp lớn nhất có thể được thành lập để bù đắp hoàn toàn, hoặc "âm mưu", tất cả các plottable ô. Để hợp lệ, tất cả các ô của hình chữ nhật đã cho phải chứa cùng một lệnh.

Đây là cách tiếp cận nhanh hơn Quickfort 1.0: vẽ từng ô riêng lẻ dưới dạng hình chữ nhật 1x1. This video cho thấy sự khác biệt hiệu suất giữa hai phiên bản.

Đối với các kế hoạch chi tiết ở trên, giải pháp trông như thế này:

. 9 . 0 3 2 
8 1 1 1 3 2 
. 1 1 1 . 2 
7 1 1 1 4 2 
. 6 . 5 4 2 

Mỗi hình chữ nhật cùng đánh số trên biểu thị một hình chữ nhật liền kề. Các hình chữ nhật lớn nhất được ưu tiên hơn các hình chữ nhật nhỏ hơn cũng có thể được hình thành trong khu vực của chúng. Thứ tự của số/hình chữ nhật là không quan trọng.

My current approach là lặp lại. Trong mỗi lần lặp lại, tôi xây dựng một danh sách các hình chữ nhật lớn nhất có thể được hình thành từ mỗi ô có thể vẽ bằng lưới bằng cách mở rộng trong tất cả 4 hướng từ ô. Sau khi sắp xếp danh sách lớn nhất trước tiên, tôi bắt đầu với hình chữ nhật lớn nhất được tìm thấy, đánh dấu các ô bên dưới của nó là "được vẽ" và ghi lại hình chữ nhật trong danh sách. Trước khi vẽ từng hình chữ nhật, các ô bên dưới của nó được kiểm tra để đảm bảo chúng chưa được vẽ (chồng lên một ô trước đó). Sau đó chúng tôi bắt đầu lại, tìm các hình chữ nhật còn lại lớn nhất có thể được hình thành và vẽ chúng cho đến khi tất cả các ô được vẽ như một phần của một số hình chữ nhật.

Tôi xem xét phương pháp này được tối ưu hóa hơn một chút so với tìm kiếm bạo lực câm, nhưng tôi lãng phí rất nhiều chu kỳ (lại) tính toán hình chữ nhật lớn nhất của ô và kiểm tra trạng thái của ô bên dưới.

Hiện tại, thói quen khám phá hình chữ nhật này chiếm tỷ trọng của tổng thời gian chạy của công cụ, đặc biệt là đối với các bản thiết kế lớn. Tôi đã hy sinh một số độ chính xác vì lợi ích của tốc độ bằng cách chỉ xem xét hình chữ nhật từ các tế bào xuất hiện để tạo thành một góc hình chữ nhật (được xác định bằng cách sử dụng một số chẩn đoán tế bào lân cận không phải lúc nào cũng chính xác). Do 'tối ưu hóa' này, mã hiện tại của tôi không thực sự tạo ra giải pháp trên một cách chính xác, nhưng nó đủ gần.

Nói chung, tôi xem xét mục tiêu của hình chữ nhật lớn nhất đầu tiên là phương pháp "đủ tốt" cho ứng dụng này.Tuy nhiên tôi nhận thấy rằng nếu mục tiêu là thay vì để tìm tối thiểu (số ít nhất) của hình chữ nhật hoàn toàn bao gồm nhiều lĩnh vực, giải pháp sẽ trông như thế thay vì điều này:

. 3 . 5 6 8 
1 3 4 5 6 8 
. 3 4 5 . 8 
2 3 4 5 7 8 
. 3 . 5 7 8 

mục tiêu thứ hai này thực sự đại diện cho một hơn giải pháp tối ưu cho vấn đề, vì ít hình chữ nhật hơn thường có nghĩa là ít lệnh được gửi tới Dwarf Fortress hơn. Tuy nhiên, cách tiếp cận này đánh tôi gần hơn với NP-Hard, dựa trên kiến ​​thức toán học hạn chế của tôi.

Xem the video nếu bạn muốn hiểu rõ hơn về chiến lược tổng thể; Tôi đã không giải quyết các khía cạnh khác của quá trình của Quickfort, chẳng hạn như tìm đường dẫn con trỏ ngắn nhất vẽ tất cả các hình chữ nhật. Có thể có một giải pháp cho vấn đề này kết hợp chặt chẽ các chiến lược này.

Trợ giúp của bất kỳ biểu mẫu nào sẽ được đánh giá cao.

+1

Bạn đã thử một thuật toán di truyền chưa? Nghe có vẻ thích hợp cho vấn đề này. – marcog

Trả lời

10

Tôi tìm thấy giấy Fast Algorithms To Partition Simple Rectilinear Polygons từ San-Yuan Wu và Sartaj Sahni, có thể bạn quan tâm. Trong ví dụ của bạn, vùng có ký tự 'd' tạo thành một đa giác tuyến tính, cũng là các vùng có 'c' và '.'. Bài báo này bao gồm các thuật toán cho đa giác tuyến tính đơn giản lỗ miễn phí.

Nếu đa giác bao gồm các lỗ, có các thuật toán chạy với thời gian O (n^3/2 log n), như JM Keil trong trang Polygon Decomposition trên trang 11.

Nếu giảm thiểu tổng chiều dài của các đoạn đường được giới thiệu trong quy trình phân đoạn là tiêu chí tối ưu hóa khác, sự cố sẽ trở thành NP-nếu đa giác chứa lỗ (trang 12). Đối với những vấn đề này xấp xỉ các thuật toán tồn tại (giấy đề cập đến các giấy tờ với các thuật toán như vậy). Nếu đa giác không chứa các lỗ thì có một thuật toán thời gian O (n^4).

+0

Xem nhanh tóm tắt nói rằng điều này chỉ hoạt động đối với các đa giác "không có lỗ". Điều này sẽ làm việc ở đây? BTW cảm ơn cho một liên kết tuyệt vời! – templatetypedef

+0

@templatetypedef: Tôi thậm chí không phải là rất familar với chủ đề này, câu hỏi từ joelpt chỉ làm dấy lên sự quan tâm của tôi. Nhưng có, các thuật toán được mô tả trong bài báo chỉ dành cho * đa giác * không có lỗ. Nếu đa giác bao gồm lỗ, vấn đề trở thành NP-hoàn thành (như giấy * Phân rã đa giác * nêu rõ, tôi đã chỉnh sửa câu trả lời của mình để bao gồm liên kết đến bài báo này) –

+0

Bài thứ hai nói ở trang 12, "Nếu đa giác có lỗ họ chỉ ra rằng vấn đề trở thành NP-complete ", nhưng chúng đang giảm thiểu tổng chiều dài của các đoạn thẳng được giới thiệu trong quá trình phân vùng (tổng lượng mực). – Steinbitglis

2

Đây không phải là thực sự là một câu trả lời nhưng sử dụng một tìm kiếm ngây thơ bạn có thể nhận

. 1 . 2 3 3 
4 1 5 2 3 3 
. 1 5 2 . 6 
7 1 5 2 8 6 
. 1 . 2 8 6 

Về cơ bản bạn bắt đầu từ góc trên bên trái và sử dụng nó như góc trên cùng bên trái của hình chữ nhật tiếp theo của bạn, sau đó bạn kiểm tra như thế nào đến nay bạn có thể mở rộng nó sang phải và xuống, sau đó tìm thấy ô trên cùng và tận cùng bên trái của các bit còn lại và vân vân.

Đây có lẽ là rất hiệu quả trong một số trường hợp nhưng nó nhanh chóng như bạn không cần phải tính toán lại bất cứ điều gì ..

+0

Tôi thường sửa đổi mã của mình để chỉ thực hiện các tìm kiếm liên tiếp từ các ô ở phía đông và phía nam, và thấy hiệu suất tăng ~ 25% với chi phí từ 10-45% số lần gõ phím cao hơn (ít chính xác hơn). Tôi chắc chắn nó có thể được nhiều hơn nữa performant nếu tôi cấu trúc lại mã để giảm thiểu rescans. – joelpt

1

Theo quan điểm của tôi, tất cả các giải pháp tìm một tập hợp các hình chữ nhật bao phủ diện tích ban đầu là một đúng một. Tìm một tập hợp hình chữ nhật nhỏ hơn là tốt hơn vì nó nén/hoạt động tốt hơn.

Vì vậy, tôi sẽ không khuyên cố gắng tìm giải pháp tối ưu. (Tôi đoán nó cũng là NP-hard).

Để có giải pháp chạy nhanh hơn, bạn có thể xếp ma trận thành các nhóm gồm 4 ô ban đầu và cố gắng hợp nhất chúng nếu chúng giống nhau. Sau đó, bạn có thể hợp nhất các nhóm gồm 4 nhóm, nếu chúng giống nhau. Và làm như vậy đệ quy nếu bạn đã làm xong.

Điều này sẽ không tìm ra giải pháp tối ưu nhưng sẽ rất nhanh. Nếu ma trận của bạn là lớn, với các khu vực tiếp giáp lớn, sự khác biệt với tối ưu sẽ không được tuyệt vời.

+0

Cảm ơn ý tưởng này. Điều này khiến tôi suy nghĩ về một giải pháp ở mỗi ô, chúng tôi phóng to kích thước hình chữ nhật bằng cách lặp lại cố gắng mở rộng từng cạnh của nó ra ngoài. Tôi nghĩ điều này tương tự như ý tưởng nhóm 4 tế bào của bạn, nhưng cho phép sự tăng trưởng diện tích không hình vuông. – joelpt

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