Menu
×

Được chứng nhận

Ghi lại kiến ​​thức của bạn

Đăng nhập Đăng ký

Tạo Tài khoản Example.com.vn miễn phí để cải thiện trải nghiệm học tập của bạn

Người tìm đường và việc học của tôi

Theo dõi tiến độ học tập của bạn tại Example.com.vn và thu thập phần thưởng

Nâng cấp

Trở thành người dùng PLUS và mở khóa các tính năng mạnh mẽ (không có quảng cáo, lưu trữ, hỗ trợ, ..)

Bắt đầu từ đâu

Bạn không chắc chắn muốn bắt đầu từ đâu? Đi theo con đường được hướng dẫn của chúng tôi

Trình chỉnh sửa mã (Dùng thử)

Với trình chỉnh sửa mã trực tuyến của chúng tôi, bạn có thể chỉnh sửa mã và xem kết quả trong trình duyệt của mình

Video

Tìm hiểu những điều cơ bản về HTML qua video hướng dẫn thú vị và hấp dẫn

Mẫu

Chúng tôi đã tạo một loạt mẫu trang web đáp ứng mà bạn có thể sử dụng - miễn phí!

Web hosting

Lưu trữ trang web của riêng bạn và chia sẻ nó với mọi người với Example.com.vn Spaces

Tạo một máy chủ

Tạo máy chủ của riêng bạn bằng Python, PHP, React.js, Node.js, Java, C#, v.v.

Làm thế nào để

Bộ sưu tập lớn các đoạn mã cho HTML, CSS và JavaScript

Khung CSS

Xây dựng các trang web nhanh và phản hồi bằng cách sử dụng khung W3.CSS miễn phí của chúng tôi

Thống kê trình duyệt

Đọc xu hướng dài hạn của việc sử dụng trình duyệt

Tốc độ gõ

Kiểm tra tốc độ đánh máy của bạn

Đào tạo AWS

Tìm hiểu dịch vụ web của Amazon

Bộ chọn màu

Sử dụng công cụ chọn màu của chúng tôi để tìm các màu RGB, HEX và HSL khác nhau. Bánh xe màu hình tròn thể hiện sự chuyển màu trong quang phổ

Trò chơi mã

Trò chơi mã hóa W3Schools! Giúp linh miêu thu thập nón thông Logo Lynx

Đặt mục tiêu

Nhận hành trình học tập được cá nhân hóa dựa trên các kỹ năng và mục tiêu hiện tại của bạn

Bản tin

Tham gia bản tin của chúng tôi và có quyền truy cập vào nội dung độc quyền mỗi tháng

Việc làm

Thuê những tài năng công nghệ hàng đầu. Hợp lý hóa quy trình tuyển dụng của bạn để có đội ngũ phù hợp hoàn hảo

Lớp học

Hãy liên hệ để sử dụng Example.com.vn Plus và các chứng chỉ với tư cách là một tổ chức giáo dục

×
HTML CSS JAVASCRIPT SQL PYTHON JAVA PHP CÁCH W3.CSS C C++ C# BOOTSTRAP REACT MYSQL JQUERY EXCEL XML DJANGO NUMPY PANDAS NODEJS R TYPESCRIPT ANGULAR GIT POSTGRESQL MONGODB ASP AI GO KOTLIN SASS VUE DSA GEN AI SCIPY AWS AN NINH MẠNG DỮ LIỆU KHOA HỌC

Hướng dẫn DSA

DSA TRANG CHỦ DSA Giới thiệu Thuật toán đơn giản DSA

Mảng

Mảng DSA Sắp xếp bong bóng DSA Sắp xếp lựa chọn DSA Sắp xếp chèn DSA Sắp xếp nhanh DSA Sắp xếp đếm DSA Sắp xếp cơ số DSA Sắp xếp hợp nhất DSA Tìm kiếm tuyến tính DSA Tìm kiếm nhị phân DSA

Danh sách liên kết

