Tại sao Python sử dụng bảng băm để thực hiện dict, nhưng không phải Red-Black Tree? [đóng cửa]


11

Tại sao Python sử dụng bảng băm để thực hiện dict, nhưng không phải Red-Black Tree?

Chìa khóa là gì? Hiệu suất?


2
Chia sẻ nghiên cứu của bạn giúp mọi người . Hãy cho chúng tôi những gì bạn đã cố gắng và tại sao nó không đáp ứng nhu cầu của bạn. Điều này chứng tỏ rằng bạn đã dành thời gian để cố gắng tự giúp mình, nó giúp chúng tôi tránh nhắc lại các câu trả lời rõ ràng và hầu hết nó giúp bạn có được câu trả lời cụ thể và phù hợp hơn. Xem thêm Cách hỏi
gnat

Câu trả lời:


16

Đây là một câu trả lời chung chung, không dành riêng cho Python.

Thuật toán so sánh phức tạp

       | Hash Table  |   Red-Black Tree    |
-------+-------------+---------------------+
Space  | O(n) : O(n) | O(n)     : O(n)     |
Insert | O(1) : O(n) | O(log n) : O(log n) |
Fetch  | O(1) : O(n) | O(log n) : O(log n) |
Delete | O(1) : O(n) | O(log n) : O(log n) |
       | avg  :worst | average  : worst    |

Vấn đề với bảng băm là băm có thể va chạm. Có nhiều cơ chế khác nhau để giải quyết các va chạm, ví dụ như mở địa chỉ hoặc tách chuỗi. Trường hợp xấu nhất tuyệt đối là tất cả các khóa đều có cùng mã băm, trong trường hợp đó, bảng băm sẽ biến thành một danh sách được liên kết.

Trong tất cả các trường hợp khác, bảng băm là một cấu trúc dữ liệu tuyệt vời, dễ thực hiện và mang lại hiệu suất tốt. Một nhược điểm là việc triển khai có thể nhanh chóng phát triển bảng và phân phối lại các mục nhập của chúng có thể sẽ lãng phí gần như nhiều bộ nhớ như đang thực sự được sử dụng.

Cây RB tự cân bằng và không thay đổi độ phức tạp thuật toán của chúng trong trường hợp xấu nhất. Tuy nhiên, chúng khó thực hiện hơn. Độ phức tạp trung bình của chúng cũng tệ hơn so với bảng băm.

Hạn chế về phím

Tất cả các khóa trong bảng băm phải có thể băm và có thể so sánh cho sự bình đẳng lẫn nhau. Điều này đặc biệt dễ dàng đối với các chuỗi hoặc số nguyên, nhưng cũng khá đơn giản để mở rộng sang các loại do người dùng xác định. Trong một số ngôn ngữ như Java, các thuộc tính này được đảm bảo theo định nghĩa.

Các khóa trong Cây RB phải có tổng thứ tự: mỗi khóa phải tương đương với bất kỳ khóa nào khác và hai khóa phải so sánh nhỏ hơn, lớn hơn hoặc bằng nhau. Bình đẳng thứ tự này phải tương đương với bình đẳng ngữ nghĩa. Điều này rất đơn giản đối với các số nguyên và các số khác, cũng khá dễ dàng đối với các chuỗi (thứ tự chỉ cần nhất quán và không thể quan sát được bên ngoài, vì vậy thứ tự không cần phải xem xét các vị trí [1] ), nhưng khó khăn cho các loại khác không có thứ tự vốn có . Hoàn toàn không thể có các loại khóa khác nhau trừ khi có thể so sánh giữa chúng.

[1]: Thật ra, tôi sai ở đây. Hai chuỗi có thể không bằng byte nhưng vẫn tương đương theo quy tắc của một số ngôn ngữ. Xem ví dụ: chuẩn hóa Unicode cho một ví dụ trong đó hai chuỗi bằng nhau được mã hóa khác nhau. Việc thành phần ký tự Unicode có quan trọng đối với khóa băm của bạn hay không là điều mà việc triển khai bảng băm không thể biết được.

Mọi người có thể nghĩ rằng một giải pháp rẻ tiền cho các khóa RB-Tree sẽ là thử nghiệm đầu tiên cho sự bằng nhau, sau đó so sánh danh tính (ví dụ so sánh các con trỏ). Tuy nhiên, thứ tự này sẽ không mang tính bắc cầu: Nếu a == bid(a) > id(c), thì nó cũng phải tuân theo điều đó id(b) > id(c), điều này không được đảm bảo ở đây. Vì vậy, thay vào đó, chúng tôi có thể sử dụng mã băm của các khóa làm khóa tra cứu. Ở đây, thứ tự hoạt động chính xác, nhưng chúng ta có thể kết thúc với nhiều khóa riêng biệt có cùng mã băm, sẽ được gán cho cùng một nút trong cây RB. Để giải quyết các xung đột băm này, chúng ta có thể sử dụng các chuỗi riêng biệt giống như với các bảng băm, nhưng điều này cũng thừa hưởng hành vi xấu nhất đối với các bảng băm - điều tồi tệ nhất của cả hai thế giới.

