Khác biệt giữa bản sửa đổi của “Tìm kiếm nhị phân”

Bách khoa toàn thư mở Wikipedia
Nội dung được xóa Nội dung được thêm vào
nKhông có tóm lược sửa đổi
Không có tóm lược sửa đổi
Dòng 140: Dòng 140:
Trong trường hợp tốt nhất, khi giá trị cần tìm nằm ngay giữa mảng, vị trí của nó được trả về ngay sau một phép lặp.{{Sfn|Chang|2003|p=169}}
Trong trường hợp tốt nhất, khi giá trị cần tìm nằm ngay giữa mảng, vị trí của nó được trả về ngay sau một phép lặp.{{Sfn|Chang|2003|p=169}}


In terms of iterations, không có thuật toán tìm kiếm nào chỉ bằng so sánh các phần tử có thể có hiệu suất trung bình hay thậm chí trong trường hợp tệ nhất tốt hơn tìm kiếm nhị phân. Cây so sánh biểu diễn tìm kiếm nhị phân có số bậc ít nhất có thể as every level above the lowest level of the tree is filled completely.{{Efn|Bất cứ thuật toán tìm kiếm nào chỉ dựa trên các phép so sánh có thể được biểu diễn bằng cây so sánh nhị phân. ''Đường đi trong'' (''internal path'') là bất kỳ đường nào đi từ gốc tới một nút có sẵn. Gọi <math>I</math> là ''độ dài đường đi trong'' (''internal path length''), hay tổng độ dài tất cả các đường đi trong của cây. Nếu mỗi phần tử có xác suất được tìm như nhau, trường hợp trung bình là <math>1 + \frac{I}{n}</math>, hoặc đơn giản hơn là một cộng với trung bình toàn bộ độ dài các đường đi trong trong cây. Nguyên nhân là do các đường đi trong biểu diễn các phần tử mà thuật toán tìm kiếm tiến hành so sánh với giá trị cần tìm. Độ dài của các đường này biểu diễn số phép lặp ''sau'' nút gốc. Làm phép cộng trung bình tổng độ dài các đường này với thêm một phép lặp nữa ở nút gốc sẽ ra trường hợp trung bình. Do đó, để giảm tối đa số phép so sánh trung bình, cần phải giảm độ dài đường đi trong <math>I</math>. Thực chất cây biểu diễn tìm kiếm nhị phân đã giảm tối đa độ dài đường đi trong. {{Harvnb|Knuth|1998}} chứng minh được rằng độ dài ''đường đi ngoài'' (''external path'' - the path length over all nodes where both children are present for each already-existing node) được giảm tối đa khi các nút ngoài (các nút không có nút con) nằm trong hai bậc liên tiếp của cây. Điều này cũng áp dụng với các đường đi trong do độ dài đường đi trong <math>I</math> có liên hệ tuyến tính với độ dài đường đi ngoài <math>E</math>. Với mỗi cây có <math>n</math> nút, <math>I = E - 2n</math>. Khi mỗi cây con có số nút bằng nhau, tương đương với khi mảng được chia thành hai nửa ở mỗi phép lặp, các nút ngoài và các nút cha phía trong của chúng nằm trong vòng hai bậc. Như vậy, thuật toán tìm kiếm nhị phân đã giảm được tối đa số phép so sánh trung bình do cây so sánh của thuật toán này có độ dài đường trong nhỏ nhất.{{Sfn|Knuth|1998|loc=§6.2.1 ("Searching an ordered table"), phân mục "Further analysis of binary search"}}}} Otherwise, the search algorithm can eliminate few elements in an iteration, increasing the number of iterations required in the average and worst case. This is the case for other search algorithms based on comparisons, as while they may work faster on some target values, the average performance over ''all'' elements is worse than binary search. By dividing the array in half, binary search ensures that the size of both subarrays are as similar as possible.{{Sfn|Knuth|1998|loc=§6.2.1 ("Searching an ordered table"), subsection "Further analysis of binary search"}}
Về mặt số phép lặp, không có thuật toán tìm kiếm nào chỉ bằng so sánh các phần tử có thể có hiệu suất trung bình hay thậm chí trong trường hợp tệ nhất tốt hơn tìm kiếm nhị phân. Cây so sánh biểu diễn tìm kiếm nhị phân có số bậc ít nhất có thể do mọi bậc phía trên bậc thấp nhất của cây đều được lấp đầy hoàn toàn.{{Efn|Bất cứ thuật toán tìm kiếm nào chỉ dựa trên các phép so sánh có thể được biểu diễn bằng cây so sánh nhị phân. ''Đường đi trong'' (''internal path'') là bất kỳ đường nào đi từ gốc tới một nút có sẵn. Gọi <math>I</math> là ''độ dài đường đi trong'' (''internal path length''), hay tổng độ dài tất cả các đường đi trong của cây. Nếu mỗi phần tử có xác suất được tìm như nhau, trường hợp trung bình là <math>1 + \frac{I}{n}</math>, hoặc đơn giản hơn là một cộng với trung bình toàn bộ độ dài các đường đi trong trong cây. Nguyên nhân là do các đường đi trong biểu diễn các phần tử mà thuật toán tìm kiếm tiến hành so sánh với giá trị cần tìm. Độ dài của các đường này biểu diễn số phép lặp ''sau'' nút gốc. Làm phép cộng trung bình tổng độ dài các đường này với thêm một phép lặp nữa ở nút gốc sẽ ra trường hợp trung bình. Do đó, để giảm tối đa số phép so sánh trung bình, cần phải giảm độ dài đường đi trong <math>I</math>. Thực chất cây biểu diễn tìm kiếm nhị phân đã giảm tối đa độ dài đường đi trong. {{Harvnb|Knuth|1998}} chứng minh được rằng độ dài ''đường đi ngoài'' (''external path'' - the path length over all nodes where both children are present for each already-existing node) được giảm tối đa khi các nút ngoài (các nút không có nút con) nằm trong hai bậc liên tiếp của cây. Điều này cũng áp dụng với các đường đi trong do độ dài đường đi trong <math>I</math> có liên hệ tuyến tính với độ dài đường đi ngoài <math>E</math>. Với mỗi cây có <math>n</math> nút, <math>I = E - 2n</math>. Khi mỗi cây con có số nút bằng nhau, tương đương với khi mảng được chia thành hai nửa ở mỗi phép lặp, các nút ngoài và các nút cha phía trong của chúng nằm trong vòng hai bậc. Như vậy, thuật toán tìm kiếm nhị phân đã giảm được tối đa số phép so sánh trung bình do cây so sánh của thuật toán này có độ dài đường trong nhỏ nhất.{{Sfn|Knuth|1998|loc=§6.2.1 ("Searching an ordered table"), phân mục "Further analysis of binary search"}}}} Nếu không, thuật toán tìm kiếm thể loại bỏ một vài phần tử trong mỗi phép lặp, làm tăng số phép lặp cần thực hiện trong trường hợp trung bình và tệ nhất. Các thuật toán tìm kiếm khác dùng cách so sánh đều bị rơi vào trường hợp này, do mặc chúng thể chạy nhanh hơn với một số giá trị cần tìm nhất định, hiệu suất trung bình trên ''tất cả'' phần tử vẫn kém hơn tìm kiếm nhị phân. Bằng cách chia mảng thành hai nửa, thuật toán tìm kiếm nhị phân đảm bảo được kích thước của hai mảng con giống nhau nhất có thể.{{Sfn|Knuth|1998|loc=§6.2.1 ("Searching an ordered table"), subsection "Further analysis of binary search"}}


==== Độ phức tạp không gian ====
==== Độ phức tạp không gian ====
Dòng 176: Dòng 176:
</math>
</math>


Với <math>n</math> là một số nguyên, this is equivalent to the equation for the average case on a successful search specified above.
Với <math>n</math> là một số nguyên, phương trình trên tương đương với phương trình trong trường hợp trung bình cho một lần tìm kiếm thành công như đã chỉ ra ở trên.


==== Tìm kiếm không thành công ====
==== Tìm kiếm không thành công ====
Dòng 202: Dòng 202:


== Tìm kiếm nhị phân với các hệ thống khác ==
== Tìm kiếm nhị phân với các hệ thống khác ==
Áp dụng tìm kiếm nhị phân với các mảng đã được sắp xếp là một giải pháp rất không hiệu quả khi xen giữa các phép chèn và xóa còn là các phép truy hồi, mỗi thao tác đó phải mất <math display="inline">O(n)</math> thời gian để thực hiện. Ngoài ra, yêu cầu phải sắp xếp trước các mảng có thể làm phức tạp việc sử dụng bộ nhớ, đặc biệt là khi các phần tử thường được chèn vào trong mảng.{{Sfn|Knuth|1997|loc=§2.2.2 ("Sequential Allocation")}} Một số cấu trúc dữ liệu khác có thể hỗ trợ các thao tác chèn và xóa hiệu quả hơn nhiều. Tìm kiếm nhị phân có thể được dùng để thực hiện so khớp chính xác và thao tác [[Tập hợp (cấu trúc dữ liệu trừu tượng)|quan hệ tập hợp]] (determining whether a target value is in a collection of values). Hai bài toán này cũng có thể được thực hiện nhanh hơn trên các cấu trúc dữ liệu khác. Tuy nhiên, khác với các hệ thống tìm kiếm khác, tìm kiếm nhị phân hiệu quả hơn trong phép so khớp xấp xỉ, thường chỉ thực hiện trong <math display="inline">O(\log n)</math> thời gian bất kể kiểu hay cấu trúc của các giá trị.<ref name="pred">{{cite journal|last1=Beame|first1=Paul|last2=Fich|first2=Faith E.|authorlink2=Faith Ellen|title=Optimal bounds for the predecessor problem and related problems|journal=[[Journal of Computer and System Sciences]]|date=2001|volume=65|issue=1|pages=38–72|doi=10.1006/jcss.2002.1822}}</ref> Ngoài ra còn có một số phép toán, như tìm phần tử nhỏ nhất và lớn nhất, có thể được thực hiện hiệu quả trên mảng đã được sắp xếp.{{sfn|Sedgewick|Wayne|2011|loc=§3.1, subsection "Rank and selection"}}
Áp dụng tìm kiếm nhị phân với các mảng đã được sắp xếp là một giải pháp rất không hiệu quả khi xen giữa các phép chèn và xóa còn là các phép truy hồi, mỗi thao tác đó phải mất <math display="inline">O(n)</math> thời gian để thực hiện. Ngoài ra, yêu cầu phải sắp xếp trước các mảng có thể làm phức tạp việc sử dụng bộ nhớ, đặc biệt là khi các phần tử thường được chèn vào trong mảng.{{Sfn|Knuth|1997|loc=§2.2.2 ("Sequential Allocation")}} Một số cấu trúc dữ liệu khác có thể hỗ trợ các thao tác chèn và xóa hiệu quả hơn nhiều. Tìm kiếm nhị phân có thể được dùng để thực hiện so khớp chính xác và thao tác [[Tập hợp (cấu trúc dữ liệu trừu tượng)|quan hệ tập hợp]] (xác định giá trị cần tìm phải một tập hợp nhiều giá trị không). Hai bài toán này cũng có thể được thực hiện nhanh hơn trên các cấu trúc dữ liệu khác. Tuy nhiên, khác với các hệ thống tìm kiếm khác, tìm kiếm nhị phân hiệu quả hơn trong phép so khớp xấp xỉ, thường chỉ thực hiện trong <math display="inline">O(\log n)</math> thời gian bất kể kiểu hay cấu trúc của các giá trị.<ref name="pred">{{cite journal|last1=Beame|first1=Paul|last2=Fich|first2=Faith E.|authorlink2=Faith Ellen|title=Optimal bounds for the predecessor problem and related problems|journal=[[Journal of Computer and System Sciences]]|date=2001|volume=65|issue=1|pages=38–72|doi=10.1006/jcss.2002.1822}}</ref> Ngoài ra còn có một số phép toán, như tìm phần tử nhỏ nhất và lớn nhất, có thể được thực hiện hiệu quả trên mảng đã được sắp xếp.{{sfn|Sedgewick|Wayne|2011|loc=§3.1, subsection "Rank and selection"}}