Danh sách liên kết DSA Danh sách liên kết DSA trong bộ nhớ Danh sách liên kết DSA Các loại Danh sách liên kết Hoạt động

Ngăn xếp & hàng đợi

Ngăn xếp DSA Hàng đợi DSA

Bảng băm

Bảng băm DSA Bộ hàm băm DSA Bản đồ hàm băm DSA

Cây

Cây DSA Cây nhị phân DSA DSA Traversal đặt hàng trước DSA Traversal theo thứ tự DSA Traversal DSA sau thực hiện mảng DSA Cây tìm kiếm nhị phân DSA Cây AVL DSA

Đồ thị

Đồ thị DSA Thực hiện đồ thị Đồ thị DSA Phát hiện chu trình DSA truyền tải

Con đường ngắn nhất

Đường đi ngắn nhất DSA DSA Bellman-Ford của DSA Dijkstra

Cây bao trùm tối thiểu

Cây khung tối thiểu DSA Prim's DSA Kruskal's

Lưu lượng cực đại

DSA Lưu lượng tối đa DSA Ford-Fulkerson DSA Edmonds-Karp

Độ phức tạp thời gian

Giới thiệu Sắp xếp bong bóng Lựa chọn Sắp xếp Chèn Sắp xếp Sắp xếp nhanh Đếm Sắp xếp Cơ số Sắp xếp Hợp nhất Sắp xếp Tìm kiếm tuyến tính Tìm kiếm nhị phân

Tham chiếu DSA

Thuật toán Euclide DSA Thuật toán tham lam DSA Ghi nhớ DSA DSA Người bán hàng du lịch

Ví dụ về DSA

Ví dụ về DSA Bài tập DSA Câu hỏi DSA Chứng chỉ DSA

Thuật toán DSA Bellman-Ford


Thuật toán Bellman-Ford

Thuật toán Bellman-Ford phù hợp nhất để tìm đường đi ngắn nhất trong đồ thị có hướng, với một hoặc nhiều trọng số cạnh âm, từ đỉnh nguồn đến tất cả các đỉnh khác.

Nó làm như vậy bằng cách liên tục kiểm tra tất cả các cạnh trong biểu đồ để tìm các đường đi ngắn hơn, với số lần bằng số đỉnh trong biểu đồ (trừ 1).

4 -3 3 3 B thông tin C thông tin -4 2 4 7 5 MỘT thông tin E thông tin D 0 4 7 3 2 3 3 3 -4 5 1 -3

Thuật toán Bellman-Ford cũng có thể được sử dụng cho các đồ thị có cạnh dương (cả có hướng và vô hướng), giống như chúng ta có thể làm với thuật toán Dijkstra, nhưng thuật toán Dijkstra được ưa thích hơn trong những trường hợp như vậy vì nó nhanh hơn.

Sử dụng thuật toán Bellman-Ford trên đồ thị có chu kỳ âm sẽ không tạo ra kết quả là đường đi ngắn nhất vì trong chu trình âm, chúng ta luôn có thể đi thêm một vòng nữa và nhận được đường đi ngắn hơn.

Chu trình âm là đường đi mà chúng ta có thể đi theo đường tròn, trong đó tổng trọng số của các cạnh là âm.

May mắn thay, thuật toán Bellman-Ford có thể được triển khai để phát hiện và báo cáo một cách an toàn sự hiện diện của các chu kỳ tiêu cực.

Làm thế nào nó hoạt động:

  1. Đặt khoảng cách ban đầu thành 0 cho đỉnh nguồn và đặt khoảng cách ban đầu thành vô cùng cho tất cả các đỉnh khác.
  2. Đối với mỗi cạnh, hãy kiểm tra xem có thể tính được khoảng cách ngắn hơn hay không và cập nhật khoảng cách nếu khoảng cách được tính ngắn hơn.
  3. Kiểm tra tất cả các cạnh (bước 2) \(V-1\) lần. Số lần này bằng số đỉnh (\(V\)), trừ đi một.
  4. Tùy chọn: Kiểm tra chu kỳ âm. Điều này sẽ được giải thích chi tiết hơn sau.

