You are on page 1of 13

BRUTE – FORCE

1. Selection Sort và Bubble Sort:


1.1. Selection Sort:
a) Ý tưởng thuật toán:
Thuật toán selection sort sắp xếp một mảng bằng cách đi tìm
phần tử có giá trị nhỏ nhất (giả sử với sắp xếp mảng tăng dần) trong
đoạn chưa được sắp xếp và đổi cho phần tử nhỏ nhất đó với phần tử
ở đầu đoạn chưa được sắp xếp(không phải đầu mảng). Thuật toán sẽ
chia mảng làm 2 mảng con:
- Một mảng con đã được sắp xếp;
- Một mảng con chưa được sắp xếp.
Tại mỗi bước lặp của thuật toán, phần tử nhỏ nhất ở mảng con
chưa được sắp xếp sẽ được di chuyển về đoạn đã sắp xếp.
Ví dụ minh họa :
Cho một dãy số gồm: 5, 6, 2, 10, 32, 1. Ta sẽ áp dụng thuật toán
Selection Sort để sắp xếp lại mảng số nguyên trên theo thứ tự tăng
dần.
Mảng ban đầu chưa được sắp xếp: [5, 6, 2, 10, 32, 1]
Bước 1: Ta thấy phần tử có giá trị nhỏ nhất là 1 và có chỉ số là 5.
Tiến hành di chuyển giá trị 1 lên đầu tiên và hoán vị vị trí nó với số
đầu tiên là số 5 trong mảng ta được : [1, 6, 2, 10, 32, 5]
Bước 2: Tiếp tục duyệt mảng từ vị trí 1 có giá trị là 6, so sánh với
các phần tử còn lại trong mảng ta thấy giá trị 2 là nhỏ nhất. Tiếp tục
hoán vị vị trí số 2 này ta được : [1, 2, 6, 10, 32, 5]
Bước 3: Tiếp tục so sánh vị trí thứ 2 có giá trị là 6 với 3 phần tử
còn lại. Tiếp tục hoán vị số 6 với số 5 có giá trị nhỏ nhất ta được: [1,
2, 5, 10, 32, 6]
Tương tự như vậy có thể sắp xếp 3 giá trị còn lại và kết quả sau
khi được sắp xếp là: [1, 2, 5, 6, 10, 31]
b) Mã giả:
- Input: Một mảng A[0..n-1] gồm các phần tử có thể sắp xếp
theo thứ tự.
- Output: Mảng A[0..n-1] được sắp xếp theo thứ tự tăng dần.
n  length of s
m  length of t
for i  0 to n – 2 do
min  i
for i + 1 ≤ j ≤ n – 1 do
if A[j] < A[min] then
min  j
end if
end for
Swap A[i] and A[min]
end for

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


Để chọn được phần tử nhỏ nhất, ta cần duyệt qua n phần
tử (tốn n-1 phép so sánh) và sau đó hoán vị nó với phần tử đầu
tiên của dãy hiện hành. Để tìm phần tử nhỏ nhất tiếp theo; ta
cần duyệt qua n-1 phần tử (tốn n-2 phép so sánh). Cứ như vậy,
ta thấy ngay thuật toán sẽ tốn (n-1) + (n-2) + … + 1 = n(n-1)/2
= O(n2) phép so sánh.
Mỗi lần duyệt, ta luôn phải hoán vị một lần (1 hoán vị
tương đương với 3 phép gán), nghĩa là thuật toán sẽ tốn 3(n-1)
+ 3(n-2) + … + 3 = 3n(n-1)/2 = O(n2) phép gán.
Tổng kết lại, ta luôn có độ phức tạp cảu thuật toán
Selection Sort là O(n2) trong mọi trường hợp.
1.2. Bubble Sort:
a) Ý tưởng thuật toán:
Thuật toán sắp xếp Bubble Sort thực hiện sắp xếp dãy số bằng
cách lặp lại công việc đổi chỗ 2 số liên tiếp nhau nếu chúng đứng sai
thứ tự (số sau bé hơn số trước với trường hợp sắp xếp tăng dần) cho
đến khi dãy số được sắp xếp.
Ví dụ minh họa :
Giả sử chúng ta cần sắp xếp dãy số [5, 1, 4, 2, 8] theo thứ tự tăng
dần.

- Lần lặp đầu tiên:


5, 1, 4, 2, 8  1, 5, 4, 2, 8 (Ở đây, thuật toán sẽ so sánh hai phần
tử đầu tiên, và đổi chỗ cho nhau do 5 > 1)
1, 5, 4, 2, 8  1, 4, 5, 2, 8 (Đổi chỗ do 5 > 4)
1, 4, 5, 2, 8  1, 4, 2, 5, 8 (Đổi chỗ do 5 > 2)
1, 4, 2, 5, 8  1, 4, 2, 5, 8 (Ở đây, hai phần tử đang xét đã đúng
thứ tự (8 > 5), vậy ta không cần đổi chỗ)
- Lần lặp thứ 2:
1, 4, 2, 5, 8  1, 4, 2, 5, 8
1, 4, 2, 5, 8  1, 2, 4, 5, 8 (Đổi chỗ do 4 > 2)
1, 2, 4, 5, 8  1, 2, 4, 5, 8
1, 2, 4, 5, 8  1, 2, 4, 5, 8

Bây giờ, dãy số đã được sắp xếp, Nhưng thuật toán của chúng ta
không nhận ra điều đó ngay được. Thuật toán sẽ cần thêm một lần
lặp nữa để kết luận dãy đã sắp xếp khi và khi khi nó đi từ đầu tới
cuối mà không có bất kỳ lần đổi chỗ nào được thực hiện.
- Lần lặp thứ 3:
1, 2, 4, 5, 8  1, 2, 4, 5, 8
1, 2, 4, 5, 8  1, 2, 4, 5, 8
1, 2, 4, 5, 8  1, 2, 4, 5, 8
1, 2, 4, 5, 8  1, 2, 4, 5, 8
b) Mã giả:
- Input: Một mảng A[0..n-1] gồm các phần tử có thể sắp xếp
theo thứ tự.
- Output: Mảng A[0..n-1] được sắp xếp theo thứ tự tăng dần.
for i ← 0 to n − 2 do
for j ← 0 to n − 2 − i do
if A[j] > A[j + 1], then swap A[j] and A[j + 1].
end for
end for
 Độ phức tạp thời gian :
Thấy ngay số phép so sánh là luôn không đổi, tức không
phụ thuộc vào tình trạng ban đầu của dãy. Với i bất kỳ, ta luôn
phải so sánh V[j] với V[j-1], mà j chạy từ n đến i + 1, tức ta
tốn n – i phép so sánh. Thêm nữa, i chạy từ 1 đến n – 1. Vậy ta
tính được số phép so sánh tổng cộng: ∑(n-i) với i chạy từ 1
đến n-1 = (n-1) + (n-2) + … + 1 = n(n-1)/2.
Số phép hoán vị (tương đương 3 phép gán) lại phụ thuộc
vào tình trạng ban đầu của dãy. Cụ thể như sau:
 Trường hợp tốt nhất: Dãy ban đầu đã có thứ tự. Ta
thấy ngay ta không tốn một phép hoán vị nào.
 Trường hợp xấu nhất: Dãy ban đầu có thứ tự ngược.
Xét i bất kỳ, ta thấy rằng mỗi lần so sánh a[j] với a[j-
1], ta đều phải thực hiện phép hoán vị. Điều này có
nghĩa là số phép hoán vị bằng n(n-1)/2.
Tổng kết lại, ta luôn có độ phức tạp cảu thuật toán
Selection Sort là O(n2) trong mọi trường hợp.
2. Sequential Search and Brute-Force String Matching:
2.1. Sequential Search:
a) Ý tưởng thuật toán:
Là phương pháp tìm kiếm một phần tử cho trước trong một
danh sách bằng cách duyệt lần lượt từng phần từ của danh sách đó
đến khi nào tìm được giá trị mong muốn hay đã duyệt hết qua hết
danh sách.
Sau đây là ý tưởng triển khai thuật toán:
 Bắt đầu từ bản ghi đầu tiên của mảng, duyệt từ đầu mảng đến
cuối mảng với x.
 Nếu phần tử đang duyệt bằng x thì trả về vị trí.
 Nếu không tìm thấy bất cứ phần từ nào khi đã duyệt hết thì trả