=== Tìm kiếm tuyến tính ===
=== Tìm kiếm tuyến tính ===
[[Tìm kiếm tuyến tính]] là một thuật toán tìm kiếm đơn giản: nó kiểm tra mọi bản ghi cho tới khi tìm thấy giá trị cần tìm. Tìm kiếm tuyến tính có thể được thực hiện trên danh sách liên kết, cho phép các thao tác chèn và xóa nhanh hơn so với mảng. Binary search is faster than linear search for sorted arrays except if the array is short, although the array needs to be sorted beforehand.{{Efn|{{Harvnb|Knuth|1998}} đã thực hiện phân tích hiệu suất thời gian của cả hai thuật toán tìm kiếm này. Trên chiếc máy tính [[MIX]] của Knuth, được ông thiết kế để biểu diễn cho một chiếc máy tính thông thường, tìm kiếm nhị phân trung bình mất <math display="inline">18 \log n - 16</math> đơn vị thời gian cho một lần tìm kiếm thành công, trong khi tìm kiếm tuyến tính khi có một [[sentinel node]] ở cuối danh sách mất <math display="inline">1.75n + 8.5 - \frac{n \text{ mod } 2}{4n}</math> đơn vị. Tìm kiếm tuyến tính có độ phức tạp ban đầu thấp hơn do nó chỉ yêu cầu các tính toán tối thiểu, nhưng sau đó nhanh chóng vượt qua tìm kiếm nhị phân về độ phức tạp. Trên máy tính MIX, binary search only outperforms linear search with a sentinel if <math display="inline">n > 44</math>.{{Sfn|Knuth|1998|loc=§6.2.1 ("Searching an ordered table"), phân mục "Further analysis of binary search"}}{{Sfn|Knuth|1998|loc=Answers to Exercises (§6.2.1) cho "Exercise 5"}}}}{{Sfn|Knuth|1998|loc=§6.2.1 ("Searching an ordered table")}} Tất cả các [[thuật toán sắp xếp]] dựa trên thao tác so sánh các phần tử, như [[sắp xếp nhanh]] và [[sắp xếp trộn]], cần ít nhất <math display="inline">O(n \log n)</math> phép so sánh trong trường hợp tệ nhất.{{Sfn|Knuth|1998|loc=§5.3.1 ("Minimum-Comparison sorting")}} Khác với tìm kiếm tuyến tính, tìm kiếm nhị phân có thể được ứng dụng để tìm so khớp xấp xỉ một cách hiệu quả. Một số thao tác như tìm phần tử nhỏ nhất và lớn nhất có thể được thực hiện hiệu quả chỉ khi mảng đã được sắp xếp.{{Sfn|Sedgewick|Wayne|2011|loc=§3.2 ("Ordered symbol tables")}}
[[Tìm kiếm tuyến tính]] là một thuật toán tìm kiếm đơn giản: nó kiểm tra mọi bản ghi cho tới khi tìm thấy giá trị cần tìm. Tìm kiếm tuyến tính có thể được thực hiện trên danh sách liên kết, cho phép các thao tác chèn và xóa nhanh hơn so với mảng. Tìm kiếm nhị phân nhanh hơn tìm kiếm tuyến tính trên các mảng đã được sắp xếp trừ khi mảng có kích thước nhỏ, mặc dù trước khi tìm kiếm cần phải thực hiện sắp xếp lại mảng trước.{{Efn|{{Harvnb|Knuth|1998}} đã thực hiện phân tích hiệu suất thời gian của cả hai thuật toán tìm kiếm này. Trên chiếc máy tính [[MIX]] của Knuth, được ông thiết kế để biểu diễn cho một chiếc máy tính thông thường, tìm kiếm nhị phân trung bình mất <math display="inline">18 \log n - 16</math> đơn vị thời gian cho một lần tìm kiếm thành công, trong khi tìm kiếm tuyến tính khi có một [[sentinel node]] ở cuối danh sách mất <math display="inline">1.75n + 8.5 - \frac{n \text{ mod } 2}{4n}</math> đơn vị. Tìm kiếm tuyến tính có độ phức tạp ban đầu thấp hơn do nó chỉ yêu cầu các tính toán tối thiểu, nhưng sau đó nhanh chóng vượt qua tìm kiếm nhị phân về độ phức tạp. Trên máy tính MIX, binary search only outperforms linear search with a sentinel if <math display="inline">n > 44</math>.{{Sfn|Knuth|1998|loc=§6.2.1 ("Searching an ordered table"), phân mục "Further analysis of binary search"}}{{Sfn|Knuth|1998|loc=Answers to Exercises (§6.2.1) cho "Exercise 5"}}}}{{Sfn|Knuth|1998|loc=§6.2.1 ("Searching an ordered table")}} Tất cả các [[thuật toán sắp xếp]] dựa trên thao tác so sánh các phần tử, như [[sắp xếp nhanh]] và [[sắp xếp trộn]], cần ít nhất <math display="inline">O(n \log n)</math> phép so sánh trong trường hợp tệ nhất.{{Sfn|Knuth|1998|loc=§5.3.1 ("Minimum-Comparison sorting")}} Khác với tìm kiếm tuyến tính, tìm kiếm nhị phân có thể được ứng dụng để tìm so khớp xấp xỉ một cách hiệu quả. Một số thao tác như tìm phần tử nhỏ nhất và lớn nhất có thể được thực hiện hiệu quả chỉ khi mảng đã được sắp xếp.{{Sfn|Sedgewick|Wayne|2011|loc=§3.2 ("Ordered symbol tables")}}


=== Cây ===
=== Cây ===
Dòng 226: Dòng 226:
=== Các cấu trúc dữ liệu khác ===
=== Các cấu trúc dữ liệu khác ===
Còn có các cấu trúc dữ liệu khác có thể cải thiện tìm kiếm nhị phân trong một số trường hợp với cả mục đích tìm kiếm lẫn các thao tác khác có thể được thực hiện trên các mảng đã được sắp xếp. Ví dụ, các phép tìm kiếm, so khớp xấp xỉ và các thao tác khác trên mảng đã được sắp xếp có thể được thực hiện hiệu quả hơn tìm kiếm nhị phân nếu áp dụng trên các cấu trúc dữ liệu chuyên biệt như cây van Emde Boas, [[fusion tree]]s, [[trie]] và [[mảng bit]]. Các cấu trúc dữ liệu chuyên biệt này thường chỉ nhanh hơn vì chúng sử dụng các tính chất của những khóa có điều kiện nhất định (thông thường những khóa này là các số nguyên nhỏ), và do đó thời gian chạy hoặc không gian cần sử dụng có thể lớn nếu không đáp ứng được các điều kiện ấy.<ref name="pred" /> As long as the keys can be ordered, these operations can always be done at least efficiently on a sorted array regardless of the keys. Some structures, such as Judy arrays, use a combination of approaches to mitigate this while retaining efficiency and the ability to perform approximate matching.<ref name="judyarray" />
Còn có các cấu trúc dữ liệu khác có thể cải thiện tìm kiếm nhị phân trong một số trường hợp với cả mục đích tìm kiếm lẫn các thao tác khác có thể được thực hiện trên các mảng đã được sắp xếp. Ví dụ, các phép tìm kiếm, so khớp xấp xỉ và các thao tác khác trên mảng đã được sắp xếp có thể được thực hiện hiệu quả hơn tìm kiếm nhị phân nếu áp dụng trên các cấu trúc dữ liệu chuyên biệt như cây van Emde Boas, [[fusion tree]]s, [[trie]] và [[mảng bit]]. Các cấu trúc dữ liệu chuyên biệt này thường chỉ nhanh hơn vì chúng sử dụng các tính chất của những khóa có điều kiện nhất định (thông thường những khóa này là các số nguyên nhỏ), và do đó thời gian chạy hoặc không gian cần sử dụng có thể lớn nếu không đáp ứng được các điều kiện ấy.<ref name="pred" /> As long as the keys can be ordered, these operations can always be done at least efficiently on a sorted array regardless of the keys. Some structures, such as Judy arrays, use a combination of approaches to mitigate this while retaining efficiency and the ability to perform approximate matching.<ref name="judyarray" />

==Biến thể==

===Uniform binary search===
{{main|Uniform binary search}}
[[File:Uniform binary search.svg|thumb|upright=1.0|[[Uniform binary search]] stores the difference between the current and the two next possible middle elements instead of specific bounds.]]
Uniform binary search stores, instead of the lower and upper bounds, the difference in the index of the middle element from the current iteration to the next iteration. A [[lookup table]] containing the differences is computed beforehand. For example, if the array to be searched is {{math|[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]}}, the middle element (<math>m</math>) would be {{math|6}}. In this case, the middle element of the left subarray ({{math|[1, 2, 3, 4, 5]}}) is {{math|3}} and the middle element of the right subarray ({{math|[7, 8, 9, 10, 11]}}) is {{math|9}}. Uniform binary search would store the value of {{math|3}} as both indices differ from {{math|6}} by this same amount.{{Sfn|Knuth|1998|loc=§6.2.1 ("Searching an ordered table"), subsection "An important variation"}} To reduce the search space, the algorithm either adds or subtracts this change from the index of the middle element. Uniform binary search may be faster on systems where it is inefficient to calculate the midpoint, such as on [[decimal computer]]s.{{Sfn|Knuth|1998|loc=§6.2.1 ("Searching an ordered table"), subsection "Algorithm U"}}

===Tìm kiếm mũ{{anchor|One-sided search}}===
{{main|Exponential search}}
[[File:Exponential search.svg|thumb|upright=1.5|Visualization of [[exponential search]]ing finding the upper bound for the subsequent binary search]]
Exponential search extends binary search to unbounded lists. It starts by finding the first element with an index that is both a power of two and greater than the target value. Afterwards, it sets that index as the upper bound, and switches to binary search. A search takes <math display="inline">\lfloor \log_2 x + 1\rfloor</math> iterations before binary search is started and at most <math display="inline">\lfloor \log_2 x \rfloor</math> iterations of the binary search, where <math display="inline">x</math> is the position of the target value. Exponential search works on bounded lists, but becomes an improvement over binary search only if the target value lies near the beginning of the array.{{sfn|Moffat|Turpin|2002|p=33}}