Hoạt hình của thuật toán Bellman-Ford ở trên chỉ cho chúng ta thấy khi kiểm tra một cạnh dẫn đến khoảng cách được cập nhật, chứ không hiển thị tất cả các kiểm tra cạnh khác không dẫn đến khoảng cách được cập nhật.


Chạy qua thủ công

Thuật toán Bellman-Ford thực sự khá đơn giản vì nó kiểm tra tất cả các cạnh bằng cách sử dụng ma trận kề. Mỗi lần kiểm tra là để xem liệu có thể tạo ra khoảng cách ngắn hơn hay không bằng cách đi từ đỉnh ở một bên của cạnh, qua cạnh, đến đỉnh ở phía bên kia của cạnh.

Và việc kiểm tra tất cả các cạnh này được thực hiện \(V - 1\) lần, với \(V\) là số đỉnh trong biểu đồ.

Đây là cách thuật toán Bellman-Ford kiểm tra tất cả các cạnh trong ma trận kề trong biểu đồ của chúng tôi 5-1=4 lần:

4 -3 3 3 B C -4 2 4 7 5 MỘT E D 4 -3 3 3 -4 2 4 7 5 MỘT B C D E MỘT B C D E 4 5 -4 -3 4 7 3 2 3

Đã kiểm tra tất cả các cạnh 0 lần.

Bốn cạnh đầu tiên được kiểm tra trong biểu đồ của chúng tôi là A->C, A->E, B->C và C->A. Việc kiểm tra bốn cạnh đầu tiên này không dẫn đến bất kỳ cập nhật nào về khoảng cách ngắn nhất vì đỉnh bắt đầu của tất cả các cạnh này có khoảng cách vô hạn.

4 -3 3 3 B thông tin C thông tin -4 2 4 7 5 MỘT thông tin E thông tin D 0

Sau khi kiểm tra các cạnh của đỉnh A, B và C, các cạnh của D cũng được kiểm tra. Vì điểm bắt đầu (đỉnh D) có khoảng cách 0 nên khoảng cách cập nhật cho A, B và C là trọng số của các cạnh đi ra từ đỉnh D.

4 -3 3 3 B thông tin C 7 -4 2 4 7 5 MỘT 4 E 3 D 0

Các cạnh tiếp theo cần kiểm tra là các cạnh đi ra từ đỉnh E, dẫn đến cập nhật khoảng cách cho đỉnh B và C.

4 -3 3 3 B 5 C 6 -4 2 4 7 5 MỘT 4 E 3 D 0

Thuật toán Bellman-Ford hiện đã kiểm tra tất cả các cạnh 1 lần. Thuật toán sẽ kiểm tra tất cả các cạnh thêm 3 lần nữa trước khi kết thúc, vì Bellman-Ford sẽ kiểm tra tất cả các cạnh với số lần bằng số đỉnh trong đồ thị, trừ đi 1.

Thuật toán bắt đầu kiểm tra tất cả các cạnh lần thứ hai, bắt đầu bằng việc kiểm tra các cạnh đi ra từ đỉnh A. Việc kiểm tra các cạnh A->C và A->E không dẫn đến khoảng cách được cập nhật.

4 -3 3 3 B 5 C 6 -4 2 4 7 5 MỘT 4 E 3 D 0

Cạnh tiếp theo cần kiểm tra là B->C, đi ra từ đỉnh B. Điều này dẫn đến khoảng cách cập nhật từ đỉnh D đến C là 5-4=1.

4 -3 3 3 B 5 C 1 -4 2 4 7 5 MỘT 4 E 3 D 0

Kiểm tra cạnh tiếp theo C->A, dẫn đến khoảng cách cập nhật 1-3=-2 cho đỉnh A.

4 -3 3 3 B 5 C 1 -4 2 4 7 5 MỘT -2 E 3 D 0