về -1.
b) Mã giả:
- Input: Một mảng A gồm n phần tử và khóa tìm kiếm K.
- Output: Chỉ số của phần tử đầu tiên trong mảng A[0..n-1] có giá
trị bằng K hoặc -1 nếu không tìm thấy phần tử đó.
A[n] ← K
i←0
While A[i] ≠ K do
i=i+1
EndWhile
If i < n, then "Found" at i.
Else, "not found"
End
 Độ phức tạp thời gian :
Trong trường hợp tốt nhất độ phức tạp của thuật toán này
là O(1), trường hơp xấu nhất là O(n), trung bình cũng là O(n).

2.2. Brute-Force String Matching:


a) Ý tưởng thuật toán:
Lần lượt xét từng vị trí i trong xâu ký tự gốc từ 0 đến n-m, so
sánh T[i…(i+m-1)] với P[0…m-1] bằng cách xét từng cặp ký tự một
và đưa ra kết quả tìm kiếm.
b) Mã giả:
- Input: Một mảng T[0..n-1] gồm n ký tự đại diện cho một văn bản
và một mảng P[0..m-1] gồm m ký tự đại diện cho một mẫu.
- Output: Chỉ số của ký tự đầu tiên trong văn bản bắt đầu một chuỗi
con phù hợp hoặc -1 nếu tìm kiếm không thành công.
for i ← 0 to n − m
Found=True
for j ← 0 to m − 1

if A[i+j] ≠ P[j], then Found=False;


break
end for
if Found, then Location is i;
break
end for
 Độ phức tạp thời gian : O(mn)
3. Closest-Pair by Brute Force:
a) Ý tưởng thuật toán:
- Sắp xếp các điểm dọc theo tọa độ x.
- Chia tập hợp các điểm thành hai tập con có kích thước bằng nhau
theo một đường thẳng đứng x = x giữa.
- Giải bài toán đệ quy trong tập con trái và phải. Điều này sẽ cung
cấp cho khoảng cách tối thiểu bên trái và bên phải d Lmin và
d Rmin tương ứng.
- Tìm khoảng cách tối thiểu d LRmin trong số các cặp điểm trong đó
một điểm nằm bên trái của đường thẳng phân chia và điểm thứ
hai nằm ở bên phải (một cặp phân tách).
- Câu trả lời cuối cùng là mức tối thiểu giữa d Lmin , d Rmin và d LRmin .
b) Mã giả: Mục đích tìm khoảng cách giữa hai điểm gần nhất trong
mặt phẳng bằng brute force.
- Input: Một danh sách P gồm n (n ≥ 2) điểm p1(x1, y1),…,pn(xn,yn).
- Output: Khoảng cách giữa các cặp điểm gần nhất.
dmin = ∞
for i ← 0 to n − 2 do
for j ← i + 1 to n − 1 do
d = (x[i, 0] − x[j, 0])2 + (x[i, 1] − x[j, 1])2 + . . .(x[i, m − 1] −
x[j, m − 1])2
if d < dmin, then dmin = d, i1 = i, i2 = j.
end for
end for
minimal distance in √dmin and i1, i2 is the minimal distance pair.
end
Độ phức tạp thời gian :
- Thuật toán thực hiện Θ (n2) tính toán khoảng cách - giữa mỗi cặp
điểm.
- Mỗi phép tính khoảng cách mất Θ (m) thời gian.
- Do đó, độ phức tập thời gian của thuật toán là C (n) = Θ (n2m).
DIVIDE – AND - CONQUER
1. Mergesort:
a) Ý tưởng thuật toán:
Thuật toán này chia mảng cần sắp xếp thành 2 nửa. Tiếp tục
lặp lại việc này ở các nửa mảng đã chia. Sau cùng gộp các nửa đó
thành mảng đã sắp xếp. Hàm merge() được sử dụng để gộp hai nửa
mảng. Hàm merge(arr, l, m, r) là tiến trình quan trọng nhất sẽ gộp
hai nửa mảng thành 1 mảng sắp xếp, các nửa mảng là arr[l…m] và
arr[m+1…r] sau khi gộp sẽ thành một mảng duy nhất đã sắp xếp.
Ví dụ minh họa :
38 | 27 | 43 | 3 | 9 | 82 | 10

38 | 27 | 43 | 3 9 | 82 | 10

38 | 27 43 | 3 9 | 82 10

38 27 43 3 9 82 10

27 | 38 3 | 43 9 | 82 10

3 | 27 | 38 | 43 9 | 10 | 82