Những khía cạnh khác

  • Tôi hy vọng một bảng băm có địa phương bộ nhớ tốt hơn một cây, bởi vì bảng băm về cơ bản chỉ là một mảng.

  • Các mục trong cả hai cấu trúc dữ liệu có chi phí khá cao:

    • bảng băm: khóa, giá trị và con trỏ mục tiếp theo trong trường hợp xâu chuỗi riêng. Ngoài ra lưu trữ mã băm có thể tăng tốc độ thay đổi kích thước.
    • Cây RB: khóa, giá trị, màu sắc, con trỏ con trái, con trỏ con phải. Lưu ý rằng mặc dù màu sắc chỉ là một bit, các vấn đề căn chỉnh có thể có nghĩa là bạn vẫn đang lãng phí đủ dung lượng cho gần như toàn bộ con trỏ hoặc thậm chí gần bốn con trỏ khi chỉ có thể phân bổ các khối bộ nhớ có kích thước bằng hai. Trong mọi trường hợp, một mục nhập cây RB tiêu thụ nhiều bộ nhớ hơn mục nhập bảng băm.
  • Chèn và xóa trong cây RB liên quan đến xoay cây. Chúng không thực sự tốn kém, nhưng có liên quan đến chi phí chung. Trong một hàm băm, việc chèn và xóa không tốn kém hơn một truy cập đơn giản (mặc dù thay đổi kích thước bảng băm khi chèn là một O(n)nỗ lực).

  • Các bảng băm vốn dĩ có thể thay đổi, trong khi đó cây RB cũng có thể được thực hiện theo cách bất biến. Tuy nhiên, điều này hiếm khi hữu ích.


Chúng ta có thể có một bảng băm với các cây RB nhỏ để băm va chạm không?
aragaer

@aragaer không nói chung, nhưng nó có thể trong một số trường hợp cụ thể. Tuy nhiên, các va chạm thường được xử lý bởi các danh sách được liên kết - dễ thực hiện hơn, ít chi phí hơn và thường có hiệu suất cao hơn nhiều vì chúng ta thường chỉ có rất ít va chạm. Nếu chúng ta mong đợi nhiều va chạm, chúng ta có thể thay đổi hàm băm hoặc sử dụng cây B đơn giản hơn. Cây tự cân bằng như cây RB là tuyệt vời, nhưng có nhiều trường hợp đơn giản là chúng không thêm giá trị.
amon

Cây cần các đối tượng hỗ trợ "<". Các bảng băm cần các đối tượng hỗ trợ hàm băm + "=". Vì vậy, cây RB có thể không thể. Nhưng thực sự nếu bảng băm của bạn có bất kỳ va chạm đáng kể nào thì bạn cần một hàm băm mới, không phải là một thuật toán thay thế cho các khóa va chạm.
gnasher729

1

Có rất nhiều lý do thể đúng, nhưng những lý do chính có thể là:

  • Bảng băm dễ thực hiện hơn cây. Không phải là hoàn toàn tầm thường, nhưng bảng băm dễ dàng hơn một chút và tác động đến miền của các khóa pháp lý ít nghiêm ngặt hơn khi bạn chỉ cần một hàm băm và hàm bình đẳng; cây đòi hỏi một hàm tổng thứ tự, và điều đó khó viết hơn nhiều.
  • Bảng băm (có thể) có hiệu suất tốt hơn ở kích thước nhỏ. Điều này rất quan trọng vì một phần đáng kể công việc chỉ liên quan về mặt lý thuyết với các bộ dữ liệu lớn; trong thực tế, phần lớn thực sự hoạt động chỉ với hàng chục hoặc hàng trăm phím, không phải hàng triệu. Hiệu suất quy mô nhỏ rất quan trọng và bạn không thể sử dụng phân tích tiệm cận để tìm ra cái gì là tốt nhất ở đó; bạn phải thực sự thực hiện và đo lường.

Dễ dàng hơn để viết / duy trì và một người chiến thắng hiệu suất trong các trường hợp sử dụng điển hình? Hãy đăng ký cho tôi!

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.