Việc kiểm tra cạnh C->A ở vòng 2 của thuật toán Bellman-Ford thực sự là lần kiểm tra cuối cùng dẫn đến khoảng cách được cập nhật cho biểu đồ cụ thể này. Thuật toán sẽ tiếp tục kiểm tra tất cả các cạnh thêm 2 lần nữa mà không cập nhật bất kỳ khoảng cách nào.

Việc kiểm tra tất cả các cạnh \(V-1\) lần trong thuật toán Bellman-Ford có vẻ như rất nhiều, nhưng việc này được thực hiện nhiều lần để đảm bảo rằng sẽ luôn tìm được khoảng cách ngắn nhất.


Triển khai thuật toán Bellman-Ford

Việc triển khai thuật toán Bellman-Ford rất giống với cách chúng tôi triển khai thuật toán Dijkstra .

Chúng tôi bắt đầu bằng cách tạo lớp Graph , trong đó các phương thức __init__ , add_edgeadd_vertex sẽ được sử dụng để tạo biểu đồ cụ thể mà chúng tôi muốn chạy thuật toán Bellman-Ford để tìm đường đi ngắn nhất.

 class Graph:
    def __init__(self, size):
        self.adj_matrix = [[0] * size for _ in range(size)]
        self.size = size
        self.vertex_data = [''] * size

    def add_edge(self, u, v, weight):
        if 0 <= u < self.size and 0 <= v < self.size:
            self.adj_matrix[u][v] = weight
            #self.adj_matrix[v][u] = weight  # For undirected graph

    def add_vertex_data(self, vertex, data):
        if 0 <= vertex < self.size:
            self.vertex_data[vertex] = data

Phương thức bellman_ford cũng được đặt bên trong lớp Graph . Phương pháp này chạy thuật toán Bellman-Ford.

    def bellman_ford(self, start_vertex_data):
        start_vertex = self.vertex_data.index(start_vertex_data)
        distances = [float('inf')] * self.size
        distances[start_vertex] = 0

        for i in range(self.size - 1):
            for u in range(self.size):
                for v in range(self.size):
                    if self.adj_matrix[u][v] != 0:
                        if distances[u] + self.adj_matrix[u][v] < distances[v]:
                            distances[v] = distances[u] + self.adj_matrix[u][v]
                            print(f"Relaxing edge {self.vertex_data[u]}-{self.vertex_data[v]}, Updated distance to {self.vertex_data[v]}: {distances[v]}")

        return distances

Dòng 18-19: Lúc đầu, tất cả các đỉnh được đặt có khoảng cách vô hạn tính từ đỉnh bắt đầu, ngoại trừ chính đỉnh bắt đầu, trong đó khoảng cách được đặt thành 0.

Dòng 21: Tất cả các cạnh đều được kiểm tra \(V-1\) lần.

Dòng 22-23: Vòng lặp for kép kiểm tra tất cả các cạnh trong ma trận kề. Với mọi đỉnh u , kiểm tra các cạnh đi đến đỉnh v .

Dòng 24-26: Nếu cạnh tồn tại và nếu khoảng cách tính toán ngắn hơn khoảng cách hiện tại, hãy cập nhật khoảng cách tới đỉnh đó v .

Mã hoàn chỉnh, bao gồm cả việc khởi tạo biểu đồ cụ thể của chúng tôi và mã để chạy thuật toán Bellman-Ford, trông như thế này:

Ví dụ

Trăn:

 class Graph:
    def __init__(self, size):
        self.adj_matrix = [[0] * size for _ in range(size)]
        self.size = size
        self.vertex_data = [''] * size

    def add_edge(self, u, v, weight):
        if 0 <= u < self.size and 0 <= v < self.size:
            self.adj_matrix[u][v] = weight
            #self.adj_matrix[v][u] = weight  # For undirected graph

    def add_vertex_data(self, vertex, data):
        if 0 <= vertex < self.size:
            self.vertex_data[vertex] = data

    def bellman_ford(self, start_vertex_data):
        start_vertex = self.vertex_data.index(start_vertex_data)
        distances = [float('inf')] * self.size
        distances[start_vertex] = 0

        for i in range(self.size - 1):
            for u in range(self.size):
                for v in range(self.size):
                    if self.adj_matrix[u][v] != 0:
                        if distances[u] + self.adj_matrix[u][v] < distances[v]:
                            distances[v] = distances[u] + self.adj_matrix[u][v]
                            print(f"Relaxing edge {self.vertex_data[u]}-{self.vertex_data[v]}, Updated distance to {self.vertex_data[v]}: {distances[v]}")

        return distances