===Tìm kiếm nội suy===
{{main|Tìm kiếm nội suy}}
[[File:Interpolation search.svg|thumb|upright=1.5|Visualization of [[interpolation search]] using linear interpolation. In this case, no searching is needed because the estimate of the target's location within the array is correct. Other implementations may specify another function for estimating the target's location.]]
Instead of calculating the midpoint, interpolation search estimates the position of the target value, taking into account the lowest and highest elements in the array as well as length of the array. It works on the basis that the midpoint is not the best guess in many cases. For example, if the target value is close to the highest element in the array, it is likely to be located near the end of the array.{{Sfn|Knuth|1998|loc=§6.2.1 ("Searching an ordered table"), subsection "Interpolation search"}}

A common interpolation function is [[linear interpolation]]. If <math>A</math> is the array, <math>L, R</math> are the lower and upper bounds respectively, and <math>T</math> is the target, then the target is estimated to be about <math>(T - A_L) / (A_R - A_L)</math> of the way between <math>L</math> and <math>R</math>. When linear interpolation is used, and the distribution of the array elements is uniform or near uniform, interpolation search makes <math display="inline">O(\log \log n)</math> comparisons.{{Sfn|Knuth|1998|loc=§6.2.1 ("Searching an ordered table"), subsection "Interpolation search"}}{{Sfn|Knuth|1998|loc=§6.2.1 ("Searching an ordered table"), subsection "Exercise 22"}}<ref>{{cite journal|last1=Perl|first1=Yehoshua|last2=Itai|first2=Alon|last3=Avni|first3=Haim|title=Interpolation search—a log log ''n'' search|journal=[[Communications of the ACM]]|date=1978|volume=21|issue=7|pages=550–553|doi=10.1145/359545.359557|bibcode=1985CACM...28...22S}}</ref>

In practice, interpolation search is slower than binary search for small arrays, as interpolation search requires extra computation. Its time complexity grows more slowly than binary search, but this only compensates for the extra computation for large arrays.{{Sfn|Knuth|1998|loc=§6.2.1 ("Searching an ordered table"), subsection "Interpolation search"}}