3 | 9 | 10 | 27 | 38 | 43 | 82
b) Mã giả:
- Input: Mảng A[i],…,A[j] với i ≤ j.
- Output: Mảng A[i],…,A[j] đã được sắp xếp.
MergeSort(A, i, j) (* Assumption: i ≤ j. *)
1. If i = j, then return Endif
2. Let k = ⌊i+j−1 2 ⌋.
3. MergeSort(A, i, k)
4. MergeSort(A, k + 1, j)
5. Merge(A, i, k, j)
End
Merge (A, i, k, j ) (* Assumption: i ≤ k < j; A[i : k] and A[k + 1 :
j] are sorted. *)
1. p1 = i; p2 = k + 1; p3 = i
2. While p 1 ≤ k and p 2 ≤ j { 2.1 If A[p1] ≤ A[p2], then B[p3] =
A[p1], p1 = p1 + 1 Else B[p3] = A[p2], p2 = p2 + 1; Endif 2.2 p3 =
p3 + 1; }
3. (* Sao chép các phần còn lại vào B *) While p 1 ≤ k { B[p3] =
A[p1]; p1 = p1 + 1; p3 = p3 + 1; } While p 2 ≤ j { B[p3] = A[p2]; p2
= p2 + 1; p3 = p3 + 1; }
4. For r = i to j { A[r] = B[r] }
End
Độ phức tạp thời gian : Trong mọi trường hợp đều là O(nlogn)
2. Quicksort:
a) Ý tưởng thuật toán:
Là một thuật toán sắp xếp hiệu quả dựa trên việc phân chia
mảng dữ liệu thành các nhóm phần tử nhỏ hơn. Giải thuật sắp xếp
nhanh chia mảng thành hai phần bằng cách so sánh từng phần tử của
mảng với một phần tử được gọi là phần tử chốt. Một mảng bao gồm
các phần tử nhỏ hơn hoặc bằng phần tử chốt và một mảng gồm các
phần tử lớn hơn phần tử chốt:
1. Chọn phần tử chốt.
2. Khai báo 2 biến con trỏ để trỏ để duyệt 2 phía của phần tử
chốt.
3. Biến bên trái trỏ đến từng phần tử mảng con bên trái của phần
tử chốt.
4. Biến bên phải trỏ đến từng phần tử mảng con bên phải của
phần tử chốt.
5. Khi biến bên trái nhỏ hơn phần tử chốt thì di chuyển sang
phải.
6. Khi biến bên phải nhỏ hơn phần tử chốt thì di chuyển sang
trái.
7. Nếu không xảy ra trưởng hợp 5 và 6 thì tráo đổi giá trị 2 biến
trái và phải.
8. Nếu trái lơn hơn phải thì đây là giá trị chốt mới.
Ví dụ minh họa :
Cho mảng [5, 7, 2, 1, 9, 4].
Chọn pivot = 5.
5, 7, 2, 1, 9, 4 (item tiếp theo: 7 > pivot; vì thế giữ nguyên vị
trí).
5, 7, 2, 1, 9, 4 (item tiếp theo: 2 < pivot; vì thể đổi chỗ với
phần tử lớn hơn đầu tiên).
5, 2, 7, 1, 9, 4 (item tiếp theo: 1 < pivot; vì thể đổi chỗ với
phần tử lớn hơn đầu tiên).
5, 2, 1, 7, 9, 4 (item tiếp theo: 9 > pivot; vì thể giữ nguyên vị
trí).
5, 2, 1, 7, 9, 4 (item tiếp theo: 4 < pivot; vì thể đổi chỗ với
phần tử lớn hơn đầu tiên).
5, 2, 1, 4, 9, 7
Cuối cùng: đổi chỗ pivot với phần tử nhỏ hơn cuối cùng.
4, 2, 1, 5, 9, 7
Lặp lại như trên ta sẽ được mảng đã sắp xếp như sau: [1, 2, 4,
5, 7, 9]