g = Graph(5)

g.add_vertex_data(0, 'A')
g.add_vertex_data(1, 'B')
g.add_vertex_data(2, 'C')
g.add_vertex_data(3, 'D')
g.add_vertex_data(4, 'E')

g.add_edge(3, 0, 4)  # D -> A, weight 4
g.add_edge(3, 2, 7)  # D -> C, weight 7
g.add_edge(3, 4, 3)  # D -> E, weight 3
g.add_edge(0, 2, 4)  # A -> C, weight 4
g.add_edge(2, 0, -3) # C -> A, weight -3
g.add_edge(0, 4, 5)  # A -> E, weight 5
g.add_edge(4, 2, 3)  # E -> C, weight 3
g.add_edge(1, 2, -4) # B -> C, weight -4
g.add_edge(4, 1, 2)  # E -> B, weight 2

# Running the Bellman-Ford algorithm from D to all vertices
print("\nThe Bellman-Ford Algorithm starting from vertex D:")
distances = g.bellman_ford('D')
for i, d in enumerate(distances):
    print(f"Distance from D to {g.vertex_data[i]}: {d}")
Chạy ví dụ »

Các cạnh tiêu cực trong thuật toán Bellman-Ford

Nói rằng thuật toán Bellman-Ford tìm ra "đường đi ngắn nhất" là không trực quan, bởi vì làm sao chúng ta có thể vẽ hoặc tưởng tượng những khoảng cách âm? Vì vậy, để dễ hiểu hơn, thay vào đó chúng ta có thể nói rằng đó là "con đường rẻ nhất " được tìm thấy với Bellman-Ford.

Trong thực tế, thuật toán Bellman-Ford chẳng hạn có thể giúp chúng ta tìm ra các tuyến đường giao hàng trong đó trọng số của cạnh biểu thị chi phí nhiên liệu và những thứ khác, trừ đi số tiền kiếm được bằng cách lái cạnh đó giữa hai đỉnh đó.

4 -3 3 3 B 5 C 1 -4 2 4 7 5 MỘT -2 E 3 D 0

Với cách giải thích này, trọng số -3 ở cạnh C->A có thể có nghĩa là chi phí nhiên liệu là 5 đô la khi lái xe từ C đến A và chúng tôi được trả 8 đô la để nhận các gói hàng ở C và giao chúng ở A. Vì vậy, chúng tôi cuối cùng kiếm được nhiều hơn 3 đô la so với số tiền chúng ta chi tiêu. Do đó, bạn có thể kiếm được tổng cộng 2 USD bằng cách điều khiển tuyến đường giao hàng D->E->B->C->A trong biểu đồ trên của chúng tôi.


Chu kỳ âm trong thuật toán Bellman-Ford

Nếu chúng ta có thể đi các đường tròn trong đồ thị và tổng các cạnh trong đường tròn đó là âm thì chúng ta có một chu trình âm.

4 -9 3 3 B C -4 2 4 7 5 MỘT E D

Bằng cách thay đổi trọng số trên cạnh C->A từ -3 thành -9, chúng ta nhận được hai chu kỳ âm: A->C->A và A->E->C->A. Và mỗi khi chúng tôi kiểm tra các cạnh này bằng thuật toán Bellman-Ford, khoảng cách chúng tôi tính toán và cập nhật ngày càng thấp hơn.