=== Đổ xuống một phần ===
{{Main|Fractional cascading}}
[[File:Fractional cascading.svg|thumb|upright=2.5|In [[fractional cascading]], each array has pointers to every second element of another array, so only one binary search has to be performed to search all the arrays.]]
Fractional cascading is a technique that speeds up binary searches for the same element in multiple sorted arrays. Searching each array separately requires <math display="inline">O(k \log n)</math> time, where <math display="inline">k</math> is the number of arrays. Fractional cascading reduces this to <math display="inline">O(k + \log n)</math> by storing specific information in each array about each element and its position in the other arrays.<ref name="ChazelleLiu2001">{{cite conference|last1=Chazelle|first1=Bernard|last2=Liu|first2=Ding|authorlink1=Bernard Chazelle|title=Lower bounds for intersection searching and fractional cascading in higher dimension|conference=33rd [[Symposium on Theory of Computing|ACM Symposium on Theory of Computing]]|pages=322–329|date=6 July 2001|doi=10.1145/380752.380818|url=https://dl.acm.org/citation.cfm?doid=380752.380818 |accessdate=30 June 2018 |publisher=ACM|isbn=978-1-58113-349-3}}</ref><ref name="ChazelleLiu2004">{{cite journal|last1=Chazelle|first1=Bernard|last2=Liu|first2=Ding|authorlink1=Bernard Chazelle|title=Lower bounds for intersection searching and fractional cascading in higher dimension|journal=Journal of Computer and System Sciences|date=1 March 2004 |volume=68|issue=2|pages=269–284 |language=en |issn=0022-0000|doi=10.1016/j.jcss.2003.07.003|citeseerx=10.1.1.298.7772|url=http://www.cs.princeton.edu/~chazelle/pubs/FClowerbounds.pdf|accessdate=30 June 2018}}</ref>

Fractional cascading was originally developed to efficiently solve various [[computational geometry]] problems. Fractional cascading has been applied elsewhere, such as in [[data mining]] and [[Internet Protocol]] routing.<ref name="ChazelleLiu2001" />

=== Generalization to graphs ===
Binary search has been generalized to work on certain types of graphs, where the target value is stored in a vertex instead of an array element. Binary search trees are one such generalization&mdash;when a vertex (node) in the tree is queried, the algorithm either learns that the vertex is the target, or otherwise which subtree the target would be located in. However, this can be further generalized as follows: given an undirected, positively weighted graph and a target vertex, the algorithm learns upon querying a vertex that it is equal to the target, or it is given an incident edge that is on the shortest path from the queried vertex to the target. The standard binary search algorithm is simply the case where the graph is a path. Similarly, binary search trees are the case where the edges to the left or right subtrees are given when the queried vertex is unequal to the target. For all undirected, positively weighted graphs, there is an algorithm that finds the target vertex in <math>O(\log n)</math> queries in the worst case.<ref>{{cite conference|last1=Emamjomeh-Zadeh|first1=Ehsan|last2=Kempe|first2=David|last3=Singhal|first3=Vikrant|title=Deterministic and probabilistic binary search in graphs|date=2016|pages=519–532|conference=48th [[Symposium on Theory of Computing|ACM Symposium on Theory of Computing]]|arxiv=1503.00805|doi=10.1145/2897518.2897656}}</ref>

=== Noisy binary search ===
[[File:Noisy binary search.svg|thumb|upright=1.5|In noisy binary search, there is a certain probability that a comparison is incorrect.]]
Noisy binary search algorithms solve the case where the algorithm cannot reliably compare elements of the array. For each pair of elements, there is a certain probability that the algorithm makes the wrong comparison. Noisy binary search can find the correct position of the target with a given probability that controls the reliability of the yielded position. Every noisy binary search procedure must make at least <math>(1 - \tau)\frac{\log_2 (n)}{H(p)} - \frac{10}{H(p)}</math> comparisons on average, where <math>H(p) = -p \log_2 (p) - (1 - p) \log_2 (1 - p)</math><!-- Attribution of LaTeX code: see history of https://en.wikipedia.org/wiki/Binary_entropy_function --> is the [[binary entropy function]] and <math>\tau</math> is the probability that the procedure yields the wrong position.<ref>{{cite conference |last1=Ben-Or |first1=Michael |last2=Hassidim |first2=Avinatan |title=The Bayesian learner is optimal for noisy binary search (and pretty good for quantum as well) |date=2008 |book-title=49th [[Annual IEEE Symposium on Foundations of Computer Science|Symposium on Foundations of Computer Science]] |pages=221–230 |doi=10.1109/FOCS.2008.58 |ref=harv |url=http://www2.lns.mit.edu/~avinatan/research/search-full.pdf |isbn=978-0-7695-3436-7}}</ref><ref name="pelc1989">{{cite journal|last1=Pelc|first1=Andrzej|title=Searching with known error probability|journal=Theoretical Computer Science|date=1989|volume=63|issue=2|pages=185–202|doi=10.1016/0304-3975(89)90077-7}}</ref><ref>{{cite conference|last1=Rivest|first1=Ronald L.|last2=Meyer|first2=Albert R.|last3=Kleitman|first3=Daniel J.|last4=Winklmann|first4=K.|authorlink1=Ronald Rivest|authorlink2=Albert R. Meyer|authorlink3=Daniel Kleitman|title=Coping with errors in binary search procedures|conference=10th [[Symposium on Theory of Computing|ACM Symposium on Theory of Computing]]|doi=10.1145/800133.804351}}</ref> The noisy binary search problem can be considered as a case of the [[Ulam's game|Rényi-Ulam game]],<ref>{{cite journal|last1=Pelc|first1=Andrzej|title=Searching games with errors—fifty years of coping with liars|journal=Theoretical Computer Science|date=2002|volume=270|issue=1–2|pages=71–109|doi=10.1016/S0304-3975(01)00303-6}}</ref> a variant of [[Twenty Questions]] where the answers may be wrong.<ref>{{Cite journal | last1=Rényi | first1=Alfréd | title=On a problem in information theory | language=Hungarian | mr=0143666 | year=1961 | journal=Magyar Tudományos Akadémia Matematikai Kutató Intézetének Közleményei| volume=6 | pages=505–516}}</ref>

=== Tìm kiếm nhị phân lượng tử ===
Classical computers are bounded to the worst case of exactly <math display="inline">\lfloor \log_2 n + 1 \rfloor</math> iterations when performing binary search. [[Quantum algorithm]]s for binary search are still bounded to a proportion of <math display="inline">\log_2 n</math> queries (representing iterations of the classical procedure), but the constant factor is less than one, providing for a lower time complexity on [[quantum computing|quantum computers]]. Any ''exact'' quantum binary search procedure—that is, a procedure that always yields the correct result—requires at least <math display="inline">\frac{1}{\pi}(\ln n - 1) \approx 0.22 \log_2 n</math> queries in the worst case, where <math display="inline">\ln</math> is the [[natural logarithm]].<ref>{{cite journal|last1=Høyer|first1=Peter|last2=Neerbek|first2=Jan|last3=Shi|first3=Yaoyun|title=Quantum complexities of ordered searching, sorting, and element distinctness|journal=[[Algorithmica]]|date=2002|volume=34|issue=4|pages=429–448|doi=10.1007/s00453-002-0976-3|ref=harv|arxiv=quant-ph/0102078}}</ref> There is an exact quantum binary search procedure that runs in <math display="inline">4 \log_{605} n \approx 0.433 \log_2 n</math> queries in the worst case.<ref name="quantumalgo">{{cite journal|last1=Childs|first1=Andrew M.|last2=Landahl|first2=Andrew J.|last3=Parrilo|first3=Pablo A.|title=Quantum algorithms for the ordered search problem via semidefinite programming|journal=Physical Review A|date=2007|volume=75|issue=3|at=032335|doi=10.1103/PhysRevA.75.032335|ref=harv|arxiv=quant-ph/0608161|bibcode=2007PhRvA..75c2335C}}</ref> In comparison, [[Grover's algorithm]] is the optimal quantum algorithm for searching an unordered list of elements, and it requires <math>O(\sqrt{n})</math> queries.<ref>{{cite conference |last1=Grover |first1=Lov K. | authorlink=Lov Grover | title=A fast quantum mechanical algorithm for database search | conference=28th [[Symposium on Theory of Computing|ACM Symposium on Theory of Computing]] |pages=212–219|date=1996| location=Philadelphia, PA | doi=10.1145/237814.237866| arxiv=quant-ph/9605043}}</ref>


== Lịch sử ==
== Lịch sử ==

Phiên bản lúc 09:13, ngày 15 tháng 5 năm 2020

Tìm kiếm nhị phân
Minh họa thuật toán tìm kiếm nhị phân, trong đó 7 là giá trị cần tìm
Phân loạiThuật toán tìm kiếm
Cấu trúc dữ liệuMảng
Hiệu suất trường hợp tệ nhấtO(log n)
Hiệu suất trường hợp tốt nhấtO(1)
Hiệu suất trung bìnhO(log n)
Độ phức tạp không gian trường hợp tệ nhấtO(1)
Tối ưu

Trong khoa học máy tính, tìm kiếm nhị phân (tiếng Anh: binary search), còn gọi là tìm kiếm nửa khoảng (half-interval search),[1] tìm kiếm logarit (logarithmic search),[2] hay binary chop,[3] là một thuật toán tìm kiếm xác định vị trí của một giá trị cần tìm trong một mảng đã được sắp xếp.[4][5] Thuật toán tiến hành so sánh giá trị cần tìm với phần tử đứng giữa mảng. Nếu hai giá trị không bằng nhau, phần nửa mảng không chứa giá trị cần tìm sẽ bị bỏ qua và tiếp tục tìm kiếm trên nửa còn lại, một lần nữa lấy phần tử ở giữa và so sánh với giá trị cần tìm, cứ thế lặp lại cho đến khi tìm thấy giá trị đó. Nếu phép tìm kiếm kết thúc khi nửa còn lại trống thì giá trị cần tìm không có trong mảng.

Tìm kiếm nhị phân chạy theo thời gian logarit trong trường hợp tệ nhất, thực hiện bước so sánh, trong đó là số phần tử trong mảng, kí hiệu O lớn, và là phép toán logarit.[6] Thuật toán tìm kiếm nhị phân nhanh hơn so với tìm kiếm tuyến tính, ngoại trừ các trường hợp mảng có kích thước nhỏ. Tuy nhiên, mảng phải được sắp xếp trước khi áp dụng tìm kiếm nhị phân. Một số cấu trúc dữ liệu chuyên dụng được thiết kế cho việc tìm kiếm nhanh, ví dụ như bảng băm, có thể thực hiện tìm hiệu quả hơn tìm kiếm nhị phân. Tuy nhiên, thuật toán này có thể dùng để giải quyết nhiều loại vấn đề hơn, như tìm phần tử nhỏ nhất hoặc lớn nhất tiếp theo trong mảng so với giá trị cho trước, kể cả khi nó không có trong mảng.

Có nhiều biến thể của phép tìm kiếm nhị phân. Cụ thể, phương pháp "đổ xuống một phần" (fractional cascading) giúp tăng tốc tìm kiếm nhị phân với cùng một giá trị trong nhiều mảng. Phương pháp này cho phép giải quyết hiệu quả một số vấn đề về tìm kiếm trong hình học tính toán và nhiều lĩnh vực khác. Thuật toán tìm kiếm mũ (exponential search) mở rộng tìm kiếm nhị phân sang các danh sách không có giới hạn. Các cấu trúc dữ liệu cây tìm kiếm nhị phânB-cây được xây dựng dựa trên tìm kiếm nhị phân.

Thuật toán

Thuật toán tìm kiếm nhị phân hoạt động trên các mảng đã được sắp xếp. Thuật toán bắt đầu bằng việc so sánh một phần tử đứng chính giữa mảng với giá trị cần tìm. Nếu bằng nhau, vị trí của nó trong mảng sẽ được trả về. Nếu giá trị cần tìm nhỏ hơn phần tử này, quá trình tìm kiếm tiếp tục ở nửa nhỏ hơn của mảng. Nếu giá trị cần tìm lớn hơn phần tử ở giữa, quá trình tìm kiếm tiếp tục ở nửa lớn hơn của mảng. Bằng cách này, ở mỗi phép lặp thuật toán có thể loại bỏ nửa mảng mà giá trị cần tìm chắc chắn không xuất hiện.[7]

Quy trình

Cho một mảng phần tử với các giá trị hoặc bản ghi đã được sắp xếp sao cho , và giá trị cần tìm , chương trình con sau đây sử dụng tìm kiếm nhị phân để tìm chỉ số của trong .[7]

  1. Gán với giá trị với giá trị .
  2. Nếu , tìm kiếm kết thúc (không thành công).
  3. Gán (vị trí của phần tử đứng giữa) với giá trị floor của , tức là số nguyên lớn nhất nhỏ hơn hoặc bằng .
  4. Nếu , gán với và quay lại bước 2.
  5. Nếu , gán với và quay lại bước 2.
  6. Khi , quá trình tìm kiếm hoàn tất; trả về .

Quy trình lặp này dùng hai biến để lưu giới hạn tìm kiếm. Quy trình này có thể diễn giải bằng mã giả như dưới đây, trong đó tên các biến và kiểu giữ nguyên so với như trên, floor là hàm floor, và không_thành_công là một giá trị cụ thể thông báo tìm kiếm thất bại.[8]

function binary_search(A, n, T) is
    L := 0
    R := n − 1
    while L ≤ R do
        m := floor((L + R) / 2)
        if A[m] < T then
            L := m + 1
        else if A[m] > T then
            R := m - 1
        else:
            return m
    return không_thành_công

Ngoài ra, thuật toán có thể lấy giá trị ceiling của . Điều này có thể thay đổi kết quả nếu giá trị cần tìm xuất hiện nhiều hơn một lần trong mảng.

Cách làm khác

Trong quy trình trên, thuật toán kiểm tra phần tử ở giữa () có bằng giá trị cần tìm () không ở mỗi phép lặp. Một số cách làm bỏ qua bước này ở mỗi phép lặp. Khi đó thuật toán chỉ kiểm tra điều này khi chỉ còn một phần tử còn lại (khi ). Nhờ đó mà vòng lặp so sánh được thực hiện nhanh hơn do mỗi phép lặp đã loại bỏ được một bước so sánh. Tuy nhiên, với cách làm này thì số phép lặp trung bình tăng lên.[9]

Hermann Bottenbruch cho xuất bản cách áp dụng đầu tiên bỏ qua bước kiểm tra này vào năm 1962.[9][10]

  1. Gán với giá trị với giá trị .
  2. Khi ,
    1. Gán (vị trí của phần tử đứng giữa) với giá trị ceiling của , tức là số nguyên nhỏ nhất lớn hơn hoặc bằng .
    2. Nếu , gán với .
    3. Trường hợp còn lại, ; gán với .
  3. Khi , quá trình tìm kiếm hoàn tất. Nếu , trả về . Nếu không, tìm kiếm thất bại.

Với ceil là hàm ceiling, mã giả cho phiên bản này như sau:

function binary_search_alternative(A, n, T) is
    L := 0
    R := n − 1
    while L != R do
        m := ceil((L + R) / 2)
        if A[m] > T then
            R := m - 1
        else:
            L := m
    if A[L] = T then
        return L
    return không_thành_công
    

Phần tử lặp lại

Quy trình trên có thể trả về bất cứ chỉ số nào có giá trị phần tử bằng giá trị cần tìm, kể cả khi trong mảng có các phần tử xuất hiện lặp lại. Ví dụ, với mảng và giá trị cần tìm là , thuật toán có thể trả về một trong hai kết quả đúng là phần tử thứ 4 (chỉ số là 3) hoặc thứ 5 (chỉ số là 4). Quy trình chuẩn trong trường hợp này sẽ trả về phần tử thứ 4 (chỉ số 3). Thuật toán này không phải lúc nào cũng trả về vị trí đầu tiên xuất hiện giá trị cần tìm (ví dụ với mảng , kết quả trả về vẫn là phần tử thứ 4). Tuy nhiên, có một số trường hợp cần phải tìm phần tử nằm xa nhất về phía bên trái hoặc bên phải mang giá trị cần tìm khi giá trị này xuất hiện lặp lại trong mảng. Trong ví dụ trên, phần tử thứ 4 là phần tử đứng xa nhất về bên trái mang giá trị 4, trong khi phần tử thứ 5 là phần từ đứng xa nhất về bên phải mang giá trị 4. Cách làm thứ hai ở trên sẽ luôn trả về chỉ số của phần tử đứng xa nhất về bên phải nếu tồn tại các phần tử lặp lại.[10]

Quy trình tìm phần tử xa nhất về bên trái

Để tìm phần tử xa nhất về bên trái, ta có thể dùng quy trình sau:[11]

  1. Gán với giá trị với .
  2. Khi ,
  3. Gán (vị trí của phần tử đứng giữa) với giá trị floor của , tức là số nguyên lớn nhất nhỏ hơn hoặc bằng .
    1. Nếu , gán với .
    2. Trường hợp còn lại, ; gán với .
  4. Trả về .

Nếu thì là phần tử đứng xa nhất về bên trái có giá trị bằng . Kể cả khi không nằm trong mảng, hạng của trong mảng, hay số phần tử trong mảng nhỏ hơn .

Với floor là hàm floor, mã giả cho phiên bản này như sau:

function binary_search_leftmost(A, n, T):
    L := 0
    R := n
    while L < R:
        m := floor((L + R) / 2)
        if A[m] < T:
            L := m + 1
        else:
            R := m
    return L

Quy trình tìm phần tử xa nhất về bên phải

Để tìm phần tử xa nhất về bên phải, ta có thể dùng quy trình sau:[11]

  1. Gán với giá trị với .
  2. Khi ,
  3. Gán (vị trí của phần tử đứng giữa) với giá trị floor của , tức là số nguyên lớn nhất nhỏ hơn hoặc bằng .
    1. Nếu , gán với .
    2. Trường hợp còn lại, ; gán với .
  4. Trả về .

Nếu , thì là phần tử đứng xa nhất về phía bên phải có giá trị bằng . Kể cả khi không có trong mảng, sẽ là số phần tử trong mảng lớn hơn .

Với floor là hàm floor, mã giả cho phiên bản này như sau:

function binary_search_rightmost(A, n, T):
    L := 0
    R := n
    while L < R:
        m := floor((L + R) / 2)
        if A[m] > T:
            R := m
        else:
            L := m + 1
    return R - 1

Hiệu suất

Một cây biểu diễn tìm kiếm nhị phân. Mảng được dùng trong ví dụ này là và giá trị cần tìm là .
Trường hợp tệ nhất là khi quá trình tìm kiếm phải đi tới bậc sâu nhất của cây, còn trường hợp tốt nhất là khi giá trị cần tìm chính là phần tử đứng giữa.

Về mặt số phép so sánh, hiệu suất của thuật toán tìm kiếm nhị phân có thể được phân tích bằng cách biểu diễn quá trình chạy thuật toán trên cây nhị phân. Nút gốc của cây là phần tử đứng giữa mảng. Phần tử đứng giữa nửa nhỏ hơn là nút con bên trái của nút gốc, và phần tử đứng giữa nửa lớn hơn là nút con bên phải của nút gốc. Phần còn lại của cây cũng được dựng như vậy. Bắt đầu từ nút gốc, tùy theo giá trị cần tìm nhỏ hơn hay lớn hơn nút đang xét mà thuật toán sẽ duyệt theo cây con bên trái hay bên phải.[6][12]

Trong trường hợp tệ nhất, thuật toán thực hiện lần lặp so sánh, trong đó là ký hiệu của hàm floor cho kết quả là số nguyên lớn nhất nhỏ hơn hoặc bằng giá trị bên trong, và là phép logarit nhị phân. Nguyên nhân là do khi gặp phải trường hợp tệ nhất, thuật toán phải đi tới bậc sâu nhất trong cây, và số bậc trong cây luôn bằng với bất cứ phép tìm kiếm nhị phân nào.

Trường hợp tệ nhất còn có thể xảy ra khi giá trị cần tìm không có trong mảng. Nếu ở dạng thì trường hợp này luôn xảy ra. Nếu không, thuật toán có thể thực hiện phép lặp nếu phải đi tới bậc sâu nhất của cây. Tuy nhiên, nếu thuật toán kết thúc ngay ở bậc sâu thứ hai của cây, quá trình tìm kiếm có thể chỉ cần thực hiện phép lặp, ít hơn trường hợp tệ nhất một phép.[13]

Giả sử mỗi phần tử có xác suất được thực hiện tìm kiếm như nhau, trung bình thuật toán tìm kiếm nhị phân thực hiện phép lặp khi phần tử cần tìm có mặt trong mảng, hay xấp xỉ bằng . Khi phần tử cần tìm không có trong mảng, thuật toán thực hiện trung bình phép lặp, nếu giả sử khoảng giữa và bên ngoài các phần tử đều có xác suất được tìm như nhau.[12]

Trong trường hợp tốt nhất, khi giá trị cần tìm nằm ngay giữa mảng, vị trí của nó được trả về ngay sau một phép lặp.[14]

Về mặt số phép lặp, không có thuật toán tìm kiếm nào chỉ bằng so sánh các phần tử có thể có hiệu suất trung bình hay thậm chí trong trường hợp tệ nhất tốt hơn tìm kiếm nhị phân. Cây so sánh biểu diễn tìm kiếm nhị phân có số bậc ít nhất có thể do mọi bậc phía trên bậc thấp nhất của cây đều được lấp đầy hoàn toàn.[a] Nếu không, thuật toán tìm kiếm có thể loại bỏ một vài phần tử trong mỗi phép lặp, làm tăng số phép lặp cần thực hiện trong trường hợp trung bình và tệ nhất. Các thuật toán tìm kiếm khác dùng cách so sánh đều bị rơi vào trường hợp này, do mặc dù chúng có thể chạy nhanh hơn với một số giá trị cần tìm nhất định, hiệu suất trung bình trên tất cả phần tử vẫn kém hơn tìm kiếm nhị phân. Bằng cách chia mảng thành hai nửa, thuật toán tìm kiếm nhị phân đảm bảo được kích thước của hai mảng con giống nhau nhất có thể.[15]

Độ phức tạp không gian

Thuật toán tìm kiếm nhị phân cần ba con trỏ để trỏ tới các phần tử, bất kể kích thước của mảng; đó có thể là chỉ số các phần tử trong mảng hoặc con trỏ tới địa chỉ trong bộ nhớ. Tuy nhiên, thuật toán cần ít nhất bit để mã hóa một con trỏ tới một phần tử trong mảng có phần tử.[16] Do đó, độ phức tạp không gian của tìm kiếm nhị phân là . Ngoài ra, thuật toán còn cần không gian để lưu trữ mảng.

Trường hợp trung bình

Số phép lặp trung bình được thực hiện bởi thuật toán tùy thuộc vào xác suất mỗi phần tử được thực hiện tìm kiếm. Trường hợp trung bình khi tìm kiếm thành công và không thành công là khác nhau. Ta giả sử mỗi phần tử có xác suất được tìm như nhau nếu tìm kiếm thành công. Nếu tìm kiếm không thành công, ta giả sử các khoảng giữa và ngoài các phần tử có xác suất được tìm giống nhau. Trường hợp trung bình khi tìm kiếm thành công là số phép lặp cần thực hiện để tìm mọi phần tử chính xác một lần, chia cho , số phần tử trong mảng. Trường hợp trung bình khi tìm kiếm không thành công là số phép lặp cần thực hiện để tìm một phần tử trong mọi khoảng chính xác một lần, chia cho khoảng.[12]

Tìm kiếm thành công

Khi biểu diễn bằng cây nhị phân, một lần tìm kiếm thành công có thể được biểu diễn bằng một đường đi từ gốc tới nút cần tìm, gọi là đường đi trong (internal path). Độ dài của một đường đi là số cạnh (đường nối giữa các nút) mà đường đó đi qua. Số phép lặp mà một lần tìm kiếm thực hiện, với điều kiện độ dài của đường đi tương ứng với lần tìm đó là , là tính cả phép lặp ban đầu. Độ dài đường đi trong (internal path length) là tổng độ dài tất cả các đường đi trong. Do chỉ có một đường đi duy nhất từ gốc tới bất cứ nút nào, mỗi đường trong đó biểu diễn cho một phép tìm kiếm phần tử tương ứng. Nếu có phần tử, và độ dài đường trong là , thì số phép lặp trung bình cho một lần tìm kiếm thành công là , trong đó đã cộng thêm một bước lặp ở đầu.[12]

Do tìm kiếm nhị phân là thuật toán tối ưu cho việc tìm kiếm bằng cách so sánh, bài toán được đơn giản hóa thành việc tính độ dài đường đi trong tối thiểu của tất cả cây nhị phân có nút, tức là bằng:[17]

Ví dụ, với một mảng 7 phần tử, phần tử gốc cần một phép lặp, hai phần tử bên dưới gốc cần hai phép lặp, và bốn phần tử dưới nữa gần ba phép lặp. Trong trường hợp này, độ dài đường đi trong là:[17]

Số phép lặp trung bình sẽ là dựa theo công thức với trường hợp trung bình. Tổng có thể rút gọn thành:[15]

Thay vào phương trình :[15]

Với là một số nguyên, phương trình trên tương đương với phương trình trong trường hợp trung bình cho một lần tìm kiếm thành công như đã chỉ ra ở trên.

Tìm kiếm không thành công

Ta có thể biểu diễn lần tìm kiếm không thành công bằng cách thêm vào cây các nút ngoài, tạo thành cây nhị phân mở rộng. Nếu một nút trong, hoặc một nút có trong cây, có ít hơn hai nút con, thì ta sẽ thêm vào các nút con bổ sung, gọi là các nút ngoài, để mỗi nút trong đều có hai nút con. By doing so, an unsuccessful search can be represented as a path to an external node, whose parent is the single element that remains during the last iteration. Đường đi ngoài (external path) là đường đi từ gốc đến một nút ngoài. Độ dài đường đi ngoài (external path length) là tổng độ dài tất cả đường đi ngoài unique. Nếu số phần tử là ( là số nguyên dương), và độ dài đường đi ngoài là , thì số phép lặp trung bình cho một lần tìm kiếm không thành công là , trong đó đã tính một phép lặp lúc đầu. Trong công thức này, ta lấy độ dài đường đi ngoài chia cho chứ không phải vì có đường đi ngoài biểu diễn cho các khoảng giữa và ngoài các phần tử trong mảng.[15]

Tương tự, bài toán này có thể đơn giản hóa thành bài toán tính độ dài đường đi ngoài tối thiếu của tất cả cây nhị phân có nút. Với tất cả các cây nhị phân, độ dài đường đi ngoài bằng độ dài đường đi trong cộng với .[17] Thay vào ta có:[15]

Thay vào công thức tính , ta có thể xác định trường hợp trung bình cho những lần tìm kiếm không thành công:[15]

Hiệu suất của các cách thay thế

Mỗi phép lặp trong cách tìm kiếm nhị phân trên thực hiện một hoặc hai lần so sánh để kiểm tra phần tử ở giữa có bằng với giá trị cần tìm không. Giả sử mỗi phần tử có xác suất được tìm bằng nhau, mỗi phép lặp thực hiện trung bình 1,5 lần so sánh. Một cách làm khác của thuật toán sẽ thực hiện kiểm tra phần tử giữa ở cuối mỗi lần tìm kiếm. Theo cách này, trung bình mỗi phép lặp bỏ qua 0,5 lần so sánh, tiết kiệm thời gian hơn một chút trên mỗi phép lặp ở hầu hết các loại máy tính. Tuy nhiên, cách này lại khiến mỗi lần tìm kiếm luôn phải thực hiện số phép lặp tối đa, trung bình mỗi lần tìm cộng thêm một phép lặp. Vì vòng lặp so sánh chỉ được thực hiện lần trong trường hợp tệ nhất, the slight increase in efficiency per iteration does not compensate for the extra iteration for all but very large .[b][19][20]

Thời gian chạy và mức sử dụng cache

Trong quá trình phân tích hiệu suất của thuật toán, một vấn đề khác cần lưu ý là thời gian cần để so sánh hai phần tử. Với các số nguyên và chuỗi ký tự, khoảng thời gian này tăng tuyến tính do độ dài mã hóa (thường là số bit) của các phần tử tăng theo. Ví dụ, một cặp số nguyên dương 64 bit sẽ cần phải so sánh số bit gấp đôi so với một cặp số nguyên dương 32 bit. Trường hợp tệ nhất được đạt đến khi hai số nguyên bằng nhau. Điều này có thể đáng lưu tâm khi độ dài mã hóa của các phần tử ở mức lớn, ví dụ như khi so sánh các số thuộc kiểu nguyên lớn hơn hay so sánh các chuỗi ký tự dài, khiến cho việc so sánh rất tốn thời gian. Hơn nữa, so với các số nguyên hoặc chuỗi ký tự ngắn thì so sánh các giá trị dấu phẩy động (cách biểu diễn số thông dụng nhất của số thực) thường còn tốn thời gian hơn..

Trên hầu hết các kiểu kiến trúc máy tính, bộ vi xử lý có bộ nhớ cache riêng tách biệt khỏi RAM. Do nằm ngay trong bộ xử lý, bộ nhớ cache có thể truy cập nhanh hơn nhưng thường chỉ chứa được ít dữ liệu hơn RAM. Do đó, hầu hết các bộ xử lý lưu địa chỉ những vùng bộ nhớ đã được truy cập gần đây và cả những vùng bộ nhớ xung quanh chúng. Ví dụ, khi truy cập vào một phần tử trong mảng, phần tử này, cùng với cả những phần tử được lưu gần đó, có thể được lưu vào trong RAM để sau đó có thể truy cập nhanh hơn được vào các phần tử khác liền kề (tính địa phương trong truy xuất - locality of reference). Với một mảng đã sắp xếp, quá trình tìm kiếm nhị phân có thể phải nhảy sang các vùng bộ nhớ nằm xa nếu mảng có kích thước lớn, không giống như các thuật toán (như tìm kiếm tuyến tính hay dò tuyến tính trong bảng băm) truy cập các phần tử theo thứ tự lần lượt. Điều này làm thời gian chạy của thuật toán tăng lên đôi chút với các mảng lớn trên hầu hết các hệ thống.[21]

Tìm kiếm nhị phân với các hệ thống khác

Áp dụng tìm kiếm nhị phân với các mảng đã được sắp xếp là một giải pháp rất không hiệu quả khi xen giữa các phép chèn và xóa còn là các phép truy hồi, mỗi thao tác đó phải mất thời gian để thực hiện. Ngoài ra, yêu cầu phải sắp xếp trước các mảng có thể làm phức tạp việc sử dụng bộ nhớ, đặc biệt là khi các phần tử thường được chèn vào trong mảng.[22] Một số cấu trúc dữ liệu khác có thể hỗ trợ các thao tác chèn và xóa hiệu quả hơn nhiều. Tìm kiếm nhị phân có thể được dùng để thực hiện so khớp chính xác và thao tác quan hệ tập hợp (xác định giá trị cần tìm có phải là một tập hợp nhiều giá trị không). Hai bài toán này cũng có thể được thực hiện nhanh hơn trên các cấu trúc dữ liệu khác. Tuy nhiên, khác với các hệ thống tìm kiếm khác, tìm kiếm nhị phân hiệu quả hơn trong phép so khớp xấp xỉ, thường chỉ thực hiện trong thời gian bất kể kiểu hay cấu trúc của các giá trị.[23] Ngoài ra còn có một số phép toán, như tìm phần tử nhỏ nhất và lớn nhất, có thể được thực hiện hiệu quả trên mảng đã được sắp xếp.[24]

Tìm kiếm tuyến tính

Tìm kiếm tuyến tính là một thuật toán tìm kiếm đơn giản: nó kiểm tra mọi bản ghi cho tới khi tìm thấy giá trị cần tìm. Tìm kiếm tuyến tính có thể được thực hiện trên danh sách liên kết, cho phép các thao tác chèn và xóa nhanh hơn so với mảng. Tìm kiếm nhị phân nhanh hơn tìm kiếm tuyến tính trên các mảng đã được sắp xếp trừ khi mảng có kích thước nhỏ, mặc dù trước khi tìm kiếm cần phải thực hiện sắp xếp lại mảng trước.[c][26] Tất cả các thuật toán sắp xếp dựa trên thao tác so sánh các phần tử, như sắp xếp nhanhsắp xếp trộn, cần ít nhất phép so sánh trong trường hợp tệ nhất.[27] Khác với tìm kiếm tuyến tính, tìm kiếm nhị phân có thể được ứng dụng để tìm so khớp xấp xỉ một cách hiệu quả. Một số thao tác như tìm phần tử nhỏ nhất và lớn nhất có thể được thực hiện hiệu quả chỉ khi mảng đã được sắp xếp.[28]

Cây

Phép tìm kiếm trên cây tìm kiếm nhị phân sử dụng một thuật toán tương tự tìm kiếm nhị phân.

Cây tìm kiếm nhị phân là một cấu trúc dữ liệu dạng cây nhị phân hoạt động dựa trên nguyên lý của tìm kiếm nhị phân. Các bản ghi trong cây được biểu diễn theo thứ tự đã được sắp xếp, và mỗi bản ghi có thể được tìm bằng một thuật toán tương tự tìm kiếm nhị phân, thực hiện trung bình theo thời gian logarit. Các phép chèn và xóa cũng cần trung bình thời gian logarit trong cây tìm kiếm nhị phân. Chúng có thể được thực hiện nhanh hơn so với các thao tác chèn và xóa theo thời gian tuyến tính trên các mảng đã được sắp xếp, đồng thời các thao tác có thể thực hiện trên mảng đã được sắp xếp cũng có thể được thực hiện trên cây nhị phân, bao gồm cả các phép truy vấn khoảng và truy vấn xấp xỉ.[23][29]

Tuy nhiên, tìm kiếm nhị phân thường hiệu quả hơn trong việc tìm kiếm, do các cây nhị phân hầu như đều không cân bằng tuyệt đối, khiến hiệu suất kém hơn đôi chút so với tìm kiếm nhị phân. Điều này thậm chí vẫn áp dụng với các cây tìm kiếm nhị phân tự cân bằng, vì chúng hiếm khi tạo ra cây có số bậc nhỏ nhất có thể. Except for balanced binary search trees, the tree may be severely imbalanced with few internal nodes with two children, resulting in the average and worst-case search time approaching comparisons.[d] Cây tìm kiếm nhị phân cần nhiều không gian hơn so với mảng đã được sắp xếp.[31]

Cây tìm kiếm nhị phân hữu ích nhất cho việc tìm kiềm nhanh trong các ổ cứng ngoài, bởi cây tìm kiếm nhị phân có thể được dựng hiệu quả trong các hệ thống tập tin. Cấu trúc B-cây tổng quát hóa phương pháp tổ chức cây này. B-cây thường được dùng để tổ chức các ổ lưu trữ dài hạn như cơ sở dữ liệuhệ thống tập tin.[32][33]

Băm

For implementing associative arrays, hash tables, a data structure that maps keys to records using a hash function, are generally faster than binary search on a sorted array of records.[34] Most hash table implementations require only amortized constant time on average.[e][36] However, hashing is not useful for approximate matches, such as computing the next-smallest, next-largest, and nearest key, as the only information given on a failed search is that the target is not present in any record.[37] Binary search is ideal for such matches, performing them in logarithmic time. Binary search also supports approximate matches. Some operations, like finding the smallest and largest element, can be done efficiently on sorted arrays but not on hash tables.[23]

Các thuật toán quan hệ tập hợp

Một vấn đề liên quan tới tìm kiếm là quan hệ tập hợp. Bất cứ thuật toán nào thực hiện tra cứu, như tìm kiếm nhị phân, cũng có thể dùng cho quan hệ tập hợp. Một số thuật toán khác được thiết kế phù hợp hơn với quan hệ tập hợp. Mảng bit là cách làm đơn giản nhất, thường được dùng khi số khóa bị giới hạn. It compactly stores a collection of bits, with each bit representing a single key within the range of keys. Bit arrays are very fast, requiring only time.[38] The Judy1 type of Judy array handles 64-bit keys efficiently.[39]

For approximate results, Bloom filters, another probabilistic data structure based on hashing, store a set of keys by encoding the keys using a bit array and multiple hash functions. Bloom filters are much more space-efficient than bit arrays in most cases and not much slower: with hash functions, membership queries require only time. However, Bloom filters suffer from false positives.[f][g][41]

Các cấu trúc dữ liệu khác

Còn có các cấu trúc dữ liệu khác có thể cải thiện tìm kiếm nhị phân trong một số trường hợp với cả mục đích tìm kiếm lẫn các thao tác khác có thể được thực hiện trên các mảng đã được sắp xếp. Ví dụ, các phép tìm kiếm, so khớp xấp xỉ và các thao tác khác trên mảng đã được sắp xếp có thể được thực hiện hiệu quả hơn tìm kiếm nhị phân nếu áp dụng trên các cấu trúc dữ liệu chuyên biệt như cây van Emde Boas, fusion trees, triemảng bit. Các cấu trúc dữ liệu chuyên biệt này thường chỉ nhanh hơn vì chúng sử dụng các tính chất của những khóa có điều kiện nhất định (thông thường những khóa này là các số nguyên nhỏ), và do đó thời gian chạy hoặc không gian cần sử dụng có thể lớn nếu không đáp ứng được các điều kiện ấy.[23] As long as the keys can be ordered, these operations can always be done at least efficiently on a sorted array regardless of the keys. Some structures, such as Judy arrays, use a combination of approaches to mitigate this while retaining efficiency and the ability to perform approximate matching.[39]

Biến thể

Uniform binary search

Uniform binary search stores the difference between the current and the two next possible middle elements instead of specific bounds.

Uniform binary search stores, instead of the lower and upper bounds, the difference in the index of the middle element from the current iteration to the next iteration. A lookup table containing the differences is computed beforehand. For example, if the array to be searched is [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], the middle element () would be 6. In this case, the middle element of the left subarray ([1, 2, 3, 4, 5]) is 3 and the middle element of the right subarray ([7, 8, 9, 10, 11]) is 9. Uniform binary search would store the value of 3 as both indices differ from 6 by this same amount.[42] To reduce the search space, the algorithm either adds or subtracts this change from the index of the middle element. Uniform binary search may be faster on systems where it is inefficient to calculate the midpoint, such as on decimal computers.[43]

Tìm kiếm mũ

Visualization of exponential searching finding the upper bound for the subsequent binary search

Exponential search extends binary search to unbounded lists. It starts by finding the first element with an index that is both a power of two and greater than the target value. Afterwards, it sets that index as the upper bound, and switches to binary search. A search takes iterations before binary search is started and at most iterations of the binary search, where is the position of the target value. Exponential search works on bounded lists, but becomes an improvement over binary search only if the target value lies near the beginning of the array.[44]

Tìm kiếm nội suy

Visualization of interpolation search using linear interpolation. In this case, no searching is needed because the estimate of the target's location within the array is correct. Other implementations may specify another function for estimating the target's location.

Instead of calculating the midpoint, interpolation search estimates the position of the target value, taking into account the lowest and highest elements in the array as well as length of the array. It works on the basis that the midpoint is not the best guess in many cases. For example, if the target value is close to the highest element in the array, it is likely to be located near the end of the array.[45]

A common interpolation function is linear interpolation. If is the array, are the lower and upper bounds respectively, and is the target, then the target is estimated to be about of the way between and . When linear interpolation is used, and the distribution of the array elements is uniform or near uniform, interpolation search makes comparisons.[45][46][47]

In practice, interpolation search is slower than binary search for small arrays, as interpolation search requires extra computation. Its time complexity grows more slowly than binary search, but this only compensates for the extra computation for large arrays.[45]

Đổ xuống một phần

In fractional cascading, each array has pointers to every second element of another array, so only one binary search has to be performed to search all the arrays.

Fractional cascading is a technique that speeds up binary searches for the same element in multiple sorted arrays. Searching each array separately requires time, where is the number of arrays. Fractional cascading reduces this to by storing specific information in each array about each element and its position in the other arrays.[48][49]

Fractional cascading was originally developed to efficiently solve various computational geometry problems. Fractional cascading has been applied elsewhere, such as in data mining and Internet Protocol routing.[48]

Generalization to graphs

Binary search has been generalized to work on certain types of graphs, where the target value is stored in a vertex instead of an array element. Binary search trees are one such generalization—when a vertex (node) in the tree is queried, the algorithm either learns that the vertex is the target, or otherwise which subtree the target would be located in. However, this can be further generalized as follows: given an undirected, positively weighted graph and a target vertex, the algorithm learns upon querying a vertex that it is equal to the target, or it is given an incident edge that is on the shortest path from the queried vertex to the target. The standard binary search algorithm is simply the case where the graph is a path. Similarly, binary search trees are the case where the edges to the left or right subtrees are given when the queried vertex is unequal to the target. For all undirected, positively weighted graphs, there is an algorithm that finds the target vertex in queries in the worst case.[50]

Noisy binary search

In noisy binary search, there is a certain probability that a comparison is incorrect.

Noisy binary search algorithms solve the case where the algorithm cannot reliably compare elements of the array. For each pair of elements, there is a certain probability that the algorithm makes the wrong comparison. Noisy binary search can find the correct position of the target with a given probability that controls the reliability of the yielded position. Every noisy binary search procedure must make at least comparisons on average, where is the binary entropy function and is the probability that the procedure yields the wrong position.[51][52][53] The noisy binary search problem can be considered as a case of the Rényi-Ulam game,[54] a variant of Twenty Questions where the answers may be wrong.[55]

Tìm kiếm nhị phân lượng tử

Classical computers are bounded to the worst case of exactly iterations when performing binary search. Quantum algorithms for binary search are still bounded to a proportion of queries (representing iterations of the classical procedure), but the constant factor is less than one, providing for a lower time complexity on quantum computers. Any exact quantum binary search procedure—that is, a procedure that always yields the correct result—requires at least queries in the worst case, where is the natural logarithm.[56] There is an exact quantum binary search procedure that runs in queries in the worst case.[57] In comparison, Grover's algorithm is the optimal quantum algorithm for searching an unordered list of elements, and it requires queries.[58]

Lịch sử

Ý tưởng về việc sắp xếp một danh sách để tìm kiếm được nhanh hơn đã có từ thời cổ xưa. Ví dụ được biết tới sớm nhất là tấm bảng của Inakibit-Anu từ thời Babylon vào k. 200 TCN. Tấm bảng này chứa khoảng 500 con số ở hệ thập lục phân và các số nghịch đảo của chúng được sắp xếp theo thứ tự từ điển, khiến việc tìm kiếm dễ dàng hơn. Ngoài ra trên Quần đảo Aegean còn phát hiện được một vài danh sách những cái tên được sắp xếp theo chữ cái đầu tiên. Catholicon, một cuốn từ điển Latin được hoàn thành vào năm 1286 SCN, là tác phẩm đầu tiên mô tả các quy tắc sắp xếp các từ theo thứ tự bảng chữ cái, thay vì chỉ theo vài ký tự đầu tiên.[10]

Vào năm 1946, John Mauchly là người đầu tiên nhắc tới tìm kiếm nhị phân trong Moore School Lectures, một khóa học về máy tính tại Trường Kỹ thuật Điện tử Moore, Đại học Pennsylvania.[59] Năm 1957, William Wesley Peterson xuất bản phương pháp đầu tiên cho giải thuật tìm kiếm nội suy.[10][60] Mọi thuật toán tìm kiếm nhị phân đã được xuất bản chỉ hoạt động với các mảng có độ dài ở dạng [h] cho tới năm 1960, khi Derrick Henry Lehmer cho xuất bản một thuật toán tìm kiếm nhị phân có thể hoạt động với mọi mảng.[62] Năm 1962, Hermann Bottenbruch trình bày thuật toán tìm kiếm nhị phân được áp dụng bằng ngôn ngữ ALGOL 60, trong đó bước kiểm tra phần tử giữa được đưa xuống cuối, làm tăng số phép lặp trung bình thêm một, nhưng giảm được một bước so sánh mỗi phép lặp.[9] Thuật toán uniform binary search được phát triển bởi A. K. Chandra tại Đại học Stanford vào năm 1971.[10] Năm 1986, Bernard ChazelleLeonidas J. Guibas giới thiệu phương pháp đổ xuống một phần (fractional cascading) nhằm giải quyết nhiều vấn đề về tìm kiếm trong hình học tính toán.[48][63][64]

Thư viện hỗ trợ

Nhiều thư viện chuẩn của các ngôn ngữ có các chương trình con thực hiện tìm kiếm nhị phân:

  • C cung cấp hàm bsearch() trong thư viện chuẩn, which is typically implemented via binary search, although the official standard does not require it so.[65]
  • Standard Template Library của C++ cung cấp các hàm binary_search(), lower_bound(), upper_bound()equal_range().[66]
  • COBOL cung cấp động từ SEARCH ALL có chức năng thực hiện tìm kiếm nhị phân trên các bảng được sắp xếp trong COBOL.[67]
  • Gói thư viện chuẩn sort của Go cung cấp các hàm Search, SearchInts, SearchFloat64s, và SearchStrings, lần lượt có chức năng thực hiện tìm kiếm nhị phân tổng quát, tìm kiếm các lát số nguyên, số thập phân dấu phẩy động và chuỗi ký tự.[68]
  • Java cung cấp một bộ các phương thức static chồng binarySearch() trong các lớp ArraysCollections chứa trong gói java.util chuẩn, lần lượt có chức năng thực hiện tìm kiếm nhị phân trên các mảng trong Java và trên các List.[69][70]
  • Microsoft's .NET Framework 2.0 offers static generic versions of the binary search algorithm in its collection base classes. An example would be System.Array's method BinarySearch<T>(T[] array, T value).[71]
  • Với Objective-C, framework Cocoa cung cấp phương thức NSArray -indexOfObject:inSortedRange:options:usingComparator: trong Mac OS X từ bản 10.6 trở lên.[72] Framework Core Foundation C của Apple cũng chứa hàm CFArrayBSearchValues().[73]
  • Python cung cấp mô đun bisect.[74]
  • Lớp Array của Ruby có chứa phương thức bsearch có cả chức năng so khớp xấp xỉ.[75]

Ghi chú và tham khảo

Ghi chú

  1. ^ Bất cứ thuật toán tìm kiếm nào chỉ dựa trên các phép so sánh có thể được biểu diễn bằng cây so sánh nhị phân. Đường đi trong (internal path) là bất kỳ đường nào đi từ gốc tới một nút có sẵn. Gọi độ dài đường đi trong (internal path length), hay tổng độ dài tất cả các đường đi trong của cây. Nếu mỗi phần tử có xác suất được tìm như nhau, trường hợp trung bình là , hoặc đơn giản hơn là một cộng với trung bình toàn bộ độ dài các đường đi trong trong cây. Nguyên nhân là do các đường đi trong biểu diễn các phần tử mà thuật toán tìm kiếm tiến hành so sánh với giá trị cần tìm. Độ dài của các đường này biểu diễn số phép lặp sau nút gốc. Làm phép cộng trung bình tổng độ dài các đường này với thêm một phép lặp nữa ở nút gốc sẽ ra trường hợp trung bình. Do đó, để giảm tối đa số phép so sánh trung bình, cần phải giảm độ dài đường đi trong . Thực chất cây biểu diễn tìm kiếm nhị phân đã giảm tối đa độ dài đường đi trong. Knuth 1998 chứng minh được rằng độ dài đường đi ngoài (external path - the path length over all nodes where both children are present for each already-existing node) được giảm tối đa khi các nút ngoài (các nút không có nút con) nằm trong hai bậc liên tiếp của cây. Điều này cũng áp dụng với các đường đi trong do độ dài đường đi trong có liên hệ tuyến tính với độ dài đường đi ngoài . Với mỗi cây có nút, . Khi mỗi cây con có số nút bằng nhau, tương đương với khi mảng được chia thành hai nửa ở mỗi phép lặp, các nút ngoài và các nút cha phía trong của chúng nằm trong vòng hai bậc. Như vậy, thuật toán tìm kiếm nhị phân đã giảm được tối đa số phép so sánh trung bình do cây so sánh của thuật toán này có độ dài đường trong nhỏ nhất.[12]
  2. ^ Knuth 1998 cho thấy trên mẫu máy tính MIX của ông, được Knuth thiết kế để biểu diễn cho một chiếc máy tính thông thường, rằng thời gian chạy trung bình của cách làm này cho một lần tìm thành công là đơn vị thời gian so với đơn vị với tìm kiếm nhị phân thông thường. Độ phức tạp thời gian của cách làm này tăng nhẹ ở tốc độ chậm hơn, nhưng độ phức tạp ban đầu lớn hơn. [18]
  3. ^ Knuth 1998 đã thực hiện phân tích hiệu suất thời gian của cả hai thuật toán tìm kiếm này. Trên chiếc máy tính MIX của Knuth, được ông thiết kế để biểu diễn cho một chiếc máy tính thông thường, tìm kiếm nhị phân trung bình mất đơn vị thời gian cho một lần tìm kiếm thành công, trong khi tìm kiếm tuyến tính khi có một sentinel node ở cuối danh sách mất đơn vị. Tìm kiếm tuyến tính có độ phức tạp ban đầu thấp hơn do nó chỉ yêu cầu các tính toán tối thiểu, nhưng sau đó nhanh chóng vượt qua tìm kiếm nhị phân về độ phức tạp. Trên máy tính MIX, binary search only outperforms linear search with a sentinel if .[12][25]
  4. ^ Inserting the values in sorted order or in an alternating lowest-highest key pattern will result in a binary search tree that maximizes the average and worst-case search time.[30]
  5. ^ It is possible to search some hash table implementations in guaranteed constant time.[35]
  6. ^ This is because simply setting all of the bits which the hash functions point to for a specific key can affect queries for other keys which have a common hash location for one or more of the functions.[40]
  7. ^ There exist improvements of the Bloom filter which improve on its complexity or support deletion; for example, the cuckoo filter exploits cuckoo hashing to gain these advantages.[40]
  8. ^ Tức là các mảng có độ dài 1, 3, 7, 15, 31 ...[61]

Chú thích

  1. ^ Williams, Jr., Louis F. (22 tháng 4 năm 1976). A modification to the half-interval search (binary search) method. Proceedings of the 14th ACM Southeast Conference. ACM. tr. 95–101. doi:10.1145/503561.503582. Lưu trữ bản gốc 12 Tháng Ba năm 2017. Truy cập 29 Tháng sáu năm 2018.
  2. ^ Knuth 1998, §6.2.1 ("Searching an ordered table"), subsection "Binary search".
  3. ^ Butterfield & Ngondi 2016, tr. 46.
  4. ^ Cormen và đồng nghiệp 2009, tr. 39.
  5. ^ Weisstein, Eric W., "Binary search" từ MathWorld.
  6. ^ a b Flores, Ivan; Madpis, George (1 tháng 9 năm 1971). “Average binary search length for dense ordered lists”. Communications of the ACM. 14 (9): 602–603. Bibcode:1985CACM...28...22S. doi:10.1145/362663.362752. ISSN 0001-0782.
  7. ^ a b Knuth 1998, §6.2.1 ("Searching an ordered table"), phân mục "Algorithm B".
  8. ^ Knuth 1998, §6.2.1 ("Searching an ordered table"), subsection "Algorithm B".
  9. ^ a b c Bottenbruch, Hermann (1 tháng 4 năm 1962). “Structure and use of ALGOL 60”. Journal of the ACM. 9 (2): 161–221. doi:10.1145/321119.321120. ISSN 0004-5411.Quản lý CS1: ref=harv (liên kết) Procedure is described at p. 214 (§43), titled "Program for Binary Search".
  10. ^ a b c d e Knuth 1998, §6.2.1 ("Searching an ordered table"), phân mục "History and bibliography".
  11. ^ a b Kasahara & Morishita 2006, tr. 8–9.
  12. ^ a b c d e f Knuth 1998, §6.2.1 ("Searching an ordered table"), phân mục "Further analysis of binary search".
  13. ^ Knuth 1998, §6.2.1 ("Searching an ordered table"), "Theorem B".
  14. ^ Chang 2003, tr. 169.
  15. ^ a b c d e f Knuth 1998, §6.2.1 ("Searching an ordered table"), subsection "Further analysis of binary search".
  16. ^ Shannon, Claude E. (tháng 7 năm 1948). “A Mathematical Theory of Communication”. Bell System Technical Journal. 27 (3): 379–423. doi:10.1002/j.1538-7305.1948.tb01338.x. hdl:11858/00-001M-0000-002C-4314-2.
  17. ^ a b c Knuth 1997, §2.3.4.5 ("Path length").
  18. ^ Knuth 1998, §6.2.1 ("Searching an ordered table"), phân mục "Exercise 23".
  19. ^ Knuth 1998, §6.2.1 ("Searching an ordered table"), subsection "Exercise 23".
  20. ^ Rolfe, Timothy J. (1997). “Analytic derivation of comparisons in binary search”. ACM SIGNUM Newsletter. 32 (4): 15–19. doi:10.1145/289251.289255.
  21. ^ Khuong, Paul-Virak; Morin, Pat. “Array Layouts for Comparison-Based Searching”. Journal of Experimental Algorithmics. 22. Article 1.3. doi:10.1145/289251.289255.
  22. ^ Knuth 1997, §2.2.2 ("Sequential Allocation").
  23. ^ a b c d Beame, Paul; Fich, Faith E. (2001). “Optimal bounds for the predecessor problem and related problems”. Journal of Computer and System Sciences. 65 (1): 38–72. doi:10.1006/jcss.2002.1822.
  24. ^ Sedgewick & Wayne 2011, §3.1, subsection "Rank and selection".
  25. ^ Knuth 1998, Answers to Exercises (§6.2.1) cho "Exercise 5".
  26. ^ Knuth 1998, §6.2.1 ("Searching an ordered table").
  27. ^ Knuth 1998, §5.3.1 ("Minimum-Comparison sorting").
  28. ^ Sedgewick & Wayne 2011, §3.2 ("Ordered symbol tables").
  29. ^ Sedgewick & Wayne 2011, §3.2 ("Binary Search Trees"), subsection "Order-based methods and deletion".
  30. ^ Knuth 1998, §6.2.2 ("Binary tree searching"), subsection "But what about the worst case?".
  31. ^ Sedgewick & Wayne 2011, §3.5 ("Applications"), "Which symbol-table implementation should I use?".
  32. ^ Knuth 1998, §5.4.9 ("Disks and Drums").
  33. ^ Knuth 1998, §6.2.4 ("Multiway trees").
  34. ^ Knuth 1998, §6.4 ("Hashing").
  35. ^ Knuth 1998, §6.4 ("Hashing"), subsection "History".
  36. ^ Dietzfelbinger, Martin; Karlin, Anna; Mehlhorn, Kurt; Meyer auf der Heide, Friedhelm; Rohnert, Hans; Tarjan, Robert E. (tháng 8 năm 1994). “Dynamic perfect hashing: upper and lower bounds”. SIAM Journal on Computing. 23 (4): 738–761. doi:10.1137/S0097539791194094.
  37. ^ Morin, Pat. “Hash tables” (PDF). tr. 1. Truy cập ngày 28 tháng 3 năm 2016.
  38. ^ Knuth 2011, §7.1.3 ("Bitwise Tricks and Techniques").
  39. ^ a b Silverstein, Alan, Judy IV shop manual (PDF), Hewlett-Packard, tr. 80–81
  40. ^ a b Fan, Bin; Andersen, Dave G.; Kaminsky, Michael; Mitzenmacher, Michael D. (2014). Cuckoo filter: practically better than Bloom. Proceedings of the 10th ACM International on Conference on Emerging Networking Experiments and Technologies. tr. 75–88. doi:10.1145/2674005.2674994.
  41. ^ Bloom, Burton H. (1970). “Space/time trade-offs in hash coding with allowable errors”. Communications of the ACM. 13 (7): 422–426. Bibcode:1985CACM...28...22S. CiteSeerX 10.1.1.641.9096. doi:10.1145/362686.362692.
  42. ^ Knuth 1998, §6.2.1 ("Searching an ordered table"), subsection "An important variation".
  43. ^ Knuth 1998, §6.2.1 ("Searching an ordered table"), subsection "Algorithm U".
  44. ^ Moffat & Turpin 2002, tr. 33.
  45. ^ a b c Knuth 1998, §6.2.1 ("Searching an ordered table"), subsection "Interpolation search".
  46. ^ Knuth 1998, §6.2.1 ("Searching an ordered table"), subsection "Exercise 22".
  47. ^ Perl, Yehoshua; Itai, Alon; Avni, Haim (1978). “Interpolation search—a log log n search”. Communications of the ACM. 21 (7): 550–553. Bibcode:1985CACM...28...22S. doi:10.1145/359545.359557.
  48. ^ a b c Chazelle, Bernard; Liu, Ding (6 tháng 7 năm 2001). Lower bounds for intersection searching and fractional cascading in higher dimension. 33rd ACM Symposium on Theory of Computing. ACM. tr. 322–329. doi:10.1145/380752.380818. ISBN 978-1-58113-349-3. Truy cập ngày 30 tháng 6 năm 2018.
  49. ^ Chazelle, Bernard; Liu, Ding (1 tháng 3 năm 2004). “Lower bounds for intersection searching and fractional cascading in higher dimension” (PDF). Journal of Computer and System Sciences (bằng tiếng Anh). 68 (2): 269–284. CiteSeerX 10.1.1.298.7772. doi:10.1016/j.jcss.2003.07.003. ISSN 0022-0000. Truy cập ngày 30 tháng 6 năm 2018.
  50. ^ Emamjomeh-Zadeh, Ehsan; Kempe, David; Singhal, Vikrant (2016). Deterministic and probabilistic binary search in graphs. 48th ACM Symposium on Theory of Computing. tr. 519–532. arXiv:1503.00805. doi:10.1145/2897518.2897656.
  51. ^ Ben-Or, Michael; Hassidim, Avinatan (2008). “The Bayesian learner is optimal for noisy binary search (and pretty good for quantum as well)” (PDF). 49th Symposium on Foundations of Computer Science. tr. 221–230. doi:10.1109/FOCS.2008.58. ISBN 978-0-7695-3436-7.Quản lý CS1: ref=harv (liên kết)
  52. ^ Pelc, Andrzej (1989). “Searching with known error probability”. Theoretical Computer Science. 63 (2): 185–202. doi:10.1016/0304-3975(89)90077-7.
  53. ^ Rivest, Ronald L.; Meyer, Albert R.; Kleitman, Daniel J.; Winklmann, K. Coping with errors in binary search procedures. 10th ACM Symposium on Theory of Computing. doi:10.1145/800133.804351.
  54. ^ Pelc, Andrzej (2002). “Searching games with errors—fifty years of coping with liars”. Theoretical Computer Science. 270 (1–2): 71–109. doi:10.1016/S0304-3975(01)00303-6.
  55. ^ Rényi, Alfréd (1961). “On a problem in information theory”. Magyar Tudományos Akadémia Matematikai Kutató Intézetének Közleményei (bằng tiếng Hungarian). 6: 505–516. MR 0143666.Quản lý CS1: ngôn ngữ không rõ (liên kết)
  56. ^ Høyer, Peter; Neerbek, Jan; Shi, Yaoyun (2002). “Quantum complexities of ordered searching, sorting, and element distinctness”. Algorithmica. 34 (4): 429–448. arXiv:quant-ph/0102078. doi:10.1007/s00453-002-0976-3.Quản lý CS1: ref=harv (liên kết)
  57. ^ Childs, Andrew M.; Landahl, Andrew J.; Parrilo, Pablo A. (2007). “Quantum algorithms for the ordered search problem via semidefinite programming”. Physical Review A. 75 (3). 032335. arXiv:quant-ph/0608161. Bibcode:2007PhRvA..75c2335C. doi:10.1103/PhysRevA.75.032335.Quản lý CS1: ref=harv (liên kết)
  58. ^ Grover, Lov K. (1996). A fast quantum mechanical algorithm for database search. 28th ACM Symposium on Theory of Computing. Philadelphia, PA. tr. 212–219. arXiv:quant-ph/9605043. doi:10.1145/237814.237866.
  59. ^ Knuth 1998, §6.2.1 ("Searching an ordered table"), subsection "History and bibliography".
  60. ^ Peterson, William Wesley (1957). “Addressing for random-access storage”. IBM Journal of Research and Development. 1 (2): 130–146. doi:10.1147/rd.12.0130.
  61. ^ "2n−1". OEIS A000225 Lưu trữ 8 tháng 6 2016 tại Wayback Machine. Truy cập ngày 7 tháng 5 năm 2016.
  62. ^ Lehmer, Derrick (1960). Teaching combinatorial tricks to a computer. Proceedings of Symposia in Applied Mathematics. 10. tr. 180–181. doi:10.1090/psapm/010.
  63. ^ Chazelle, Bernard; Guibas, Leonidas J. (1986). “Fractional cascading: I. A data structuring technique” (PDF). Algorithmica. 1 (1–4): 133–162. CiteSeerX 10.1.1.117.8349. doi:10.1007/BF01840440.
  64. ^ Chazelle, Bernard; Guibas, Leonidas J. (1986), “Fractional cascading: II. Applications” (PDF), Algorithmica, 1 (1–4): 163–191, doi:10.1007/BF01840441
  65. ^ “bsearch – binary search a sorted table”. The Open Group Base Specifications (ấn bản 7). The Open Group. 2013. Lưu trữ bản gốc 21 Tháng Ba năm 2016. Truy cập 28 Tháng Ba năm 2016.
  66. ^ Stroustrup 2013, tr. 945.
  67. ^ Unisys (2012), COBOL ANSI-85 programming reference manual, 1, tr. 598–601
  68. ^ “Package sort”. The Go Programming Language. Lưu trữ bản gốc 25 Tháng tư năm 2016. Truy cập 28 Tháng tư năm 2016.
  69. ^ “java.util.Arrays”. Java Platform Standard Edition 8 Documentation. Oracle Corporation. Lưu trữ bản gốc 29 Tháng tư năm 2016. Truy cập 1 tháng Năm năm 2016.
  70. ^ “java.util.Collections”. Java Platform Standard Edition 8 Documentation. Oracle Corporation. Lưu trữ bản gốc 23 Tháng tư năm 2016. Truy cập 1 tháng Năm năm 2016.
  71. ^ “List<T>.BinarySearch method (T)”. Microsoft Developer Network. Lưu trữ bản gốc 7 tháng Năm năm 2016. Truy cập 10 Tháng tư năm 2016.
  72. ^ “NSArray”. Mac Developer Library. Apple Inc. Lưu trữ bản gốc 17 Tháng tư năm 2016. Truy cập 1 tháng Năm năm 2016.
  73. ^ “CFArray”. Mac Developer Library. Apple Inc. Lưu trữ bản gốc 20 Tháng tư năm 2016. Truy cập 1 tháng Năm năm 2016.
  74. ^ “8.6. bisect — Array bisection algorithm”. The Python Standard Library. Python Software Foundation. Lưu trữ bản gốc 25 Tháng Ba năm 2018. Truy cập 26 Tháng Ba năm 2018.
  75. ^ Fitzgerald 2007, tr. 152.

Tài liệu