Bộ và từ điển là cấu trúc dữ liệu lý tưởng được sử dụng khi dữ liệu của bạn không có thứ tự nội tại, nhưng có một đối tượng duy nhất có thể được sử dụng để tham chiếu nó [đối tượng tham chiếu thường là một chuỗi, nhưng có thể là bất kỳ loại có thể băm nào]. Đối tượng tham chiếu này được gọi là “khóa”, trong khi dữ liệu là “giá trị. ” Từ điển và bộ gần như giống hệt nhau, ngoại trừ bộ không thực sự chứa giá trị. một bộ chỉ đơn giản là một tập hợp các khóa duy nhất. Như tên ngụ ý, tập hợp rất hữu ích để thực hiện các thao tác tập hợp
Ghi chú
Loại có thể băm là loại thực hiện cả hàm ma thuật
6 vàphonebook
=
{
"John Doe"
:
"555-555-5555"
,
"Albert Einstein"
:
"212-555-5555"
,
}
"John Doe's phone number is"
,
phonebook
[
"John Doe"
]
7 hoặcphonebook
=
{
"John Doe"
:
"555-555-5555"
,
"Albert Einstein"
:
"212-555-5555"
,
}
"John Doe's phone number is"
,
phonebook
[
"John Doe"
]
8. Tất cả các kiểu gốc trong Python đã triển khai những kiểu này và mọi lớp người dùng đều có giá trị mặc định. Xem để biết thêm chi tiếtphonebook
=
{
"John Doe"
:
"555-555-5555"
,
"Albert Einstein"
:
"212-555-5555"
,
}
"John Doe's phone number is"
,
phonebook
[
"John Doe"
]
Trong khi chúng ta đã thấy trong chương trước rằng chúng ta bị hạn chế, tốt nhất là, thời gian tra cứu
9 trên danh sách/bộ không có thứ tự nội tại [thông qua thao tác tìm kiếm], từ điển và bộ cung cấp cho chúng taphonebook
=
{
"John Doe"
:
"555-555-5555"
,
"Albert Einstein"
:
"212-555-5555"
,
}
"John Doe's phone number is"
,
phonebook
[
"John Doe"
]
0 tra cứu dựa trên chỉ mục tùy ý. Ngoài ra, như danh sách/bộ dữ liệu, từ điển và bộ có thời gian chèndef
list_unique_names
[
phonebook
]:
unique_names
=
[]
for
name
,
phonenumber
in
phonebook
:
#
first_name
,
last_name
=
name
.
split
[
" "
,
1
]
for
unique
in
unique_names
:
#
if
unique
==
first_name
:
break
else
:
unique_names
.
append
[
first_name
]
return
len
[
unique_names
]
def
set_unique_names
[
phonebook
]:
unique_names
=
set
[]
for
name
,
phonenumber
in
phonebook
:
#
first_name
,
last_name
=
name
.
split
[
" "
,
1
]
unique_names
.
add
[
first_name
]
#
return
len
[
unique_names
]
phonebook
=
[
[
"John Doe"
,
"555-555-5555"
],
[
"Albert Einstein"
,
"212-555-5555"
],
[
"John Murphey"
,
"202-555-5555"
],
[
"Albert Rutherford"
,
"647-555-5555"
],
[
"Elaine Bodian"
,
"301-555-5555"
],
]
"Number of unique names from set method:"
,
set_unique_names
[
phonebook
]
"Number of unique names from list method:"
,
list_unique_names
[
phonebook
]
1. [] Như chúng ta sẽ thấy trong phần , tốc độ này được thực hiện thông qua việc sử dụng bảng băm địa chỉ mở làm cấu trúc dữ liệu cơ bảndef
list_unique_names
[
phonebook
]:
unique_names
=
[]
for
name
,
phonenumber
in
phonebook
:
#
first_name
,
last_name
=
name
.
split
[
" "
,
1
]
for
unique
in
unique_names
:
#
if
unique
==
first_name
:
break
else
:
unique_names
.
append
[
first_name
]
return
len
[
unique_names
]
def
set_unique_names
[
phonebook
]:
unique_names
=
set
[]
for
name
,
phonenumber
in
phonebook
:
#
first_name
,
last_name
=
name
.
split
[
" "
,
1
]
unique_names
.
add
[
first_name
]
#
return
len
[
unique_names
]
phonebook
=
[
[
"John Doe"
,
"555-555-5555"
],
[
"Albert Einstein"
,
"212-555-5555"
],
[
"John Murphey"
,
"202-555-5555"
],
[
"Albert Rutherford"
,
"647-555-5555"
],
[
"Elaine Bodian"
,
"301-555-5555"
],
]
"Number of unique names from set method:"
,
set_unique_names
[
phonebook
]
"Number of unique names from list method:"
,
list_unique_names
[
phonebook
]
Tuy nhiên, có một chi phí để sử dụng từ điển và bộ. Đầu tiên, chúng thường chiếm dung lượng lớn hơn trong bộ nhớ. Ngoài ra, mặc dù độ phức tạp của việc chèn/tra cứu là
1, nhưng tốc độ thực tế phụ thuộc rất nhiều vào hàm băm đang được sử dụng. Nếu đánh giá hàm băm chậm, thì mọi thao tác trên từ điển hoặc bộ sẽ chậm tương tựdef
list_unique_names
[
phonebook
]:
unique_names
=
[]
for
name
,
phonenumber
in
phonebook
:
#
first_name
,
last_name
=
name
.
split
[
" "
,
1
]
for
unique
in
unique_names
:
#
if
unique
==
first_name
:
break
else
:
unique_names
.
append
[
first_name
]
return
len
[
unique_names
]
def
set_unique_names
[
phonebook
]:
unique_names
=
set
[]
for
name
,
phonenumber
in
phonebook
:
#
first_name
,
last_name
=
name
.
split
[
" "
,
1
]
unique_names
.
add
[
first_name
]
#
return
len
[
unique_names
]
phonebook
=
[
[
"John Doe"
,
"555-555-5555"
],
[
"Albert Einstein"
,
"212-555-5555"
],
[
"John Murphey"
,
"202-555-5555"
],
[
"Albert Rutherford"
,
"647-555-5555"
],
[
"Elaine Bodian"
,
"301-555-5555"
],
]
"Number of unique names from set method:"
,
set_unique_names
[
phonebook
]
"Number of unique names from list method:"
,
list_unique_names
[
phonebook
]
Hãy xem một ví dụ. Giả sử chúng tôi muốn lưu trữ thông tin liên hệ của mọi người trong danh bạ điện thoại. Chúng tôi muốn lưu trữ thông tin này ở dạng giúp dễ dàng trả lời câu hỏi, "Số điện thoại của John Doe là gì?" . Với danh sách, chúng tôi sẽ lưu trữ số điện thoại và tên theo thứ tự và quét qua toàn bộ danh sách để tìm số điện thoại mà chúng tôi yêu cầu, như minh họa trong
Ví dụ 4-1. tra cứu danh bạ điện thoại với một danh sách
def
find_phonenumber
[
phonebook
,
name
]:
for
n
,
p
in
phonebook
:
if
n
==
name
:
return
p
return
None
phonebook
=
[
[
"John Doe"
,
"555-555-5555"
],
[
"Albert Einstein"
,
"212-555-5555"
],
]
"John Doe's phone number is"
,
find_phonenumber
[
phonebook
,
"John Doe"
]
Ghi chú
Chúng tôi cũng có thể làm điều này bằng cách sắp xếp danh sách và sử dụng mô-đun
3 để có được hiệu suấtdef
list_unique_names
[
phonebook
]:
unique_names
=
[]
for
name
,
phonenumber
in
phonebook
:
#
first_name
,
last_name
=
name
.
split
[
" "
,
1
]
for
unique
in
unique_names
:
#
if
unique
==
first_name
:
break
else
:
unique_names
.
append
[
first_name
]
return
len
[
unique_names
]
def
set_unique_names
[
phonebook
]:
unique_names
=
set
[]
for
name
,
phonenumber
in
phonebook
:
#
first_name
,
last_name
=
name
.
split
[
" "
,
1
]
unique_names
.
add
[
first_name
]
#
return
len
[
unique_names
]
phonebook
=
[
[
"John Doe"
,
"555-555-5555"
],
[
"Albert Einstein"
,
"212-555-5555"
],
[
"John Murphey"
,
"202-555-5555"
],
[
"Albert Rutherford"
,
"647-555-5555"
],
[
"Elaine Bodian"
,
"301-555-5555"
],
]
"Number of unique names from set method:"
,
set_unique_names
[
phonebook
]
"Number of unique names from list method:"
,
list_unique_names
[
phonebook
]
9phonebook
=
{
"John Doe"
:
"555-555-5555"
,
"Albert Einstein"
:
"212-555-5555"
,
}
"John Doe's phone number is"
,
phonebook
[
"John Doe"
]
Tuy nhiên, với một từ điển, chúng ta có thể chỉ cần có “chỉ mục” là tên và “giá trị” là số điện thoại, như thể hiện trong. Điều này cho phép chúng tôi chỉ cần tra cứu giá trị chúng tôi cần và nhận tham chiếu trực tiếp đến giá trị đó, thay vì phải đọc mọi giá trị trong tập dữ liệu của chúng tôi
Ví dụ 4-2. Tra cứu danh bạ điện thoại bằng từ điển
phonebook
=
{
"John Doe"
:
"555-555-5555"
,
"Albert Einstein"
:
"212-555-5555"
,
}
"John Doe's phone number is"
,
phonebook
[
"John Doe"
]
Đối với danh bạ điện thoại lớn, sự khác biệt giữa thời gian tra cứu
1 của từ điển và thời giandef
list_unique_names
[
phonebook
]:
unique_names
=
[]
for
name
,
phonenumber
in
phonebook
:
#
first_name
,
last_name
=
name
.
split
[
" "
,
1
]
for
unique
in
unique_names
:
#
if
unique
==
first_name
:
break
else
:
unique_names
.
append
[
first_name
]
return
len
[
unique_names
]
def
set_unique_names
[
phonebook
]:
unique_names
=
set
[]
for
name
,
phonenumber
in
phonebook
:
#
first_name
,
last_name
=
name
.
split
[
" "
,
1
]
unique_names
.
add
[
first_name
]
#
return
len
[
unique_names
]
phonebook
=
[
[
"John Doe"
,
"555-555-5555"
],
[
"Albert Einstein"
,
"212-555-5555"
],
[
"John Murphey"
,
"202-555-5555"
],
[
"Albert Rutherford"
,
"647-555-5555"
],
[
"Elaine Bodian"
,
"301-555-5555"
],
]
"Number of unique names from set method:"
,
set_unique_names
[
phonebook
]
"Number of unique names from list method:"
,
list_unique_names
[
phonebook
]
0 để tìm kiếm tuyến tính trên danh sách [hoặc tốt nhất là thời giandef
list_unique_names
[
phonebook
]:
unique_names
=
[]
for
name
,
phonenumber
in
phonebook
:
#
first_name
,
last_name
=
name
.
split
[
" "
,
1
]
for
unique
in
unique_names
:
#
if
unique
==
first_name
:
break
else
:
unique_names
.
append
[
first_name
]
return
len
[
unique_names
]
def
set_unique_names
[
phonebook
]:
unique_names
=
set
[]
for
name
,
phonenumber
in
phonebook
:
#
first_name
,
last_name
=
name
.
split
[
" "
,
1
]
unique_names
.
add
[
first_name
]
#
return
len
[
unique_names
]
phonebook
=
[
[
"John Doe"
,
"555-555-5555"
],
[
"Albert Einstein"
,
"212-555-5555"
],
[
"John Murphey"
,
"202-555-5555"
],
[
"Albert Rutherford"
,
"647-555-5555"
],
[
"Elaine Bodian"
,
"301-555-5555"
],
]
"Number of unique names from set method:"
,
set_unique_names
[
phonebook
]
"Number of unique names from list method:"
,
list_unique_names
[
phonebook
]
9 với mô-đun chia đôi] là khá đáng kểphonebook
=
{
"John Doe"
:
"555-555-5555"
,
"Albert Einstein"
:
"212-555-5555"
,
}
"John Doe's phone number is"
,
phonebook
[
"John Doe"
]
Mẹo
Tạo một tập lệnh nhân đôi hiệu suất của phương pháp list-
3 so với từ điển để tìm một số trong danh bạ điện thoại. Quy mô thời gian như thế nào khi kích thước của danh bạ điện thoại tăng lên?def
list_unique_names
[
phonebook
]:
unique_names
=
[]
for
name
,
phonenumber
in
phonebook
:
#
first_name
,
last_name
=
name
.
split
[
" "
,
1
]
for
unique
in
unique_names
:
#
if
unique
==
first_name
:
break
else
:
unique_names
.
append
[
first_name
]
return
len
[
unique_names
]
def
set_unique_names
[
phonebook
]:
unique_names
=
set
[]
for
name
,
phonenumber
in
phonebook
:
#
first_name
,
last_name
=
name
.
split
[
" "
,
1
]
unique_names
.
add
[
first_name
]
#
return
len
[
unique_names
]
phonebook
=
[
[
"John Doe"
,
"555-555-5555"
],
[
"Albert Einstein"
,
"212-555-5555"
],
[
"John Murphey"
,
"202-555-5555"
],
[
"Albert Rutherford"
,
"647-555-5555"
],
[
"Elaine Bodian"
,
"301-555-5555"
],
]
"Number of unique names from set method:"
,
set_unique_names
[
phonebook
]
"Number of unique names from list method:"
,
list_unique_names
[
phonebook
]
Mặt khác, nếu chúng tôi muốn trả lời câu hỏi, "Có bao nhiêu tên duy nhất trong danh bạ điện thoại của tôi?" . Nhớ lại rằng một tập hợp chỉ đơn giản là một tập hợp các khóa duy nhất—đây là thuộc tính chính xác mà chúng tôi muốn thực thi trong dữ liệu của mình. Điều này hoàn toàn trái ngược với cách tiếp cận dựa trên danh sách, trong đó thuộc tính đó cần được thực thi tách biệt khỏi cấu trúc dữ liệu bằng cách so sánh tất cả các tên với tất cả các tên khác. minh họa
Ví dụ 4-3. Tìm tên duy nhất với danh sách và tập hợp
def
list_unique_names
[
phonebook
]:
unique_names
=
[]
for
name
,
phonenumber
in
phonebook
:
#
first_name
,
last_name
=
name
.
split
[
" "
,
1
]
for
unique
in
unique_names
:
#
if
unique
==
first_name
:
break
else
:
unique_names
.
append
[
first_name
]
return
len
[
unique_names
]
def
set_unique_names
[
phonebook
]:
unique_names
=
set
[]
for
name
,
phonenumber
in
phonebook
:
#
first_name
,
last_name
=
name
.
split
[
" "
,
1
]
unique_names
.
add
[
first_name
]
#
return
len
[
unique_names
]
phonebook
=
[
[
"John Doe"
,
"555-555-5555"
],
[
"Albert Einstein"
,
"212-555-5555"
],
[
"John Murphey"
,
"202-555-5555"
],
[
"Albert Rutherford"
,
"647-555-5555"
],
[
"Elaine Bodian"
,
"301-555-5555"
],
]
"Number of unique names from set method:"
,
set_unique_names
[
phonebook
]
"Number of unique names from list method:"
,
list_unique_names
[
phonebook
]
Chúng tôi phải xem qua tất cả các mục trong danh bạ điện thoại của mình và do đó vòng lặp này có giá
0def
list_unique_names
[
phonebook
]:
unique_names
=
[]
for
name
,
phonenumber
in
phonebook
:
#
first_name
,
last_name
=
name
.
split
[
" "
,
1
]
for
unique
in
unique_names
:
#
if
unique
==
first_name
:
break
else
:
unique_names
.
append
[
first_name
]
return
len
[
unique_names
]
def
set_unique_names
[
phonebook
]:
unique_names
=
set
[]
for
name
,
phonenumber
in
phonebook
:
#
first_name
,
last_name
=
name
.
split
[
" "
,
1
]
unique_names
.
add
[
first_name
]
#
return
len
[
unique_names
]
phonebook
=
[
[
"John Doe"
,
"555-555-5555"
],
[
"Albert Einstein"
,
"212-555-5555"
],
[
"John Murphey"
,
"202-555-5555"
],
[
"Albert Rutherford"
,
"647-555-5555"
],
[
"Elaine Bodian"
,
"301-555-5555"
],
]
"Number of unique names from set method:"
,
set_unique_names
[
phonebook
]
"Number of unique names from list method:"
,
list_unique_names
[
phonebook
]
Ở đây, chúng ta phải kiểm tra tên hiện tại với tất cả các tên duy nhất mà chúng ta đã thấy. Nếu đó là một tên duy nhất mới, chúng tôi sẽ thêm nó vào danh sách các tên duy nhất của chúng tôi. Sau đó, chúng tôi tiếp tục thông qua danh sách, thực hiện bước này cho mọi mục trong danh bạ điện thoại
Đối với phương thức set, thay vì lặp lại tất cả các tên duy nhất mà chúng ta đã thấy, chúng ta chỉ cần thêm tên hiện tại vào tập hợp các tên duy nhất của mình. Vì các bộ đảm bảo tính duy nhất của các khóa mà chúng chứa, nên nếu bạn cố gắng thêm một mục đã có trong bộ, thì mục đó sẽ không được thêm vào. Hơn nữa, chi phí hoạt động này
1def
list_unique_names
[
phonebook
]:
unique_names
=
[]
for
name
,
phonenumber
in
phonebook
:
#
first_name
,
last_name
=
name
.
split
[
" "
,
1
]
for
unique
in
unique_names
:
#
if
unique
==
first_name
:
break
else
:
unique_names
.
append
[
first_name
]
return
len
[
unique_names
]
def
set_unique_names
[
phonebook
]:
unique_names
=
set
[]
for
name
,
phonenumber
in
phonebook
:
#
first_name
,
last_name
=
name
.
split
[
" "
,
1
]
unique_names
.
add
[
first_name
]
#
return
len
[
unique_names
]
phonebook
=
[
[
"John Doe"
,
"555-555-5555"
],
[
"Albert Einstein"
,
"212-555-5555"
],
[
"John Murphey"
,
"202-555-5555"
],
[
"Albert Rutherford"
,
"647-555-5555"
],
[
"Elaine Bodian"
,
"301-555-5555"
],
]
"Number of unique names from set method:"
,
set_unique_names
[
phonebook
]
"Number of unique names from list method:"
,
list_unique_names
[
phonebook
]
Vòng lặp bên trong của thuật toán danh sách lặp lại trên
81, vòng lặp này bắt đầu trống và sau đó phát triển, trong trường hợp xấu nhất, khi tất cả các tên là duy nhất, có kích thước bằngphonebook
=
{
"John Doe"
:
"555-555-5555"
,
"Albert Einstein"
:
"212-555-5555"
,
}
"John Doe's phone number is"
,
phonebook
[
"John Doe"
]
82. Điều này có thể được coi là thực hiện cho từng tên trong danh bạ điện thoại trên một danh sách không ngừng phát triển. Do đó, thuật toán hoàn chỉnh hoạt động nhưphonebook
=
{
"John Doe"
:
"555-555-5555"
,
"Albert Einstein"
:
"212-555-5555"
,
}
"John Doe's phone number is"
,
phonebook
[
"John Doe"
]
83, vì vòng lặp bên ngoài đóng góp hệ sốphonebook
=
{
"John Doe"
:
"555-555-5555"
,
"Albert Einstein"
:
"212-555-5555"
,
}
"John Doe's phone number is"
,
phonebook
[
"John Doe"
]
0, trong khi vòng lặp bên trong đóng góp hệ sốdef
list_unique_names
[
phonebook
]:
unique_names
=
[]
for
name
,
phonenumber
in
phonebook
:
#
first_name
,
last_name
=
name
.
split
[
" "
,
1
]
for
unique
in
unique_names
:
#
if
unique
==
first_name
:
break
else
:
unique_names
.
append
[
first_name
]
return
len
[
unique_names
]
def
set_unique_names
[
phonebook
]:
unique_names
=
set
[]
for
name
,
phonenumber
in
phonebook
:
#
first_name
,
last_name
=
name
.
split
[
" "
,
1
]
unique_names
.
add
[
first_name
]
#
return
len
[
unique_names
]
phonebook
=
[
[
"John Doe"
,
"555-555-5555"
],
[
"Albert Einstein"
,
"212-555-5555"
],
[
"John Murphey"
,
"202-555-5555"
],
[
"Albert Rutherford"
,
"647-555-5555"
],
[
"Elaine Bodian"
,
"301-555-5555"
],
]
"Number of unique names from set method:"
,
set_unique_names
[
phonebook
]
"Number of unique names from list method:"
,
list_unique_names
[
phonebook
]
9phonebook
=
{
"John Doe"
:
"555-555-5555"
,
"Albert Einstein"
:
"212-555-5555"
,
}
"John Doe's phone number is"
,
phonebook
[
"John Doe"
]
Mặt khác, thuật toán tập hợp không có vòng lặp bên trong; . Do đó, đóng góp không liên tục duy nhất vào độ phức tạp của thuật toán này là vòng lặp trên danh bạ điện thoại, khiến thuật toán này hoạt động trong
0def
list_unique_names
[
phonebook
]:
unique_names
=
[]
for
name
,
phonenumber
in
phonebook
:
#
first_name
,
last_name
=
name
.
split
[
" "
,
1
]
for
unique
in
unique_names
:
#
if
unique
==
first_name
:
break
else
:
unique_names
.
append
[
first_name
]
return
len
[
unique_names
]
def
set_unique_names
[
phonebook
]:
unique_names
=
set
[]
for
name
,
phonenumber
in
phonebook
:
#
first_name
,
last_name
=
name
.
split
[
" "
,
1
]
unique_names
.
add
[
first_name
]
#
return
len
[
unique_names
]
phonebook
=
[
[
"John Doe"
,
"555-555-5555"
],
[
"Albert Einstein"
,
"212-555-5555"
],
[
"John Murphey"
,
"202-555-5555"
],
[
"Albert Rutherford"
,
"647-555-5555"
],
[
"Elaine Bodian"
,
"301-555-5555"
],
]
"Number of unique names from set method:"
,
set_unique_names
[
phonebook
]
"Number of unique names from list method:"
,
list_unique_names
[
phonebook
]
Khi định thời gian cho hai thuật toán này bằng cách sử dụng một
82 với 10.000 mục nhập và 7.422 tên riêng biệt, chúng tôi thấy sự khác biệt lớn giữaphonebook
=
{
"John Doe"
:
"555-555-5555"
,
"Albert Einstein"
:
"212-555-5555"
,
}
"John Doe's phone number is"
,
phonebook
[
"John Doe"
]
0 vàdef
list_unique_names
[
phonebook
]:
unique_names
=
[]
for
name
,
phonenumber
in
phonebook
:
#
first_name
,
last_name
=
name
.
split
[
" "
,
1
]
for
unique
in
unique_names
:
#
if
unique
==
first_name
:
break
else
:
unique_names
.
append
[
first_name
]
return
len
[
unique_names
]
def
set_unique_names
[
phonebook
]:
unique_names
=
set
[]
for
name
,
phonenumber
in
phonebook
:
#
first_name
,
last_name
=
name
.
split
[
" "
,
1
]
unique_names
.
add
[
first_name
]
#
return
len
[
unique_names
]
phonebook
=
[
[
"John Doe"
,
"555-555-5555"
],
[
"Albert Einstein"
,
"212-555-5555"
],
[
"John Murphey"
,
"202-555-5555"
],
[
"Albert Rutherford"
,
"647-555-5555"
],
[
"Elaine Bodian"
,
"301-555-5555"
],
]
"Number of unique names from set method:"
,
set_unique_names
[
phonebook
]
"Number of unique names from list method:"
,
list_unique_names
[
phonebook
]
83 có thể lớn đến mức nàophonebook
=
{
"John Doe"
:
"555-555-5555"
,
"Albert Einstein"
:
"212-555-5555"
,
}
"John Doe's phone number is"
,
phonebook
[
"John Doe"
]
8phonebook
=
{
"John Doe"
:
"555-555-5555"
,
"Albert Einstein"
:
"212-555-5555"
,
}
"John Doe's phone number is"
,
phonebook
[
"John Doe"
]
Nói cách khác, thuật toán thiết lập đã cho chúng tôi tăng tốc 267 lần. Ngoài ra, khi kích thước của
82 tăng lên, tốc độ tăng lên [chúng tôi nhận được tốc độ tăng gấp 557 lần vớiphonebook
=
{
"John Doe"
:
"555-555-5555"
,
"Albert Einstein"
:
"212-555-5555"
,
}
"John Doe's phone number is"
,
phonebook
[
"John Doe"
]
82 với 100.000 mục nhập và 15.574 tên riêng]phonebook
=
{
"John Doe"
:
"555-555-5555"
,
"Albert Einstein"
:
"212-555-5555"
,
}
"John Doe's phone number is"
,
phonebook
[
"John Doe"
]
Từ điển và bộ hoạt động như thế nào?
Từ điển và bộ sử dụng bảng băm để đạt được tra cứu và chèn
1 của chúng. Hiệu quả này là kết quả của việc sử dụng a rất thông minh để xoay một phím tùy ý [i. e. , một chuỗi hoặc đối tượng] thành chỉ mục cho danh sách. Hàm băm và danh sách sau này có thể được sử dụng để xác định vị trí của bất kỳ phần dữ liệu cụ thể nào ngay lập tức mà không cần tìm kiếm. Bằng cách biến khóa của dữ liệu thành thứ có thể được sử dụng như chỉ mục danh sách, chúng tôi có thể đạt được hiệu suất tương tự như với danh sách. Ngoài ra, thay vì phải tham chiếu đến dữ liệu bằng một chỉ mục số, bản thân chỉ số này ngụ ý một số thứ tự đối với dữ liệu, chúng ta có thể tham chiếu đến nó bằng khóa tùy ý nàydef
list_unique_names
[
phonebook
]:
unique_names
=
[]
for
name
,
phonenumber
in
phonebook
:
#
first_name
,
last_name
=
name
.
split
[
" "
,
1
]
for
unique
in
unique_names
:
#
if
unique
==
first_name
:
break
else
:
unique_names
.
append
[
first_name
]
return
len
[
unique_names
]
def
set_unique_names
[
phonebook
]:
unique_names
=
set
[]
for
name
,
phonenumber
in
phonebook
:
#
first_name
,
last_name
=
name
.
split
[
" "
,
1
]
unique_names
.
add
[
first_name
]
#
return
len
[
unique_names
]
phonebook
=
[
[
"John Doe"
,
"555-555-5555"
],
[
"Albert Einstein"
,
"212-555-5555"
],
[
"John Murphey"
,
"202-555-5555"
],
[
"Albert Rutherford"
,
"647-555-5555"
],
[
"Elaine Bodian"
,
"301-555-5555"
],
]
"Number of unique names from set method:"
,
set_unique_names
[
phonebook
]
"Number of unique names from list method:"
,
list_unique_names
[
phonebook
]
Để tạo bảng băm từ đầu, chúng tôi bắt đầu với một số bộ nhớ được phân bổ, tương tự như những gì chúng tôi đã bắt đầu với mảng. Đối với một mảng, nếu chúng ta muốn chèn dữ liệu, chúng ta chỉ cần tìm nhóm nhỏ nhất chưa sử dụng và chèn dữ liệu của mình vào đó [và thay đổi kích thước nếu cần]. Đối với các bảng băm, trước tiên chúng ta phải tìm ra vị trí của dữ liệu trong đoạn bộ nhớ liền kề này
Vị trí của dữ liệu mới phụ thuộc vào hai thuộc tính của dữ liệu chúng tôi đang chèn. giá trị băm của khóa và cách so sánh giá trị với các đối tượng khác. Điều này là do khi chúng ta chèn dữ liệu, khóa đầu tiên được băm và ẩn để nó biến thành một chỉ mục hiệu quả trong một mảng. [] Mặt nạ đảm bảo rằng giá trị băm, có thể lấy giá trị của bất kỳ số nguyên nào, phù hợp với số lượng nhóm được phân bổ. Vì vậy, nếu chúng tôi đã phân bổ 8 khối bộ nhớ và giá trị băm của chúng tôi là
35, thì chúng tôi coi bộ chứa ở chỉ mụcphonebook
=
{
"John Doe"
:
"555-555-5555"
,
"Albert Einstein"
:
"212-555-5555"
,
}
"John Doe's phone number is"
,
phonebook
[
"John Doe"
]
36. Tuy nhiên, nếu từ điển của chúng tôi đã phát triển để yêu cầu 512 khối bộ nhớ, thì mặt nạ sẽ trở thànhphonebook
=
{
"John Doe"
:
"555-555-5555"
,
"Albert Einstein"
:
"212-555-5555"
,
}
"John Doe's phone number is"
,
phonebook
[
"John Doe"
]
37 [và trong trường hợp này, chúng tôi sẽ xem xét nhóm ở chỉ mụcphonebook
=
{
"John Doe"
:
"555-555-5555"
,
"Albert Einstein"
:
"212-555-5555"
,
}
"John Doe's phone number is"
,
phonebook
[
"John Doe"
]
38]. Bây giờ chúng ta phải kiểm tra xem thùng này đã được sử dụng chưa. Nếu nó trống, chúng ta có thể chèn khóa và giá trị vào khối bộ nhớ này. Chúng tôi lưu trữ khóa để có thể đảm bảo rằng chúng tôi đang truy xuất đúng giá trị khi tra cứu. Nếu nó đang được sử dụng và giá trị của bộ chứa bằng với giá trị chúng tôi muốn chèn [so sánh được thực hiện vớiphonebook
=
{
"John Doe"
:
"555-555-5555"
,
"Albert Einstein"
:
"212-555-5555"
,
}
"John Doe's phone number is"
,
phonebook
[
"John Doe"
]
39 tích hợp sẵn], thì cặp khóa/giá trị đã có trong bảng băm và chúng tôi có thể trả về. Tuy nhiên, nếu các giá trị không khớp nhau, thì chúng ta phải tìm một nơi mới để đặt dữ liệuphonebook
=
{
"John Doe"
:
"555-555-5555"
,
"Albert Einstein"
:
"212-555-5555"
,
}
"John Doe's phone number is"
,
phonebook
[
"John Doe"
]
Để tìm chỉ mục mới, chúng tôi tính toán chỉ mục mới bằng cách sử dụng một hàm tuyến tính đơn giản, một phương pháp được gọi là thăm dò. Cơ chế thăm dò của Python thêm phần đóng góp từ các bit bậc cao hơn của hàm băm ban đầu [hãy nhớ rằng đối với bảng có độ dài 8, chúng tôi chỉ xem xét 3 bit cuối cùng của hàm băm cho chỉ mục ban đầu, thông qua việc sử dụng giá trị mặt nạ là
80]. Việc sử dụng các bit bậc cao hơn này sẽ mang lại cho mỗi hàm băm một chuỗi các hàm băm tiếp theo có thể khác nhau, giúp tránh xung đột trong tương lai. Có rất nhiều sự tự do khi chọn thuật toán để tạo một chỉ mục mới; . Dữ liệu được phân phối tốt như thế nào trong bảng băm được gọi là “hệ số tải” và có liên quan đến hàm băm. Mã giả minh họa cách tính chỉ số băm được sử dụng trong CPython 2. 7phonebook
=
{
"John Doe"
:
"555-555-5555"
,
"Albert Einstein"
:
"212-555-5555"
,
}
"John Doe's phone number is"
,
phonebook
[
"John Doe"
]
Ví dụ 4-4. Trình tự tra cứu từ điển
3phonebook
=
{
"John Doe"
:
"555-555-5555"
,
"Albert Einstein"
:
"212-555-5555"
,
}
"John Doe's phone number is"
,
phonebook
[
"John Doe"
]
81 trả về một số nguyên, trong khi mã C thực tế trong CPython sử dụng một số nguyên không dấu. Do đó, mã giả này không sao chép 100% hành vi trong CPython;phonebook
=
{
"John Doe"
:
"555-555-5555"
,
"Albert Einstein"
:
"212-555-5555"
,
}
"John Doe's phone number is"
,
phonebook
[
"John Doe"
]
Thăm dò này là một sửa đổi của phương pháp ngây thơ của “thăm dò tuyến tính. ” Trong thăm dò tuyến tính, chúng tôi chỉ đơn giản đưa ra các giá trị
82, trong đóphonebook
=
{
"John Doe"
:
"555-555-5555"
,
"Albert Einstein"
:
"212-555-5555"
,
}
"John Doe's phone number is"
,
phonebook
[
"John Doe"
]
83 được khởi tạo thành giá trị băm của khóa và giá trị '5` không quan trọng đối với cuộc thảo luận hiện tại. [] Một điều quan trọng cần lưu ý là thăm dò tuyến tính chỉ xử lý vài byte cuối cùng của hàm băm và bỏ qua phần còn lại [i. e. , đối với một từ điển có tám phần tử, chúng tôi chỉ xem xét 3 bit cuối cùng vì tại thời điểm đó, mặt nạ làphonebook
=
{
"John Doe"
:
"555-555-5555"
,
"Albert Einstein"
:
"212-555-5555"
,
}
"John Doe's phone number is"
,
phonebook
[
"John Doe"
]
84]. Điều này có nghĩa là nếu băm hai mục cho ba chữ số nhị phân cuối cùng giống nhau, chúng ta sẽ không chỉ xảy ra xung đột mà chuỗi các chỉ số được thăm dò sẽ giống nhau. Lược đồ nhiễu loạn mà Python sử dụng sẽ bắt đầu xem xét nhiều bit hơn từ hàm băm của các mục để giải quyết vấn đề nàyphonebook
=
{
"John Doe"
:
"555-555-5555"
,
"Albert Einstein"
:
"212-555-5555"
,
}
"John Doe's phone number is"
,
phonebook
[
"John Doe"
]
Một quy trình tương tự được thực hiện khi chúng tôi đang thực hiện tra cứu trên một khóa cụ thể. khóa đã cho được chuyển đổi thành một chỉ mục và chỉ mục đó được kiểm tra. Nếu khóa trong chỉ mục đó khớp [hãy nhớ rằng chúng tôi cũng lưu trữ khóa gốc khi thực hiện thao tác chèn], thì chúng tôi có thể trả về giá trị đó. Nếu không, chúng tôi tiếp tục tạo các chỉ mục mới bằng cách sử dụng cùng một sơ đồ, cho đến khi chúng tôi tìm thấy dữ liệu hoặc chạm vào một thùng trống. Nếu chúng ta nhấn vào một thùng rỗng, chúng ta có thể kết luận rằng dữ liệu không tồn tại trong bảng
minh họa quá trình thêm một số dữ liệu vào bảng băm. Ở đây, chúng tôi đã chọn tạo một hàm băm chỉ sử dụng chữ cái đầu tiên của đầu vào. Chúng tôi thực hiện điều này bằng cách sử dụng hàm
85 của Python trên chữ cái đầu tiên của đầu vào để lấy biểu diễn số nguyên của chữ cái đó [nhớ lại các hàm băm phải trả về số nguyên]. Như chúng ta sẽ thấy trong phần , Python cung cấp các hàm băm cho hầu hết các loại của nó, vì vậy thông thường bạn sẽ không phải tự cung cấp một hàm băm trừ những trường hợp cực đoanphonebook
=
{
"John Doe"
:
"555-555-5555"
,
"Albert Einstein"
:
"212-555-5555"
,
}
"John Doe's phone number is"
,
phonebook
[
"John Doe"
]
Hình 4-1. Bảng băm kết quả từ việc chèn có va chạm
Việc chèn khóa “Barcelona” gây ra xung đột và một chỉ mục mới được tính toán bằng lược đồ trong. Từ điển này cũng có thể được tạo bằng Python bằng cách sử dụng mã trong
Ví dụ 4-5. Hàm băm tùy chỉnh
8phonebook
=
{
"John Doe"
:
"555-555-5555"
,
"Albert Einstein"
:
"212-555-5555"
,
}
"John Doe's phone number is"
,
phonebook
[
"John Doe"
]
Trong trường hợp này, “Barcelona” và “Rome” gây ra xung đột hàm băm [ hiển thị kết quả của việc chèn này]. Chúng tôi thấy điều này bởi vì đối với một từ điển có bốn phần tử, chúng tôi có giá trị mặt nạ là
86. Do đó, “Barcelona” sẽ cố gắng sử dụng chỉ sốphonebook
=
{
"John Doe"
:
"555-555-5555"
,
"Albert Einstein"
:
"212-555-5555"
,
}
"John Doe's phone number is"
,
phonebook
[
"John Doe"
]
87. Tương tự, “Rome” sẽ cố gắng sử dụng chỉ sốphonebook
=
{
"John Doe"
:
"555-555-5555"
,
"Albert Einstein"
:
"212-555-5555"
,
}
"John Doe's phone number is"
,
phonebook
[
"John Doe"
]
88phonebook
=
{
"John Doe"
:
"555-555-5555"
,
"Albert Einstein"
:
"212-555-5555"
,
}
"John Doe's phone number is"
,
phonebook
[
"John Doe"
]
89phonebook
=
{
"John Doe"
:
"555-555-5555"
,
"Albert Einstein"
:
"212-555-5555"
,
}
"John Doe's phone number is"
,
phonebook
[
"John Doe"
]
Mẹo
Làm việc thông qua các vấn đề sau. Một cuộc thảo luận về va chạm băm sau
- Tìm phần tử—Sử dụng từ điển được tạo trong , tra cứu trên khóa “Johannesburg” sẽ như thế nào?
- Xóa một phần tử—Sử dụng từ điển được tạo trong , bạn sẽ xử lý việc xóa khóa “Rome” như thế nào?
- Xung đột băm—Xem xét từ điển được tạo trong , bạn có thể mong đợi bao nhiêu xung đột băm nếu 500 thành phố, với tên tất cả bắt đầu bằng một chữ cái viết hoa, được thêm vào bảng băm?
Đối với 500 thành phố, sẽ có khoảng 474 phần tử từ điển xung đột với giá trị trước đó [500–26], với mỗi hàm băm có 500/26 = 19. 2 thành phố liên kết với nó. Đối với 1.000 thành phố, 974 phần tử sẽ xung đột và mỗi hàm băm sẽ có 1.000/26 = 38. 4 thành phố liên kết với nó. Điều này là do hàm băm chỉ đơn giản dựa trên giá trị số của chữ cái đầu tiên, chỉ có thể nhận giá trị từ
00–def
list_unique_names
[
phonebook
]:
unique_names
=
[]
for
name
,
phonenumber
in
phonebook
:
#
first_name
,
last_name
=
name
.
split
[
" "
,
1
]
for
unique
in
unique_names
:
#
if
unique
==
first_name
:
break
else
:
unique_names
.
append
[
first_name
]
return
len
[
unique_names
]
def
set_unique_names
[
phonebook
]:
unique_names
=
set
[]
for
name
,
phonenumber
in
phonebook
:
#
first_name
,
last_name
=
name
.
split
[
" "
,
1
]
unique_names
.
add
[
first_name
]
#
return
len
[
unique_names
]
phonebook
=
[
[
"John Doe"
,
"555-555-5555"
],
[
"Albert Einstein"
,
"212-555-5555"
],
[
"John Murphey"
,
"202-555-5555"
],
[
"Albert Rutherford"
,
"647-555-5555"
],
[
"Elaine Bodian"
,
"301-555-5555"
],
]
"Number of unique names from set method:"
,
set_unique_names
[
phonebook
]
"Number of unique names from list method:"
,
list_unique_names
[
phonebook
]
01, chỉ cho phép 26 giá trị hàm băm độc lập. Điều này có nghĩa là một lần tra cứu trong bảng này có thể yêu cầu tới 38 lần tra cứu tiếp theo để tìm ra giá trị chính xác. Để khắc phục điều này, chúng ta phải tăng số lượng giá trị băm có thể có bằng cách xem xét các khía cạnh khác của thành phố trong hàm băm. Hàm băm mặc định trên một chuỗi xem xét mọi ký tự để tối đa hóa số lượng giá trị có thể. Xem để giải thích thêmdef
list_unique_names
[
phonebook
]:
unique_names
=
[]
for
name
,
phonenumber
in
phonebook
:
#
first_name
,
last_name
=
name
.
split
[
" "
,
1
]
for
unique
in
unique_names
:
#
if
unique
==
first_name
:
break
else
:
unique_names
.
append
[
first_name
]
return
len
[
unique_names
]
def
set_unique_names
[
phonebook
]:
unique_names
=
set
[]
for
name
,
phonenumber
in
phonebook
:
#
first_name
,
last_name
=
name
.
split
[
" "
,
1
]
unique_names
.
add
[
first_name
]
#
return
len
[
unique_names
]
phonebook
=
[
[
"John Doe"
,
"555-555-5555"
],
[
"Albert Einstein"
,
"212-555-5555"
],
[
"John Murphey"
,
"202-555-5555"
],
[
"Albert Rutherford"
,
"647-555-5555"
],
[
"Elaine Bodian"
,
"301-555-5555"
],
]
"Number of unique names from set method:"
,
set_unique_names
[
phonebook
]
"Number of unique names from list method:"
,
list_unique_names
[
phonebook
]
Khi một giá trị bị xóa khỏi bảng băm, chúng ta không thể chỉ ghi một
02 vào bộ nhớ đó. Điều này là do chúng tôi đã sử dụngdef
list_unique_names
[
phonebook
]:
unique_names
=
[]
for
name
,
phonenumber
in
phonebook
:
#
first_name
,
last_name
=
name
.
split
[
" "
,
1
]
for
unique
in
unique_names
:
#
if
unique
==
first_name
:
break
else
:
unique_names
.
append
[
first_name
]
return
len
[
unique_names
]
def
set_unique_names
[
phonebook
]:
unique_names
=
set
[]
for
name
,
phonenumber
in
phonebook
:
#
first_name
,
last_name
=
name
.
split
[
" "
,
1
]
unique_names
.
add
[
first_name
]
#
return
len
[
unique_names
]
phonebook
=
[
[
"John Doe"
,
"555-555-5555"
],
[
"Albert Einstein"
,
"212-555-5555"
],
[
"John Murphey"
,
"202-555-5555"
],
[
"Albert Rutherford"
,
"647-555-5555"
],
[
"Elaine Bodian"
,
"301-555-5555"
],
]
"Number of unique names from set method:"
,
set_unique_names
[
phonebook
]
"Number of unique names from list method:"
,
list_unique_names
[
phonebook
]
02 làm giá trị trọng điểm trong khi thăm dò xung đột hàm băm. Do đó, chúng ta phải viết một giá trị đặc biệt biểu thị rằng nhóm trống, nhưng vẫn có thể có các giá trị sau nó để xem xét khi giải quyết xung đột hàm băm. Các vị trí trống này có thể được ghi vào trong tương lai và bị xóa khi bảng băm được thay đổi kích thướcdef
list_unique_names
[
phonebook
]:
unique_names
=
[]
for
name
,
phonenumber
in
phonebook
:
#
first_name
,
last_name
=
name
.
split
[
" "
,
1
]
for
unique
in
unique_names
:
#
if
unique
==
first_name
:
break
else
:
unique_names
.
append
[
first_name
]
return
len
[
unique_names
]
def
set_unique_names
[
phonebook
]:
unique_names
=
set
[]
for
name
,
phonenumber
in
phonebook
:
#
first_name
,
last_name
=
name
.
split
[
" "
,
1
]
unique_names
.
add
[
first_name
]
#
return
len
[
unique_names
]
phonebook
=
[
[
"John Doe"
,
"555-555-5555"
],
[
"Albert Einstein"
,
"212-555-5555"
],
[
"John Murphey"
,
"202-555-5555"
],
[
"Albert Rutherford"
,
"647-555-5555"
],
[
"Elaine Bodian"
,
"301-555-5555"
],
]
"Number of unique names from set method:"
,
set_unique_names
[
phonebook
]
"Number of unique names from list method:"
,
list_unique_names
[
phonebook
]
Khi nhiều mục được chèn vào bảng băm, bảng phải được thay đổi kích thước để phù hợp với nó. Có thể chỉ ra rằng một bảng không đầy quá hai phần ba sẽ tiết kiệm không gian tối ưu trong khi vẫn có giới hạn tốt về số lần va chạm mong đợi. Do đó, khi một bảng đạt đến điểm tới hạn này, nó sẽ phát triển. Để làm điều này, một bảng lớn hơn được phân bổ [i. e. , nhiều bộ chứa hơn trong bộ nhớ được dành riêng], mặt nạ được điều chỉnh để phù hợp với bảng mới và tất cả các thành phần của bảng cũ được chèn lại vào bảng mới. Điều này yêu cầu tính toán lại các chỉ số, vì mặt nạ đã thay đổi sẽ thay đổi chỉ mục kết quả. Do đó, việc thay đổi kích thước các bảng băm lớn có thể khá tốn kém. Tuy nhiên, vì chúng tôi chỉ thực hiện thao tác thay đổi kích thước này khi bảng quá nhỏ, trái ngược với mỗi lần chèn, nên chi phí phân bổ của một lần chèn vẫn là
1def
list_unique_names
[
phonebook
]:
unique_names
=
[]
for
name
,
phonenumber
in
phonebook
:
#
first_name
,
last_name
=
name
.
split
[
" "
,
1
]
for
unique
in
unique_names
:
#
if
unique
==
first_name
:
break
else
:
unique_names
.
append
[
first_name
]
return
len
[
unique_names
]
def
set_unique_names
[
phonebook
]:
unique_names
=
set
[]
for
name
,
phonenumber
in
phonebook
:
#
first_name
,
last_name
=
name
.
split
[
" "
,
1
]
unique_names
.
add
[
first_name
]
#
return
len
[
unique_names
]
phonebook
=
[
[
"John Doe"
,
"555-555-5555"
],
[
"Albert Einstein"
,
"212-555-5555"
],
[
"John Murphey"
,
"202-555-5555"
],
[
"Albert Rutherford"
,
"647-555-5555"
],
[
"Elaine Bodian"
,
"301-555-5555"
],
]
"Number of unique names from set method:"
,
set_unique_names
[
phonebook
]
"Number of unique names from list method:"
,
list_unique_names
[
phonebook
]
Theo mặc định, kích thước nhỏ nhất của từ điển hoặc bộ là 8 [nghĩa là nếu bạn chỉ lưu trữ ba giá trị, Python vẫn sẽ phân bổ tám phần tử]. Khi thay đổi kích thước, số lượng nhóm tăng gấp 4 lần cho đến khi chúng tôi đạt 50.000 phần tử, sau đó kích thước tăng gấp 2 lần. Điều này mang lại các kích thước có thể sau đây
0def
list_unique_names
[
phonebook
]:
unique_names
=
[]
for
name
,
phonenumber
in
phonebook
:
#
first_name
,
last_name
=
name
.
split
[
" "
,
1
]
for
unique
in
unique_names
:
#
if
unique
==
first_name
:
break
else
:
unique_names
.
append
[
first_name
]
return
len
[
unique_names
]
def
set_unique_names
[
phonebook
]:
unique_names
=
set
[]
for
name
,
phonenumber
in
phonebook
:
#
first_name
,
last_name
=
name
.
split
[
" "
,
1
]
unique_names
.
add
[
first_name
]
#
return
len
[
unique_names
]
phonebook
=
[
[
"John Doe"
,
"555-555-5555"
],
[
"Albert Einstein"
,
"212-555-5555"
],
[
"John Murphey"
,
"202-555-5555"
],
[
"Albert Rutherford"
,
"647-555-5555"
],
[
"Elaine Bodian"
,
"301-555-5555"
],
]
"Number of unique names from set method:"
,
set_unique_names
[
phonebook
]
"Number of unique names from list method:"
,
list_unique_names
[
phonebook
]
Điều quan trọng cần lưu ý là việc thay đổi kích thước có thể xảy ra để làm cho bảng băm lớn hơn hoặc nhỏ hơn. Nghĩa là, nếu đủ nhiều phần tử của bảng băm bị xóa, bảng có thể được thu nhỏ kích thước. Tuy nhiên, thay đổi kích thước chỉ xảy ra trong quá trình chèn
Hàm băm và Entropy
Các đối tượng trong Python nói chung có thể băm được, vì chúng đã có sẵn các hàm
6 vàphonebook
=
{
"John Doe"
:
"555-555-5555"
,
"Albert Einstein"
:
"212-555-5555"
,
}
"John Doe's phone number is"
,
phonebook
[
"John Doe"
]
8 được liên kết với chúng. Đối với các loại số [phonebook
=
{
"John Doe"
:
"555-555-5555"
,
"Albert Einstein"
:
"212-555-5555"
,
}
"John Doe's phone number is"
,
phonebook
[
"John Doe"
]
07 vàdef
list_unique_names
[
phonebook
]:
unique_names
=
[]
for
name
,
phonenumber
in
phonebook
:
#
first_name
,
last_name
=
name
.
split
[
" "
,
1
]
for
unique
in
unique_names
:
#
if
unique
==
first_name
:
break
else
:
unique_names
.
append
[
first_name
]
return
len
[
unique_names
]
def
set_unique_names
[
phonebook
]:
unique_names
=
set
[]
for
name
,
phonenumber
in
phonebook
:
#
first_name
,
last_name
=
name
.
split
[
" "
,
1
]
unique_names
.
add
[
first_name
]
#
return
len
[
unique_names
]
phonebook
=
[
[
"John Doe"
,
"555-555-5555"
],
[
"Albert Einstein"
,
"212-555-5555"
],
[
"John Murphey"
,
"202-555-5555"
],
[
"Albert Rutherford"
,
"647-555-5555"
],
[
"Elaine Bodian"
,
"301-555-5555"
],
]
"Number of unique names from set method:"
,
set_unique_names
[
phonebook
]
"Number of unique names from list method:"
,
list_unique_names
[
phonebook
]
08], hàm băm chỉ đơn giản dựa trên giá trị bit của số mà chúng đại diện. Bộ dữ liệu và chuỗi có giá trị băm dựa trên nội dung của chúng. Mặt khác, danh sách không hỗ trợ băm vì giá trị của chúng có thể thay đổi. Vì các giá trị của danh sách có thể thay đổi, nên hàm băm đại diện cho danh sách cũng có thể thay đổi vị trí tương đối của khóa đó trong bảng băm. []def
list_unique_names
[
phonebook
]:
unique_names
=
[]
for
name
,
phonenumber
in
phonebook
:
#
first_name
,
last_name
=
name
.
split
[
" "
,
1
]
for
unique
in
unique_names
:
#
if
unique
==
first_name
:
break
else
:
unique_names
.
append
[
first_name
]
return
len
[
unique_names
]
def
set_unique_names
[
phonebook
]:
unique_names
=
set
[]
for
name
,
phonenumber
in
phonebook
:
#
first_name
,
last_name
=
name
.
split
[
" "
,
1
]
unique_names
.
add
[
first_name
]
#
return
len
[
unique_names
]
phonebook
=
[
[
"John Doe"
,
"555-555-5555"
],
[
"Albert Einstein"
,
"212-555-5555"
],
[
"John Murphey"
,
"202-555-5555"
],
[
"Albert Rutherford"
,
"647-555-5555"
],
[
"Elaine Bodian"
,
"301-555-5555"
],
]
"Number of unique names from set method:"
,
set_unique_names
[
phonebook
]
"Number of unique names from list method:"
,
list_unique_names
[
phonebook
]
Các lớp do người dùng định nghĩa cũng có các hàm so sánh và hàm băm mặc định. Hàm
6 mặc định chỉ trả về vị trí của đối tượng trong bộ nhớ như được cung cấp bởi hàmphonebook
=
{
"John Doe"
:
"555-555-5555"
,
"Albert Einstein"
:
"212-555-5555"
,
}
"John Doe's phone number is"
,
phonebook
[
"John Doe"
]
10 tích hợp. Tương tự, toán tửdef
list_unique_names
[
phonebook
]:
unique_names
=
[]
for
name
,
phonenumber
in
phonebook
:
#
first_name
,
last_name
=
name
.
split
[
" "
,
1
]
for
unique
in
unique_names
:
#
if
unique
==
first_name
:
break
else
:
unique_names
.
append
[
first_name
]
return
len
[
unique_names
]
def
set_unique_names
[
phonebook
]:
unique_names
=
set
[]
for
name
,
phonenumber
in
phonebook
:
#
first_name
,
last_name
=
name
.
split
[
" "
,
1
]
unique_names
.
add
[
first_name
]
#
return
len
[
unique_names
]
phonebook
=
[
[
"John Doe"
,
"555-555-5555"
],
[
"Albert Einstein"
,
"212-555-5555"
],
[
"John Murphey"
,
"202-555-5555"
],
[
"Albert Rutherford"
,
"647-555-5555"
],
[
"Elaine Bodian"
,
"301-555-5555"
],
]
"Number of unique names from set method:"
,
set_unique_names
[
phonebook
]
"Number of unique names from list method:"
,
list_unique_names
[
phonebook
]
8 so sánh giá trị số của vị trí của đối tượng trong bộ nhớphonebook
=
{
"John Doe"
:
"555-555-5555"
,
"Albert Einstein"
:
"212-555-5555"
,
}
"John Doe's phone number is"
,
phonebook
[
"John Doe"
]
Điều này thường được chấp nhận, vì hai thể hiện của một lớp nói chung là khác nhau và không xung đột trong một bảng băm. Tuy nhiên, trong một số trường hợp, chúng tôi muốn sử dụng các đối tượng
12 hoặcdef
list_unique_names
[
phonebook
]:
unique_names
=
[]
for
name
,
phonenumber
in
phonebook
:
#
first_name
,
last_name
=
name
.
split
[
" "
,
1
]
for
unique
in
unique_names
:
#
if
unique
==
first_name
:
break
else
:
unique_names
.
append
[
first_name
]
return
len
[
unique_names
]
def
set_unique_names
[
phonebook
]:
unique_names
=
set
[]
for
name
,
phonenumber
in
phonebook
:
#
first_name
,
last_name
=
name
.
split
[
" "
,
1
]
unique_names
.
add
[
first_name
]
#
return
len
[
unique_names
]
phonebook
=
[
[
"John Doe"
,
"555-555-5555"
],
[
"Albert Einstein"
,
"212-555-5555"
],
[
"John Murphey"
,
"202-555-5555"
],
[
"Albert Rutherford"
,
"647-555-5555"
],
[
"Elaine Bodian"
,
"301-555-5555"
],
]
"Number of unique names from set method:"
,
set_unique_names
[
phonebook
]
"Number of unique names from list method:"
,
list_unique_names
[
phonebook
]
13 để phân biệt giữa các mục. Lấy định nghĩa lớp saudef
list_unique_names
[
phonebook
]:
unique_names
=
[]
for
name
,
phonenumber
in
phonebook
:
#
first_name
,
last_name
=
name
.
split
[
" "
,
1
]
for
unique
in
unique_names
:
#
if
unique
==
first_name
:
break
else
:
unique_names
.
append
[
first_name
]
return
len
[
unique_names
]
def
set_unique_names
[
phonebook
]:
unique_names
=
set
[]
for
name
,
phonenumber
in
phonebook
:
#
first_name
,
last_name
=
name
.
split
[
" "
,
1
]
unique_names
.
add
[
first_name
]
#
return
len
[
unique_names
]
phonebook
=
[
[
"John Doe"
,
"555-555-5555"
],
[
"Albert Einstein"
,
"212-555-5555"
],
[
"John Murphey"
,
"202-555-5555"
],
[
"Albert Rutherford"
,
"647-555-5555"
],
[
"Elaine Bodian"
,
"301-555-5555"
],
]
"Number of unique names from set method:"
,
set_unique_names
[
phonebook
]
"Number of unique names from list method:"
,
list_unique_names
[
phonebook
]
1def
list_unique_names
[
phonebook
]:
unique_names
=
[]
for
name
,
phonenumber
in
phonebook
:
#
first_name
,
last_name
=
name
.
split
[
" "
,
1
]
for
unique
in
unique_names
:
#
if
unique
==
first_name
:
break
else
:
unique_names
.
append
[
first_name
]
return
len
[
unique_names
]
def
set_unique_names
[
phonebook
]:
unique_names
=
set
[]
for
name
,
phonenumber
in
phonebook
:
#
first_name
,
last_name
=
name
.
split
[
" "
,
1
]
unique_names
.
add
[
first_name
]
#
return
len
[
unique_names
]
phonebook
=
[
[
"John Doe"
,
"555-555-5555"
],
[
"Albert Einstein"
,
"212-555-5555"
],
[
"John Murphey"
,
"202-555-5555"
],
[
"Albert Rutherford"
,
"647-555-5555"
],
[
"Elaine Bodian"
,
"301-555-5555"
],
]
"Number of unique names from set method:"
,
set_unique_names
[
phonebook
]
"Number of unique names from list method:"
,
list_unique_names
[
phonebook
]
Nếu chúng ta khởi tạo nhiều đối tượng
14 có cùng giá trị chodef
list_unique_names
[
phonebook
]:
unique_names
=
[]
for
name
,
phonenumber
in
phonebook
:
#
first_name
,
last_name
=
name
.
split
[
" "
,
1
]
for
unique
in
unique_names
:
#
if
unique
==
first_name
:
break
else
:
unique_names
.
append
[
first_name
]
return
len
[
unique_names
]
def
set_unique_names
[
phonebook
]:
unique_names
=
set
[]
for
name
,
phonenumber
in
phonebook
:
#
first_name
,
last_name
=
name
.
split
[
" "
,
1
]
unique_names
.
add
[
first_name
]
#
return
len
[
unique_names
]
phonebook
=
[
[
"John Doe"
,
"555-555-5555"
],
[
"Albert Einstein"
,
"212-555-5555"
],
[
"John Murphey"
,
"202-555-5555"
],
[
"Albert Rutherford"
,
"647-555-5555"
],
[
"Elaine Bodian"
,
"301-555-5555"
],
]
"Number of unique names from set method:"
,
set_unique_names
[
phonebook
]
"Number of unique names from list method:"
,
list_unique_names
[
phonebook
]
15 vàdef
list_unique_names
[
phonebook
]:
unique_names
=
[]
for
name
,
phonenumber
in
phonebook
:
#
first_name
,
last_name
=
name
.
split
[
" "
,
1
]
for
unique
in
unique_names
:
#
if
unique
==
first_name
:
break
else
:
unique_names
.
append
[
first_name
]
return
len
[
unique_names
]
def
set_unique_names
[
phonebook
]:
unique_names
=
set
[]
for
name
,
phonenumber
in
phonebook
:
#
first_name
,
last_name
=
name
.
split
[
" "
,
1
]
unique_names
.
add
[
first_name
]
#
return
len
[
unique_names
]
phonebook
=
[
[
"John Doe"
,
"555-555-5555"
],
[
"Albert Einstein"
,
"212-555-5555"
],
[
"John Murphey"
,
"202-555-5555"
],
[
"Albert Rutherford"
,
"647-555-5555"
],
[
"Elaine Bodian"
,
"301-555-5555"
],
]
"Number of unique names from set method:"
,
set_unique_names
[
phonebook
]
"Number of unique names from list method:"
,
list_unique_names
[
phonebook
]
16, thì tất cả chúng sẽ là các đối tượng độc lập trong bộ nhớ và do đó có các vị trí khác nhau trong bộ nhớ, điều này sẽ cung cấp cho chúng tất cả các giá trị băm khác nhau. Điều này có nghĩa là đặt tất cả chúng vào mộtdef
list_unique_names
[
phonebook
]:
unique_names
=
[]
for
name
,
phonenumber
in
phonebook
:
#
first_name
,
last_name
=
name
.
split
[
" "
,
1
]
for
unique
in
unique_names
:
#
if
unique
==
first_name
:
break
else
:
unique_names
.
append
[
first_name
]
return
len
[
unique_names
]
def
set_unique_names
[
phonebook
]:
unique_names
=
set
[]
for
name
,
phonenumber
in
phonebook
:
#
first_name
,
last_name
=
name
.
split
[
" "
,
1
]
unique_names
.
add
[
first_name
]
#
return
len
[
unique_names
]
phonebook
=
[
[
"John Doe"
,
"555-555-5555"
],
[
"Albert Einstein"
,
"212-555-5555"
],
[
"John Murphey"
,
"202-555-5555"
],
[
"Albert Rutherford"
,
"647-555-5555"
],
[
"Elaine Bodian"
,
"301-555-5555"
],
]
"Number of unique names from set method:"
,
set_unique_names
[
phonebook
]
"Number of unique names from list method:"
,
list_unique_names
[
phonebook
]
12 sẽ dẫn đến việc tất cả chúng đều có các mục riêng lẻdef
list_unique_names
[
phonebook
]:
unique_names
=
[]
for
name
,
phonenumber
in
phonebook
:
#
first_name
,
last_name
=
name
.
split
[
" "
,
1
]
for
unique
in
unique_names
:
#
if
unique
==
first_name
:
break
else
:
unique_names
.
append
[
first_name
]
return
len
[
unique_names
]
def
set_unique_names
[
phonebook
]:
unique_names
=
set
[]
for
name
,
phonenumber
in
phonebook
:
#
first_name
,
last_name
=
name
.
split
[
" "
,
1
]
unique_names
.
add
[
first_name
]
#
return
len
[
unique_names
]
phonebook
=
[
[
"John Doe"
,
"555-555-5555"
],
[
"Albert Einstein"
,
"212-555-5555"
],
[
"John Murphey"
,
"202-555-5555"
],
[
"Albert Rutherford"
,
"647-555-5555"
],
[
"Elaine Bodian"
,
"301-555-5555"
],
]
"Number of unique names from set method:"
,
set_unique_names
[
phonebook
]
"Number of unique names from list method:"
,
list_unique_names
[
phonebook
]
0def
find_phonenumber
[
phonebook
,
name
]:
for
n
,
p
in
phonebook
:
if
n
==
name
:
return
p
return
None
phonebook
=
[
[
"John Doe"
,
"555-555-5555"
],
[
"Albert Einstein"
,
"212-555-5555"
],
]
"John Doe's phone number is"
,
find_phonenumber
[
phonebook
,
"John Doe"
]
Chúng ta có thể khắc phục điều này bằng cách tạo một hàm băm tùy chỉnh dựa trên nội dung thực tế của đối tượng thay vì vị trí của đối tượng trong bộ nhớ. Hàm băm có thể tùy ý miễn là nó luôn cho cùng một kết quả cho cùng một đối tượng. [Cũng có những cân nhắc liên quan đến entropy của hàm băm, mà chúng ta sẽ thảo luận sau. ] Việc định nghĩa lại lớp
14 sau đây sẽ mang lại kết quả mà chúng ta mong đợidef
list_unique_names
[
phonebook
]:
unique_names
=
[]
for
name
,
phonenumber
in
phonebook
:
#
first_name
,
last_name
=
name
.
split
[
" "
,
1
]
for
unique
in
unique_names
:
#
if
unique
==
first_name
:
break
else
:
unique_names
.
append
[
first_name
]
return
len
[
unique_names
]
def
set_unique_names
[
phonebook
]:
unique_names
=
set
[]
for
name
,
phonenumber
in
phonebook
:
#
first_name
,
last_name
=
name
.
split
[
" "
,
1
]
unique_names
.
add
[
first_name
]
#
return
len
[
unique_names
]
phonebook
=
[
[
"John Doe"
,
"555-555-5555"
],
[
"Albert Einstein"
,
"212-555-5555"
],
[
"John Murphey"
,
"202-555-5555"
],
[
"Albert Rutherford"
,
"647-555-5555"
],
[
"Elaine Bodian"
,
"301-555-5555"
],
]
"Number of unique names from set method:"
,
set_unique_names
[
phonebook
]
"Number of unique names from list method:"
,
list_unique_names
[
phonebook
]
2def
find_phonenumber
[
phonebook
,
name
]:
for
n
,
p
in
phonebook
:
if
n
==
name
:
return
p
return
None
phonebook
=
[
[
"John Doe"
,
"555-555-5555"
],
[
"Albert Einstein"
,
"212-555-5555"
],
]
"John Doe's phone number is"
,
find_phonenumber
[
phonebook
,
"John Doe"
]
Điều này cho phép chúng tôi tạo các mục nhập trong một tập hợp hoặc từ điển được lập chỉ mục bởi các thuộc tính của đối tượng
14 chứ không phải địa chỉ bộ nhớ của đối tượng được khởi tạodef
list_unique_names
[
phonebook
]:
unique_names
=
[]
for
name
,
phonenumber
in
phonebook
:
#
first_name
,
last_name
=
name
.
split
[
" "
,
1
]
for
unique
in
unique_names
:
#
if
unique
==
first_name
:
break
else
:
unique_names
.
append
[
first_name
]
return
len
[
unique_names
]
def
set_unique_names
[
phonebook
]:
unique_names
=
set
[]
for
name
,
phonenumber
in
phonebook
:
#
first_name
,
last_name
=
name
.
split
[
" "
,
1
]
unique_names
.
add
[
first_name
]
#
return
len
[
unique_names
]
phonebook
=
[
[
"John Doe"
,
"555-555-5555"
],
[
"Albert Einstein"
,
"212-555-5555"
],
[
"John Murphey"
,
"202-555-5555"
],
[
"Albert Rutherford"
,
"647-555-5555"
],
[
"Elaine Bodian"
,
"301-555-5555"
],
]
"Number of unique names from set method:"
,
set_unique_names
[
phonebook
]
"Number of unique names from list method:"
,
list_unique_names
[
phonebook
]
0phonebook
=
{
"John Doe"
:
"555-555-5555"
,
"Albert Einstein"
:
"212-555-5555"
,
}
"John Doe's phone number is"
,
phonebook
[
"John Doe"
]
Như đã đề cập ở phần trước khi chúng ta thảo luận về xung đột băm, hàm băm được chọn tùy chỉnh phải cẩn thận để phân phối đồng đều các giá trị băm để tránh xung đột. Có nhiều xung đột sẽ làm giảm hiệu suất của bảng băm. nếu hầu hết các khóa có xung đột, thì chúng ta cần phải liên tục "thăm dò" các giá trị khác, đi bộ một cách hiệu quả một phần lớn tiềm năng của từ điển để tìm khóa được đề cập. Trong trường hợp xấu nhất, khi tất cả các khóa trong từ điển xung đột với nhau, hiệu suất tra cứu trong từ điển là
0 và do đó giống như khi chúng ta tìm kiếm trong danh sáchdef
list_unique_names
[
phonebook
]:
unique_names
=
[]
for
name
,
phonenumber
in
phonebook
:
#
first_name
,
last_name
=
name
.
split
[
" "
,
1
]
for
unique
in
unique_names
:
#
if
unique
==
first_name
:
break
else
:
unique_names
.
append
[
first_name
]
return
len
[
unique_names
]
def
set_unique_names
[
phonebook
]:
unique_names
=
set
[]
for
name
,
phonenumber
in
phonebook
:
#
first_name
,
last_name
=
name
.
split
[
" "
,
1
]
unique_names
.
add
[
first_name
]
#
return
len
[
unique_names
]
phonebook
=
[
[
"John Doe"
,
"555-555-5555"
],
[
"Albert Einstein"
,
"212-555-5555"
],
[
"John Murphey"
,
"202-555-5555"
],
[
"Albert Rutherford"
,
"647-555-5555"
],
[
"Elaine Bodian"
,
"301-555-5555"
],
]
"Number of unique names from set method:"
,
set_unique_names
[
phonebook
]
"Number of unique names from list method:"
,
list_unique_names
[
phonebook
]
Nếu chúng tôi biết rằng chúng tôi đang lưu trữ 5.000 giá trị trong từ điển và chúng tôi cần tạo hàm băm cho đối tượng mà chúng tôi muốn sử dụng làm khóa, chúng tôi phải biết rằng từ điển sẽ được lưu trữ trong bảng băm có kích thước 32.768 và
Ý tưởng về “hàm băm của tôi được phân phối tốt như thế nào” được gọi là entropy của hàm băm. Entropy, được định nghĩa là
trong đó
02 là xác suất mà hàm băm cho hàm bămdef
find_phonenumber
[
phonebook
,
name
]:
for
n
,
p
in
phonebook
:
if
n
==
name
:
return
p
return
None
phonebook
=
[
[
"John Doe"
,
"555-555-5555"
],
[
"Albert Einstein"
,
"212-555-5555"
],
]
"John Doe's phone number is"
,
find_phonenumber
[
phonebook
,
"John Doe"
]
83. Nó được tối đa hóa khi mọi giá trị băm có xác suất được chọn bằng nhau. Hàm băm tối đa hóa entropy được gọi là hàm băm lý tưởng vì nó đảm bảo số lượng va chạm tối thiểuphonebook
=
{
"John Doe"
:
"555-555-5555"
,
"Albert Einstein"
:
"212-555-5555"
,
}
"John Doe's phone number is"
,
phonebook
[
"John Doe"
]
Đối với một từ điển lớn vô hạn, hàm băm được sử dụng cho số nguyên là lý tưởng. Điều này là do giá trị băm của một số nguyên đơn giản là chính số nguyên đó. Đối với một từ điển lớn vô hạn, giá trị mặt nạ là vô hạn và do đó chúng tôi xem xét tất cả các bit trong giá trị băm. Do đó, với hai số bất kỳ, chúng tôi có thể đảm bảo rằng giá trị băm của chúng sẽ không giống nhau
Tuy nhiên, nếu chúng tôi làm cho từ điển này trở nên hữu hạn, thì chúng tôi không thể có sự đảm bảo này nữa. Ví dụ: đối với từ điển có bốn thành phần, mặt nạ chúng tôi sử dụng là
86. Do đó, giá trị băm của sốphonebook
=
{
"John Doe"
:
"555-555-5555"
,
"Albert Einstein"
:
"212-555-5555"
,
}
"John Doe's phone number is"
,
phonebook
[
"John Doe"
]
05 làdef
find_phonenumber
[
phonebook
,
name
]:
for
n
,
p
in
phonebook
:
if
n
==
name
:
return
p
return
None
phonebook
=
[
[
"John Doe"
,
"555-555-5555"
],
[
"Albert Einstein"
,
"212-555-5555"
],
]
"John Doe's phone number is"
,
find_phonenumber
[
phonebook
,
"John Doe"
]
06 và giá trị băm củadef
find_phonenumber
[
phonebook
,
name
]:
for
n
,
p
in
phonebook
:
if
n
==
name
:
return
p
return
None
phonebook
=
[
[
"John Doe"
,
"555-555-5555"
],
[
"Albert Einstein"
,
"212-555-5555"
],
]
"John Doe's phone number is"
,
find_phonenumber
[
phonebook
,
"John Doe"
]
07 làdef
find_phonenumber
[
phonebook
,
name
]:
for
n
,
p
in
phonebook
:
if
n
==
name
:
return
p
return
None
phonebook
=
[
[
"John Doe"
,
"555-555-5555"
],
[
"Albert Einstein"
,
"212-555-5555"
],
]
"John Doe's phone number is"
,
find_phonenumber
[
phonebook
,
"John Doe"
]
08, và do đó các mục nhập của chúng sẽ xung độtdef
find_phonenumber
[
phonebook
,
name
]:
for
n
,
p
in
phonebook
:
if
n
==
name
:
return
p
return
None
phonebook
=
[
[
"John Doe"
,
"555-555-5555"
],
[
"Albert Einstein"
,
"212-555-5555"
],
]
"John Doe's phone number is"
,
find_phonenumber
[
phonebook
,
"John Doe"
]
Ghi chú
Để tìm mặt nạ cho một từ điển có số lượng phần tử tùy ý,
09, trước tiên chúng tôi tìm số lượng nhóm tối thiểu mà từ điển phải có để vẫn đầy hai phần ba [def
find_phonenumber
[
phonebook
,
name
]:
for
n
,
p
in
phonebook
:
if
n
==
name
:
return
p
return
None
phonebook
=
[
[
"John Doe"
,
"555-555-5555"
],
[
"Albert Einstein"
,
"212-555-5555"
],
]
"John Doe's phone number is"
,
find_phonenumber
[
phonebook
,
"John Doe"
]
20]. Sau đó, chúng tôi tìm kích thước từ điển nhỏ nhất sẽ chứa số phần tử này [8; 32; 128; 512; 2.048; v.v. ] và tìm số bit cần thiết để giữ số này. Ví dụ: nếudef
find_phonenumber
[
phonebook
,
name
]:
for
n
,
p
in
phonebook
:
if
n
==
name
:
return
p
return
None
phonebook
=
[
[
"John Doe"
,
"555-555-5555"
],
[
"Albert Einstein"
,
"212-555-5555"
],
]
"John Doe's phone number is"
,
find_phonenumber
[
phonebook
,
"John Doe"
]
21, thì chúng ta phải có ít nhất 1.731 nhóm, nghĩa là chúng ta cần một từ điển có 2.048 nhóm. Vì vậy, mặt nạ làdef
find_phonenumber
[
phonebook
,
name
]:
for
n
,
p
in
phonebook
:
if
n
==
name
:
return
p
return
None
phonebook
=
[
[
"John Doe"
,
"555-555-5555"
],
[
"Albert Einstein"
,
"212-555-5555"
],
]
"John Doe's phone number is"
,
find_phonenumber
[
phonebook
,
"John Doe"
]
22def
find_phonenumber
[
phonebook
,
name
]:
for
n
,
p
in
phonebook
:
if
n
==
name
:
return
p
return
None
phonebook
=
[
[
"John Doe"
,
"555-555-5555"
],
[
"Albert Einstein"
,
"212-555-5555"
],
]
"John Doe's phone number is"
,
find_phonenumber
[
phonebook
,
"John Doe"
]
Không có hàm băm nào tốt nhất để sử dụng khi sử dụng từ điển hữu hạn. Tuy nhiên, biết trước phạm vi giá trị nào sẽ được sử dụng và độ lớn của từ điển sẽ giúp đưa ra lựa chọn tốt. Ví dụ: nếu chúng tôi đang lưu trữ tất cả 676 tổ hợp của hai chữ cái viết thường làm khóa trong từ điển [aa, ab, ac, v.v. ], thì hàm băm tốt sẽ là hàm được hiển thị trong
Ví dụ 4-6. Hàm băm hai ký tự tối ưu
1phonebook
=
{
"John Doe"
:
"555-555-5555"
,
"Albert Einstein"
:
"212-555-5555"
,
}
"John Doe's phone number is"
,
phonebook
[
"John Doe"
]
Điều này không có xung đột băm đối với bất kỳ sự kết hợp nào của hai chữ cái viết thường, xem xét mặt nạ là
23 [từ điển gồm 676 giá trị sẽ được giữ trong bảng băm có độ dài 2.048, có mặt nạ làdef
find_phonenumber
[
phonebook
,
name
]:
for
n
,
p
in
phonebook
:
if
n
==
name
:
return
p
return
None
phonebook
=
[
[
"John Doe"
,
"555-555-5555"
],
[
"Albert Einstein"
,
"212-555-5555"
],
]
"John Doe's phone number is"
,
find_phonenumber
[
phonebook
,
"John Doe"
]
24]def
find_phonenumber
[
phonebook
,
name
]:
for
n
,
p
in
phonebook
:
if
n
==
name
:
return
p
return
None
phonebook
=
[
[
"John Doe"
,
"555-555-5555"
],
[
"Albert Einstein"
,
"212-555-5555"
],
]
"John Doe's phone number is"
,
find_phonenumber
[
phonebook
,
"John Doe"
]
chỉ ra rất rõ ràng sự phân nhánh của việc có một hàm băm tồi đối với một lớp do người dùng định nghĩa—ở đây, chi phí của một hàm băm tồi [trên thực tế, đó là hàm băm tồi tệ nhất có thể. ] là 21. Tốc độ tra cứu chậm gấp 8 lần
Ví dụ 4-7. Sự khác biệt về thời gian giữa các hàm băm tốt và xấu
2phonebook
=
{
"John Doe"
:
"555-555-5555"
,
"Albert Einstein"
:
"212-555-5555"
,
}
"John Doe's phone number is"
,
phonebook
[
"John Doe"
]
Mẹo
- Chỉ ra rằng đối với một từ điển vô hạn [và do đó là một mặt nạ vô hạn], sử dụng giá trị của một số nguyên làm hàm băm của nó không có xung đột
- Chỉ ra rằng hàm băm được đưa ra là lý tưởng cho bảng băm có kích thước 1,024. Tại sao nó không lý tưởng cho các bảng băm nhỏ hơn?
Từ điển và không gian tên
Tra cứu từ điển rất nhanh; . Một khu vực mà bề mặt này nằm trong quản lý không gian tên của Python, nơi sử dụng nhiều từ điển để thực hiện tra cứu của nó
Bất cứ khi nào một biến, hàm hoặc mô-đun được gọi trong Python, sẽ có một hệ thống phân cấp xác định nơi nó tìm kiếm các đối tượng này. Đầu tiên, Python nhìn vào bên trong mảng
25, mảng này có các mục nhập cho tất cả các biến cục bộ. Python làm việc chăm chỉ để tra cứu biến cục bộ nhanh chóng và đây là phần duy nhất của chuỗi không yêu cầu tra cứu từ điển. Nếu nó không tồn tại ở đó, thì từ điểndef
find_phonenumber
[
phonebook
,
name
]:
for
n
,
p
in
phonebook
:
if
n
==
name
:
return
p
return
None
phonebook
=
[
[
"John Doe"
,
"555-555-5555"
],
[
"Albert Einstein"
,
"212-555-5555"
],
]
"John Doe's phone number is"
,
find_phonenumber
[
phonebook
,
"John Doe"
]
26 được tìm kiếm. Cuối cùng, nếu đối tượng không được tìm thấy ở đó, đối tượngdef
find_phonenumber
[
phonebook
,
name
]:
for
n
,
p
in
phonebook
:
if
n
==
name
:
return
p
return
None
phonebook
=
[
[
"John Doe"
,
"555-555-5555"
],
[
"Albert Einstein"
,
"212-555-5555"
],
]
"John Doe's phone number is"
,
find_phonenumber
[
phonebook
,
"John Doe"
]
27 sẽ được tìm kiếm. Điều quan trọng cần lưu ý là mặc dùdef
find_phonenumber
[
phonebook
,
name
]:
for
n
,
p
in
phonebook
:
if
n
==
name
:
return
p
return
None
phonebook
=
[
[
"John Doe"
,
"555-555-5555"
],
[
"Albert Einstein"
,
"212-555-5555"
],
]
"John Doe's phone number is"
,
find_phonenumber
[
phonebook
,
"John Doe"
]
25 vàdef
find_phonenumber
[
phonebook
,
name
]:
for
n
,
p
in
phonebook
:
if
n
==
name
:
return
p
return
None
phonebook
=
[
[
"John Doe"
,
"555-555-5555"
],
[
"Albert Einstein"
,
"212-555-5555"
],
]
"John Doe's phone number is"
,
find_phonenumber
[
phonebook
,
"John Doe"
]
26 rõ ràng là từ điển và về mặt kỹ thuật,def
find_phonenumber
[
phonebook
,
name
]:
for
n
,
p
in
phonebook
:
if
n
==
name
:
return
p
return
None
phonebook
=
[
[
"John Doe"
,
"555-555-5555"
],
[
"Albert Einstein"
,
"212-555-5555"
],
]
"John Doe's phone number is"
,
find_phonenumber
[
phonebook
,
"John Doe"
]
27 là đối tượng mô-đun, khi tìm kiếmdef
find_phonenumber
[
phonebook
,
name
]:
for
n
,
p
in
phonebook
:
if
n
==
name
:
return
p
return
None
phonebook
=
[
[
"John Doe"
,
"555-555-5555"
],
[
"Albert Einstein"
,
"212-555-5555"
],
]
"John Doe's phone number is"
,
find_phonenumber
[
phonebook
,
"John Doe"
]
27 cho một thuộc tính nhất định, chúng tôi chỉ thực hiện tra cứu từ điển bên trong bản đồdef
find_phonenumber
[
phonebook
,
name
]:
for
n
,
p
in
phonebook
:
if
n
==
name
:
return
p
return
None
phonebook
=
[
[
"John Doe"
,
"555-555-5555"
],
[
"Albert Einstein"
,
"212-555-5555"
],
]
"John Doe's phone number is"
,
find_phonenumber
[
phonebook
,
"John Doe"
]
25 của nó [đây là trường hợp cho tất cả các đối tượng mô-đun và . ]def
find_phonenumber
[
phonebook
,
name
]:
for
n
,
p
in
phonebook
:
if
n
==
name
:
return
p
return
None
phonebook
=
[
[
"John Doe"
,
"555-555-5555"
],
[
"Albert Einstein"
,
"212-555-5555"
],
]
"John Doe's phone number is"
,
find_phonenumber
[
phonebook
,
"John Doe"
]
Để làm cho điều này rõ ràng hơn, hãy xem một ví dụ đơn giản về cách gọi các hàm được xác định trong các phạm vi khác nhau []. Chúng ta có thể phân tách các chức năng bằng mô-đun
03 [] để hiểu rõ hơn về cách các tra cứu không gian tên này đang diễn raphonebook
=
{
"John Doe"
:
"555-555-5555"
,
"Albert Einstein"
:
"212-555-5555"
,
}
"John Doe's phone number is"
,
phonebook
[
"John Doe"
]
Ví dụ 4-8. tra cứu không gian tên
3phonebook
=
{
"John Doe"
:
"555-555-5555"
,
"Albert Einstein"
:
"212-555-5555"
,
}
"John Doe's phone number is"
,
phonebook
[
"John Doe"
]
Ví dụ 4-9. Tra cứu không gian tên được tháo rời
4phonebook
=
{
"John Doe"
:
"555-555-5555"
,
"Albert Einstein"
:
"212-555-5555"
,
}
"John Doe's phone number is"
,
phonebook
[
"John Doe"
]
Hàm đầu tiên,
04, thực hiện cuộc gọi đếnphonebook
=
{
"John Doe"
:
"555-555-5555"
,
"Albert Einstein"
:
"212-555-5555"
,
}
"John Doe's phone number is"
,
phonebook
[
"John Doe"
]
05 bằng cách xem rõ ràng thư viện toán học. Điều này cũng hiển nhiên trong mã byte được tạo. trước tiên phải tải tham chiếu đến mô-đunphonebook
=
{
"John Doe"
:
"555-555-5555"
,
"Albert Einstein"
:
"212-555-5555"
,
}
"John Doe's phone number is"
,
phonebook
[
"John Doe"
]
06, sau đó chúng tôi thực hiện tra cứu thuộc tính trên mô-đun này cho đến khi cuối cùng chúng tôi có tham chiếu đến hàmphonebook
=
{
"John Doe"
:
"555-555-5555"
,
"Albert Einstein"
:
"212-555-5555"
,
}
"John Doe's phone number is"
,
phonebook
[
"John Doe"
]
05. Điều này được thực hiện thông qua hai lần tra cứu từ điển, một để tìm mô-đunphonebook
=
{
"John Doe"
:
"555-555-5555"
,
"Albert Einstein"
:
"212-555-5555"
,
}
"John Doe's phone number is"
,
phonebook
[
"John Doe"
]
06 và một để tìm hàmphonebook
=
{
"John Doe"
:
"555-555-5555"
,
"Albert Einstein"
:
"212-555-5555"
,
}
"John Doe's phone number is"
,
phonebook
[
"John Doe"
]
05 trong mô-đunphonebook
=
{
"John Doe"
:
"555-555-5555"
,
"Albert Einstein"
:
"212-555-5555"
,
}
"John Doe's phone number is"
,
phonebook
[
"John Doe"
]
Mặt khác,
10 nhập hàm ________ 005 một cách rõ ràng từ mô-đun ________ 006 và sau đó hàm này có thể truy cập trực tiếp trong không gian tên chung. Điều này có nghĩa là chúng ta có thể tránh tra cứu mô-đunphonebook
=
{
"John Doe"
:
"555-555-5555"
,
"Albert Einstein"
:
"212-555-5555"
,
}
"John Doe's phone number is"
,
phonebook
[
"John Doe"
]
06 và tra cứu thuộc tính tiếp theo. Tuy nhiên, chúng ta vẫn phải tìm hàmphonebook
=
{
"John Doe"
:
"555-555-5555"
,
"Albert Einstein"
:
"212-555-5555"
,
}
"John Doe's phone number is"
,
phonebook
[
"John Doe"
]
05 trong không gian tên chung. Đây là một lý do khác để nói rõ ràng về những chức năng bạn đang nhập từ một mô-đun. Thực hành này không chỉ làm cho mã dễ đọc hơn, bởi vì trình đọc biết chính xác chức năng nào được yêu cầu từ các nguồn bên ngoài, mà còn tăng tốc độ mãphonebook
=
{
"John Doe"
:
"555-555-5555"
,
"Albert Einstein"
:
"212-555-5555"
,
}
"John Doe's phone number is"
,
phonebook
[
"John Doe"
]
Cuối cùng,
15 định nghĩa hàmphonebook
=
{
"John Doe"
:
"555-555-5555"
,
"Albert Einstein"
:
"212-555-5555"
,
}
"John Doe's phone number is"
,
phonebook
[
"John Doe"
]
05 làm đối số từ khóa, với giá trị mặc định của nó là tham chiếu đến hàmphonebook
=
{
"John Doe"
:
"555-555-5555"
,
"Albert Einstein"
:
"212-555-5555"
,
}
"John Doe's phone number is"
,
phonebook
[
"John Doe"
]
05 trong mô-đunphonebook
=
{
"John Doe"
:
"555-555-5555"
,
"Albert Einstein"
:
"212-555-5555"
,
}
"John Doe's phone number is"
,
phonebook
[
"John Doe"
]
06. Mặc dù chúng ta vẫn cần tìm tham chiếu đến hàm này trong mô-đun, nhưng điều này chỉ cần thiết khi hàmphonebook
=
{
"John Doe"
:
"555-555-5555"
,
"Albert Einstein"
:
"212-555-5555"
,
}
"John Doe's phone number is"
,
phonebook
[
"John Doe"
]
15 được xác định lần đầu. Sau này, tham chiếu đến hàmphonebook
=
{
"John Doe"
:
"555-555-5555"
,
"Albert Einstein"
:
"212-555-5555"
,
}
"John Doe's phone number is"
,
phonebook
[
"John Doe"
]
05 được lưu trữ trong định nghĩa hàm dưới dạng biến cục bộ ở dạng đối số từ khóa mặc định. Như đã đề cập trước đó, các biến cục bộ không cần tra từ điển để tìm; . Vì vậy, việc tìm kiếm hàm khá nhanhphonebook
=
{
"John Doe"
:
"555-555-5555"
,
"Albert Einstein"
:
"212-555-5555"
,
}
"John Doe's phone number is"
,
phonebook
[
"John Doe"
]
Mặc dù các hiệu ứng này là một kết quả thú vị của cách quản lý các không gian tên trong Python, nhưng
15 chắc chắn không phải là “Pythonic. ” May mắn thay, những tra cứu từ điển bổ sung này chỉ bắt đầu giảm hiệu suất khi chúng được gọi nhiều [i. e. , trong khối trong cùng của một vòng lặp rất nhanh, chẳng hạn như trong ví dụ về tập hợp Julia]. Với suy nghĩ này, một giải pháp dễ đọc hơn sẽ là đặt một biến cục bộ với tham chiếu toàn cục trước khi vòng lặp bắt đầu. Chúng ta sẽ vẫn phải thực hiện tra cứu toàn cầu một lần bất cứ khi nào hàm được gọi, nhưng tất cả các lệnh gọi đến hàm đó trong vòng lặp sẽ được thực hiện nhanh hơn. Điều này nói lên thực tế là mã bị chậm thậm chí một phút có thể được khuếch đại nếu mã đó đang được chạy hàng triệu lần. Mặc dù việc tra cứu từ điển có thể chỉ mất vài trăm nano giây, nhưng nếu chúng ta lặp lại hàng triệu lần trong quá trình tra cứu này thì nó có thể nhanh chóng tăng lên. Trên thực tế, nhìn vào chúng ta thấy một số 9. Tăng tốc 4% chỉ bằng cách đặt hàmphonebook
=
{
"John Doe"
:
"555-555-5555"
,
"Albert Einstein"
:
"212-555-5555"
,
}
"John Doe's phone number is"
,
phonebook
[
"John Doe"
]
05 cục bộ vào vòng lặp chặt chẽ gọi nóphonebook
=
{
"John Doe"
:
"555-555-5555"
,
"Albert Einstein"
:
"212-555-5555"
,
}
"John Doe's phone number is"
,
phonebook
[
"John Doe"
]
Ví dụ 4-10. Ảnh hưởng của tra cứu không gian tên chậm trong vòng lặp
5phonebook
=
{
"John Doe"
:
"555-555-5555"
,
"Albert Einstein"
:
"212-555-5555"
,
}
"John Doe's phone number is"
,
phonebook
[
"John Doe"
]
Từ điển và bộ cung cấp một cách tuyệt vời để lưu trữ dữ liệu có thể được lập chỉ mục bằng một khóa. Cách khóa này được sử dụng, thông qua hàm băm, có thể ảnh hưởng lớn đến hiệu suất kết quả của cấu trúc dữ liệu. Hơn nữa, hiểu cách thức hoạt động của từ điển giúp bạn hiểu rõ hơn không chỉ về cách tổ chức dữ liệu mà còn về cách tổ chức mã của bạn, vì từ điển là một phần nội tại của chức năng nội bộ của Python
Trong chương tiếp theo, chúng ta sẽ khám phá các trình tạo, cho phép chúng ta cung cấp dữ liệu cho mã với nhiều quyền kiểm soát hơn đối với việc đặt hàng và không phải lưu trữ toàn bộ tập dữ liệu trong bộ nhớ trước. Điều này cho phép chúng tôi vượt qua nhiều rào cản có thể gặp phải khi sử dụng bất kỳ cấu trúc dữ liệu nội tại nào của Python