Vấn đề với chu kỳ âm là không tồn tại đường đi ngắn nhất, bởi vì chúng ta luôn có thể đi thêm một vòng nữa để có được đường đi ngắn hơn.

Đó là lý do tại sao việc triển khai thuật toán Bellman-Ford với việc phát hiện các chu kỳ âm là rất hữu ích.


Phát hiện các chu kỳ âm trong thuật toán Bellman-Ford

Sau khi chạy thuật toán Bellman-Ford, kiểm tra tất cả các cạnh trong biểu đồ \(V-1\) lần, tất cả các khoảng cách ngắn nhất đều được tìm thấy.

Tuy nhiên, nếu biểu đồ chứa các chu kỳ âm và chúng ta tiến hành thêm một vòng nữa để kiểm tra tất cả các cạnh, chúng ta sẽ tìm thấy ít nhất một khoảng cách ngắn hơn ở vòng cuối cùng này, phải không?

Vì vậy, để phát hiện chu kỳ âm trong thuật toán Bellman-Ford, sau khi kiểm tra tất cả các cạnh \(V-1\) lần, chúng ta chỉ cần kiểm tra tất cả các cạnh một lần nữa và nếu lần trước tìm thấy khoảng cách ngắn hơn thì chúng ta có thể kết luận rằng một chu kỳ tiêu cực phải tồn tại.

Dưới đây là phương pháp bellman_ford , bao gồm tính năng phát hiện chu kỳ âm, chạy trên biểu đồ ở trên với chu kỳ âm do trọng số cạnh C->A là -9:

Ví dụ

Trăn:

    def bellman_ford(self, start_vertex_data):
        start_vertex = self.vertex_data.index(start_vertex_data)
        distances = [float('inf')] * self.size
        distances[start_vertex] = 0

        for i in range(self.size - 1):
            for u in range(self.size):
                for v in range(self.size):
                    if self.adj_matrix[u][v] != 0:
                        if distances[u] + self.adj_matrix[u][v] < distances[v]:
                            distances[v] = distances[u] + self.adj_matrix[u][v]
                            print(f"Relaxing edge {self.vertex_data[u]}->{self.vertex_data[v]}, Updated distance to {self.vertex_data[v]}: {distances[v]}")

        # Negative cycle detection
        for u in range(self.size):
            for v in range(self.size):
                if self.adj_matrix[u][v] != 0:
                    if distances[u] + self.adj_matrix[u][v] < distances[v]:
                        return (True, None)  # Indicate a negative cycle was found

        return (False, distances)  # Indicate no negative cycle and return distances
Chạy ví dụ »

Dòng 30-33: Tất cả các cạnh được kiểm tra một lần nữa để xem có chu kỳ âm hay không.

Dòng 34: Trả về True chỉ ra rằng tồn tại một chu trình âm và None được trả về thay vì khoảng cách ngắn nhất, bởi vì việc tìm khoảng cách ngắn nhất trong biểu đồ có chu kỳ âm không có ý nghĩa gì (vì luôn có thể tìm thấy khoảng cách ngắn hơn bằng cách kiểm tra tất cả các cạnh một lần nữa).

Dòng 36: Trả về False có nghĩa là không có chu kỳ âm và distances có thể được trả về.


Trả về đường dẫn từ thuật toán Bellman-Ford

Chúng tôi hiện đang tìm tổng trọng số của các đường đi ngắn nhất, ví dụ: "Khoảng cách từ D đến A: -2" là kết quả của việc chạy thuật toán Bellman-Ford.

Nhưng bằng cách ghi lại đỉnh trước của mỗi đỉnh bất cứ khi nào một cạnh được thả lỏng, chúng ta có thể sử dụng điều đó sau này trong mã của mình để in kết quả bao gồm cả các đường đi ngắn nhất thực tế. Điều này có nghĩa là chúng tôi có thể cung cấp thêm thông tin trong kết quả của mình, với đường dẫn thực tế ngoài trọng số đường dẫn: "D->E->B->C->A, Khoảng cách: -2".

