2009-06-02 43 views
11

Gần đây tôi đã đính kèm phiên bản thứ 3 của thuật toán Dijkstra cho đường đi ngắn nhất của nguồn đơn vào dự án của tôi.Triển khai Dijkstra nhanh nhất mà bạn biết (trong C++) là gì?

Tôi nhận thấy rằng có nhiều triển khai khác nhau khác nhau về hiệu suất và cũng thay đổi về chất lượng của kết quả trong biểu đồ lớn. Với tập dữ liệu của tôi (> 100.000 đỉnh) thời gian chạy thay đổi từ 20 phút đến vài giây. Đường đi ngắn nhất cũng thay đổi 1-2%.

Triển khai nào tốt nhất bạn biết?

EDIT: Dữ liệu của tôi là mạng lưới thủy lực, với từ 1 đến 5 đỉnh mỗi nút. Nó có thể so sánh với bản đồ đường phố. Tôi đã thực hiện một số sửa đổi cho một thuật toán đã được tăng tốc (sử dụng một danh sách được sắp xếp cho tất cả các nút còn lại) và bây giờ tìm đến kết quả tương tự trong một phần thời gian. Tôi đã tìm kiếm một điều như vậy khá lâu rồi. Tôi tự hỏi nếu thực hiện như vậy đã tồn tại.

Tôi không thể giải thích sự khác biệt nhỏ trong kết quả. Tôi biết rằng Dijkstra không phải là heuristic, nhưng tất cả các triển khai có vẻ là chính xác. Các giải pháp nhanh hơn có kết quả với đường dẫn ngắn hơn. Tôi sử dụng toán học chính xác gấp đôi.

CHỈNH SỬA 2: Tôi phát hiện ra rằng sự khác biệt trong đường dẫn tìm thấy thực sự là lỗi của tôi. Tôi đã chèn xử lý đặc biệt cho một số đỉnh (chỉ hợp lệ theo một hướng) và quên về điều đó trong việc thực hiện khác.

NHƯNG im vẫn hơn ngạc nhiên rằng Dijkstra có thể được tăng tốc đáng kể bởi sự thay đổi sau đây: Trong một thuật toán Dijkstra chung chứa một vòng lặp như:

MyListType toDoList; // List sorted by smallest distance 
InsertAllNodes(toDoList); 
while(! toDoList.empty()) 
{ 
    MyNodeType *node = *toDoList.first(); 
    toDoList.erase(toDoList.first()); 
    ... 
} 

Nếu bạn thay đổi điều này một chút, nó hoạt động tương tự, nhưng hoạt động tốt hơn:

MyListType toDoList; // List sorted by smallest distance 
toDoList.insert(startNode); 
while(! toDoList.empty()) 
{ 
    MyNodeType *node = *toDoList.first(); 
    toDoList.erase(toDoList.first()); 
    for(MyNeigborType *x = node.Neigbors; x != NULL; x++) 
    { 
     ... 
     toDoList.insert(x->Node); 
    } 
} 

Dường như, sửa đổi này làm giảm thời gian chạy theo thứ tự không có độ lớn, nhưng là thứ tự của số mũ. Nó giảm thời gian chạy của tôi từ 30 giây xuống dưới 2. Tôi không thể tìm thấy sửa đổi này trong bất kỳ tài liệu nào. Nó cũng rất rõ ràng rằng lý do nằm trong danh sách được sắp xếp. chèn/xóa thực hiện tồi tệ hơn nhiều với 100.000 yếu tố mà với một bàn tay đầy.

ĐÁP:

Sau rất nhiều googling tôi tìm thấy bản thân mình. Câu trả lời rõ ràng là: boost graph lib. Tuyệt vời - tôi đã không tìm thấy điều này trong một thời gian. Nếu bạn nghĩ rằng không có sự khác biệt về hiệu suất giữa việc triển khai Dijkstra, hãy xem wikipedia.

+26

Một người bạn của tôi có thể triển khai Dijkstra trong C++ trong khoảng 3 phút. Tôi đã không nghe nói về bất kỳ triển khai nhanh hơn. – jbasko

+5

Tôi sẽ đề nghị bạn có thể muốn kiểm tra những triển khai đó ... nếu chúng đúng, tất cả chúng phải trả về cùng một đường đi ngắn nhất ... Thuật toán của Dijkstra không phải là heuristic ... – jerryjvl

+2

Sự khác biệt trong các đường đi ngắn nhất có thể là kết quả của sử dụng phép tính chính xác gấp đôi, vì làm tròn lỗi khi tổng hợp các chuỗi dài gấp đôi. Một thứ tự tổng hợp khác có thể tạo ra các lỗi khác nhau. Bạn có thể kiểm tra việc triển khai của mình trên các số nguyên không? –

Trả lời

11

Các triển khai tốt nhất được biết cho mạng lưới đường (> 1 triệu nút) có thời gian truy vấn được biểu thị trong vi giây. Xem để biết thêm chi tiết về Thách thức thực hiện DIMACS lần thứ 9 (2006). Lưu ý rằng đây không chỉ đơn giản là Dijkstra, tất nhiên, vì toàn bộ vấn đề là để có được kết quả nhanh hơn.

