Thuật toán chia để trị (Devide and Conquer) dùng để giải lớp các bài toán có thể thực hiện được thông qua ba bước:
Bước chia (Devide). Chia bài toán lớn thành những bài toán con có cùng kiểu với bài toán lớn.
Nguyễn Duy Phương 43
Bước trị (Conquer). Giải các bài toán con đã chia ở bước trước đó. Thông thường các bài toán con chỉ khác nhau về dữ liệu vào nên ta có thể thực hiện bằng một thủ tục đệ qui.
Bước tổng hợp (Combine). Tổng hợp lại kết quả của các bài toán con để nhận được kết quả của bài toán lớn.
Ví dụ 2.13. Nâng x lên lũy thừa n.
Lời giải.
Bước chia. Ta có 𝑥𝑛 = {𝑥𝑛/2. 𝑥𝑛/2 𝑛ế𝑢 𝑛 𝑐ℎẵ𝑛 𝑥. 𝑥𝑛/2. 𝑥𝑛/2 𝑛ế𝑢 𝑥 𝑙ẻ .
Bước trị. Tính toán xn/2 trong trường hợp x chẵn, x.xn/2 trong trường hợp x lẻ.
Bước tổng hợp. Kết quả chính là xn/2. xn/2 trong trường hợp x chẵn và x.xn/2. xn/2
trong trường hợp x lẻ.
Chương trình nâng x lên lũy thừa n bằng kỹ thuật chia và trị được thể hiện như sau: #include <iostream>
#include <iomanip> using namespace std;
int power(int x, unsigned int n){
if( n == 0)
return 1; else if (n%2 == 0)
return power(x, n/2)*power(x, n/2);
else
return x*power(x, n/2)*power(x, n/2); } int main(){ int x = 2; unsigned int n = 5; cout<<power(x, n)<<setw(3); }
Thử nghiệm thuật toán: tính S = power(2, 5) S = power(2, 5)
= 2* power(2, 2)* power(2, 2)
= 2* power(2, 1)* power(2, 1)* power(2, 1)* power(2, 1)
= 2* 2* power(0, 1)*2* power(2, 0)* 2*power(2, 0)* 2*power(2, 0) = 2*2*2*2*2
Nguyễn Duy Phương 44 Thuật toán trên có độ phức tạp là O(n). Thuật toán trình bày dưới đâu có độ phức tạp O(log(n)).
#include <iostream> #include <iomanip> using namespace std;
int power(int x, unsigned int n) { int temp; if( n == 0) return 1; temp = power(x, n/2); if (n%2 == 0) return temp*temp; else return x*temp*temp; } int main(){ int x = 2; unsigned int n = 5; cout<<power(x, n)<<setw(3); }