Ví dụ mã cuối cùng này là mã hoàn chỉnh cho thuật toán Bellman-Ford, với mọi thứ chúng ta đã thảo luận cho đến bây giờ: tìm trọng số của các đường đi ngắn nhất, phát hiện các chu kỳ âm và tìm các đường đi ngắn nhất thực tế:

Ví dụ

Trăn:

 class Graph:
    def __init__(self, size):
        self.adj_matrix = [[0] * size for _ in range(size)]
        self.size = size
        self.vertex_data = [''] * size

    def add_edge(self, u, v, weight):
        if 0 <= u < self.size and 0 <= v < self.size:
            self.adj_matrix[u][v] = weight
            #self.adj_matrix[v][u] = weight  # For undirected graph

    def add_vertex_data(self, vertex, data):
        if 0 <= vertex < self.size:
            self.vertex_data[vertex] = data

    def bellman_ford(self, start_vertex_data):
        start_vertex = self.vertex_data.index(start_vertex_data)
        distances = [float('inf')] * self.size
        predecessors = [None] * self.size
        distances[start_vertex] = 0

        for i in range(self.size - 1):
            for u in range(self.size):
                for v in range(self.size):
                    if self.adj_matrix[u][v] != 0:
                        if distances[u] + self.adj_matrix[u][v] < distances[v]:
                            distances[v] = distances[u] + self.adj_matrix[u][v]
                            predecessors[v] = u
                            print(f"Relaxing edge {self.vertex_data[u]}->{self.vertex_data[v]}, Updated distance to {self.vertex_data[v]}: {distances[v]}")

        # Negative cycle detection
        for u in range(self.size):
            for v in range(self.size):
                if self.adj_matrix[u][v] != 0:
                    if distances[u] + self.adj_matrix[u][v] < distances[v]:
                        return (True, None, None)  # Indicate a negative cycle was found

        return (False, distances, predecessors)  # Indicate no negative cycle and return distances
    
    def get_path(self, predecessors, start_vertex, end_vertex):
        path = []
        current = self.vertex_data.index(end_vertex)
        while current is not None:
            path.insert(0, self.vertex_data[current])
            current = predecessors[current]
            if current == self.vertex_data.index(start_vertex):
                path.insert(0, start_vertex)
                break
        return '->'.join(path)

g = Graph(5)

g.add_vertex_data(0, 'A')
g.add_vertex_data(1, 'B')
g.add_vertex_data(2, 'C')
g.add_vertex_data(3, 'D')
g.add_vertex_data(4, 'E')

g.add_edge(3, 0, 4)  # D -> A, weight 4
g.add_edge(3, 2, 7)  # D -> C, weight 7
g.add_edge(3, 4, 3)  # D -> E, weight 3
g.add_edge(0, 2, 4)  # A -> C, weight 4
g.add_edge(2, 0, -3) # C -> A, weight -3
g.add_edge(0, 4, 5)  # A -> E, weight 5
g.add_edge(4, 2, 3)  # E -> C, weight 3
g.add_edge(1, 2, -4) # B -> C, weight -4
g.add_edge(4, 1, 2)  # E -> B, weight 2

# Running the Bellman-Ford algorithm from D to all vertices
print("\nThe Bellman-Ford Algorithm starting from vertex D:")
negative_cycle, distances, predecessors = g.bellman_ford('D')
if not negative_cycle:
    for i, d in enumerate(distances):
        if d != float('inf'):
            path = g.get_path(predecessors, 'D', g.vertex_data[i])
            print(f"{path}, Distance: {d}")
        else:
            print(f"No path from D to {g.vertex_data[i]}, Distance: Infinity")
else:
    print("Negative weight cycle detected. Cannot compute shortest paths.")
Chạy ví dụ »

Dòng 19: Mảng predecessors giữ đỉnh tiền thân của mỗi đỉnh theo đường đi ngắn nhất.

Dòng 28: Mảng predecessors được cập nhật với đỉnh mới tiền thân mỗi khi một cạnh được thư giãn.