+2

Tôi đã tìm thấy thách thức mà bạn đã đề cập trong số http://www.dis.uniroma1.it/~challenge9/papers.shtml rất ấn tượng. Cảm ơn. –

+1

Xem thêm http://algo2.iti.uni-karlsruhe.de/english/1087.php cho phương pháp Phân cấp đối số. –

0

Nó sẽ phụ thuộc vào rất nhiều thứ. Bạn biết bao nhiêu về dữ liệu đầu vào của mình? Nó dày đặc hay thưa thớt? Điều đó sẽ thay đổi các phiên bản của thuật toán là nhanh nhất.

Nếu nó dày đặc, chỉ cần sử dụng ma trận. Nếu nó thưa thớt, bạn có thể muốn xem xét các cấu trúc dữ liệu hiệu quả hơn để tìm đỉnh gần nhất tiếp theo. Nếu bạn có thêm thông tin về tập dữ liệu của mình chứ không chỉ là kết nối đồ thị, thì hãy xem liệu một thuật toán khác có hoạt động tốt hơn như A * hay không.

Vấn đề là, không có phiên bản "nhanh nhất" của thuật toán.

1

Lần cuối cùng tôi kiểm tra, Thuật toán Dijkstra trả về một giải pháp tối ưu. Tất cả việc triển khai "đúng" của Dijkstra đều phải trả về cùng một kết quả mỗi lần.

Tương tự, phân tích tiệm cận cho thấy rằng tối ưu hóa nhỏ đối với việc triển khai cụ thể sẽ không ảnh hưởng đến hiệu suất đáng kể khi kích thước đầu vào tăng lên.

+0

Vô lý. Sự khác biệt duy nhất có thể là khi có nhiều "đường đi ngắn nhất" với cùng chiều dài (trong vòng một lỗi làm tròn) (chi phí). Nếu một số triển khai không tìm thấy một đường đi ngắn nhất, nó là lỗi. Trừ khi tỷ lệ khoảng cách nút của bạn là rất cao (như 1e20 hoặc hơn), bạn chắc chắn sẽ không thấy sự khác biệt 1-2% phần trăm, ngay cả với phao nổi, không nói về tăng gấp đôi. – Suma

3

Có thể tôi không trả lời câu hỏi của bạn. Quan điểm của tôi là tại sao sử dụng Dijkstra khi có nhiều thuật toán hiệu quả hơn cho vấn đề của bạn. Nếu biểu đồ của bạn lấp đầy thuộc tính hình tam giác (đó là biểu đồ euclidian)

| ab | + | bc | > | ac |

(khoảng cách từ nút a đến nút b cộng với khoảng cách từ nút b đến nút c lớn hơn khoảng cách từ nút a đến nút c), sau đó bạn có thể áp dụng thuật toán A *. Thuật toán này khá hiệu quả. Nếu không, hãy xem xét sử dụng heuristics. Việc triển khai không phải là vấn đề lớn. Thuật toán được sử dụng không quan trọng.

+0

Tôi không nghĩ bạn thậm chí cần * tài sản hình tam giác. Tất cả bạn cần là một chức năng heuristic tốt. Với một người nghèo nhưng hợp lệ heuristic, A * xuống cấp để Dijkstra. – MSalters

+0

Đúng. Bạn đúng rồi. Những gì tôi đã nói là một Heuristic có thể chấp nhận được. – Luixv

2

Hai điểm tôi muốn thực hiện: 1) Dijkstra vs A * Thuật toán Dijkstra là một thuật toán lập trình động, không phải là phương pháp phỏng đoán. A * là một heuristic bởi vì nó cũng sử dụng một chức năng heuristic (cho phép nói h (x)) để "ước tính" làm thế nào gần một điểm x là nhận được đến điểm kết thúc. Thông tin này được khai thác trong các quyết định tiếp theo mà các nút để khám phá tiếp theo.

Đối với các trường hợp như biểu đồ Euclide, thì A * hoạt động tốt vì hàm heuristic dễ xác định (ví dụ, đơn giản có thể sử dụng khoảng cách Euclide). Tuy nhiên, đối với các đồ thị phi Euclide có thể khó xác định hàm heuristic hơn, và định nghĩa sai có thể dẫn đến một đường dẫn không tối ưu.

Do đó, dijkstra có lợi thế so với A * là nó hoạt động với bất kỳ đồ thị chung nào (ngoại trừ A * nhanh hơn trong một số trường hợp). Nó cũng có thể được thực hiện nhất định sử dụng các thuật toán này thay thế cho nhau, dẫn đến kết quả khác nhau.

2) Thuật toán dijkstra (và các thuật toán khác như A *) sử dụng hàng đợi ưu tiên để có được nút tiếp theo cần khám phá. Một triển khai tốt có thể sử dụng một đống thay vì một hàng đợi, và thậm chí tốt hơn có thể sử dụng một đống heap. Điều này có thể giải thích thời gian chạy khác nhau.

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