b) Mã giả:
- Input: Mảng A[i],…,A[j] với i ≤ j.
- Output: Mảng A[i],…,A[j] đã được sắp xếp.
Partition(𝐴, 𝑖, 𝑗)
1. Let 𝑝𝑖𝑣𝑜𝑡 = 𝐴[𝑖]
2. Let ℎ = 𝑖
3. for 𝑘 = 𝑖 to j do // A[i] is the pivot; 𝐴[𝑖 + 1], ..., 𝐴[ℎ] are smaller
than pivot. 𝐴[ℎ + 1], ..., 𝐴[𝑘 − 1] are pivot
4. if 𝐴[𝑘] < 𝑝𝑖𝑣𝑜𝑡 then
5. ℎ = ℎ + 1
6. 𝑠𝑤𝑎𝑝(𝐴[𝑘], 𝐴[ℎ])
7. 𝑠𝑤𝑎𝑝(𝐴[ℎ], 𝐴[𝑖]); return h End
Quicksort(𝐴, 𝑖, 𝑗)
1. if 𝑖 < 𝑗 then
2. 𝑝 = Partition(𝐴, 𝑖, 𝑗)
3. Quicksort(𝐴, 𝑖, 𝑝 − 1)
4. Quicksort(𝐴, 𝑝 + 1, 𝑗)
5. End
Độ phức tạp thời gian : Trong mọi trường hợp là O(n2)
3. Binary Search:
a) Ý tưởng thuật toán:
Ý tưởng chính của thuật toán Binary Search khi tìm kiếm với
đầu vào là một mảng đã có thứ tự:
1. Để tìm giá trị K trong mảng, chia mảng làm đôi.
2. Xét giá trị giữa của mảng, nếu không tìm được thì xét tiếp
nửa mảng trái hoặc phải.
3. Lặp đến lại bước 1 đến khi tìm được giá trị K hoặc không
thể chia đôi mảng.
Ví dụ minh họa :
Tìm vị trí của giá trị 31 trong mảng sau:
10 | 14 | 19 | 26 | 27 | 31 | 33 | 35 | 42 | 44
0 1 2 3 4 5 6 7 8 9
Đầu tiên, chúng ta chia mảng thành hai nửa theo phép toán
sau: 0 + (9 – 0)/2 = 4 (giá trị là 4.5). Do đó 4 là chỉ-mục-giữa của
mảng.
10 | 14 | 19 | 26 | 27 | 31 | 33 | 35 | 42 | 44
0 1 2 3 4 5 6 7 8 9
Bây giờ chúng ta so sánh giá trị phần tử giữa với phần tử cần
tìm. Giá trị phần tử giữa là 27 và phần tử cần tìm là 31, do đó là
không kết nối. Bởi vì giá trị cần tìm là lớn hơn nên phần tử cần tìm
sẽ nằm ở mảng con bên phải phần tử giữa.
Chúng ta thay đổi giá trị ban-đầu thành chỉ-mục-giữa + 1 và
lại tiếp tục tìm kiếm giá trị chỉ-mục-giữa.
Bây giờ chỉ mục giữa của chúng ta là (4+1) + (9 – 4)/2 = 7 (giá
trị là 7.5). Chúng ta so sánh giá trị tại chỉ mục này với giá trị cần
tìm.

10 | 14 | 19 | 26 | 27 | 31 | 33 | 35 | 42 | 44
0 1 2 3 4 5 6 7 8 9
Giá trị tại chỉ mục 7 là không kết nối, và ngoài ra giá trị cần
tìm là nhỏ hơn giá trị tại chỉ mục 7 do đó chúng ta cần tìm trong
mảng con bên trái của chỉ mục giữa này.
Tiếp tục tìm chỉ-mục-giữa lần nữa. Lần này nó có giá trị là 5.

10 | 14 | 19 | 26 | 27 | 31 | 33 | 35 | 42 | 44
0 1 2 3 4 5 6 7 8 9
So sánh giá trị tại chỉ mục 5 với giá trị cần tìm và thấy rằng nó
kết nối.
Do đó chúng ta kết luận rằng giá trị cần tìm 31 được lưu giữ
tại vị trí chỉ mục 5.
b) Mã giả:
- Input: Một mảng A[0..n-1] đã được sắp xếp và một khóa K.
- Output: Vị trí của giá trị cần tìm nếu có trong mảng hoặc “not
found” khi không tìm thấy.
ℓ ← 0, r ← n – 1
While ℓ ≤ r do
m ← ⌊ℓ+r 2 ⌋ If K = A[m], then { "found" at m-th location}
Else If K > A[m], then ℓ ← m + 1
Else K < A[m], then r ← m − 1 Endif
Endwhile
return "not found"
End
Độ phức tạp thời gian : Trong mọi trường hợp là O(log2n)

You might also like