Dòng 40-49: Phương thức get_path sử dụng mảng predecessors để tạo chuỗi đường dẫn ngắn nhất cho mỗi đỉnh.


Độ phức tạp thời gian của thuật toán Bellman-Ford

Độ phức tạp về thời gian của thuật toán Bellman-Ford chủ yếu phụ thuộc vào các vòng lặp lồng nhau.

Vòng lặp for bên ngoài chạy \(V-1\) lần hoặc \(V\) lần trong trường hợp chúng ta cũng phát hiện chu kỳ âm. Đối với các đồ thị có nhiều đỉnh, việc kiểm tra tất cả các cạnh ít hơn một lần so với số đỉnh sẽ tạo ra sự khác biệt nhỏ, vì vậy chúng ta có thể nói rằng vòng lặp bên ngoài đóng góp \(O(V)\) vào độ phức tạp về thời gian.

Hai vòng lặp for bên trong kiểm tra tất cả các cạnh trong biểu đồ. Nếu chúng ta giả sử một trường hợp xấu nhất về độ phức tạp thời gian, thì chúng ta có một đồ thị rất dày đặc trong đó mỗi đỉnh có một cạnh với mọi đỉnh khác, do đó, với tất cả các đỉnh \(V\) cạnh với tất cả các đỉnh khác \(V\ ) phải được kiểm tra, điều này góp phần làm tăng độ phức tạp về thời gian.

Vì vậy, tổng cộng, chúng ta có được độ phức tạp về thời gian cho thuật toán Bellman-Ford:

\[ O(V^3) \]

Tuy nhiên, trong các tình huống thực tế và đặc biệt đối với các đồ thị thưa thớt, nghĩa là mỗi đỉnh chỉ có các cạnh đối với một phần nhỏ của các đỉnh khác, độ phức tạp về thời gian của hai vòng lặp for bên trong kiểm tra tất cả các cạnh có thể xấp xỉ từ \(O(V^2) \) đến \(O(E)\) và chúng tôi nhận được tổng độ phức tạp về thời gian cho Bellman-Ford:

\[ O(V \cdot E) \]

Độ phức tạp về thời gian của thuật toán Bellman-Ford chậm hơn so với thuật toán Dijkstra, nhưng Bellman-Ford có thể tìm đường đi ngắn nhất trong đồ thị có cạnh âm và nó có thể phát hiện các chu kỳ âm, điều mà thuật toán Dijkstra không thể làm được.


Bài tập DSA

Kiểm tra bản thân bằng các bài tập

Bài tập:

Trong ma trận kề dưới đây:

Ma trận kề

Trọng lượng cạnh của cạnh đi từ D đến E là bao nhiêu?

Trọng số của cạnh D->E là .

Bắt đầu bài tập



×

Liên hệ bán hàng

Nếu bạn muốn sử dụng dịch vụ của Example.com.vn với tư cách là một tổ chức giáo dục, nhóm hoặc doanh nghiệp, hãy gửi email cho chúng tôi:
[email được bảo vệ]

Báo cáo lỗi

Nếu bạn muốn báo cáo lỗi hoặc nếu bạn muốn đưa ra đề xuất, hãy gửi email cho chúng tôi:
[email được bảo vệ]

Example.com.vn được tối ưu hóa cho việc học tập và đào tạo. Các ví dụ có thể được đơn giản hóa để cải thiện khả năng đọc và học. Các hướng dẫn, tài liệu tham khảo và ví dụ liên tục được xem xét để tránh sai sót, nhưng chúng tôi không thể đảm bảo tính chính xác hoàn toàn của mọi nội dung. Khi sử dụng W3Schools, bạn đồng ý đã đọc và chấp nhận các điều khoản sử dụng , chính sách cookie và quyền riêng tư của chúng tôi.

Bản quyền 1999-2024 của Refsnes Data. Đã đăng ký Bản quyền. Example.com.vn được cung cấp bởi W3.CSS .