1.3. ĐỘ PHỨC TẠP CỦA THUẬT TOÁN.
1.3.1. Khái niệm về độ phức tạp của một thuật toán:
Thước đo hiệu quả của một thuật toán là thời gian mà máy tính sử dụng để giải bài toán theo thuật toán đang xét, khi các giá trị đầu vào có một kích thước xác định.
Một thước đo thứ hai là dung lượng bộ nhớ đòi hỏi để thực hiện thuật toán khi các giá trị đầu vào có kích thước xác định. Các vấn đề như thế liên quan đến độ phức tạp tính toán của một thuật toán. Sự phân tích thời gian cần thiết để giải một bài toán có kích thước đặc biệt nào đó liên quan đến độ phức tạp thời gian của thuật toán. Sự phân tích bộ nhớ cần thiết của máy tính liên quan đến độ phức tạp không gian của thuật toán. Việc xem xét độ phức tạp thời gian và không gian của một thuật toán là một vấn đề rất thiết yếu khi các thuật toán được thực hiện. Biết một thuật toán sẽ đưa ra đáp số trong một micro giây, trong một phút hoặc trong một tỉ năm, hiển nhiên là hết sức quan trọng.
Tương tự như vậy, dung lượng bộ nhớ đòi hỏi phải l à khả dụng để giải một bài toán,vì vậy độ phức tạp không gian cũng cần phải tính đến.Vì việc xem xét độ phức tạp không gian gắn liền với các cấu trúc dữ liệu đặc biệt được dùng để thực hiện thuật toán nên ở đây ta sẽ tập trung xem xét độ phức tạp thời gian.
Độ phức tạp thời gian của một thuật toán có thể được biểu diễn qua số các phép toán được dùng bởi thuật toán đó khi các giá trị đầu vào có một kích thước xác định. Sở dĩ độ phức tạp thời gian được mô tả thông qua số các phép toán đòi hỏi thay vì thời gian thực của máy tính là b ởi vì các máy tính khác nhau thực hiện các phép tính sơ cấp trong những khoảng thời gian khác nhau. Hơn nữa, phân tích tất cả các phép toán thành các phép tính bit sơ cấp m à máy tính sử dụng là điều rất phức tạp.
Thí dụ 3: Xét thuật toán tìm số lớn nhất trong dãy n s ố a1, a2, ..., an. Có thể coi kích thước của dữ liệu nhập là số lượng phần tử của dãy số, t ức là n. N ếu coi mỗi lần so sánh hai số của thuật toán đòi hỏi một đơn vị thời gian (giây chẳng hạn) thì thời gian thực hiện thuật toán trong trường hợp xấu nhất là n -1 giây. Với dãy 64 số, thời gian thực hiện thuật toán nhiều lắm là 63 giây.
Thí dụ 4: Thuật toán v ề trò ch ơi “Tháp Hà Nội”
Trò chơi “Tháp Hà Nội” như sau: Có ba cọc A, B, C và 64 cái đĩa (có lỗ để đặt vào cọc), các đĩa có đường kính đôi một khác nhau. Nguyên tắc đặt đĩa vào cọc là: mỗi đĩa chỉ được chồng lên đĩa lớn hơn nó. Ban đầu, cả 64 đĩa được đặt chồng lên nhau ở cột A; hai cột B, C trống. Vấn đề là phải chuyển cả 64 đĩa đó sang cột B hay C, mỗi lần chỉ được di chuyển một đĩa.
Xét trò chơi với n đĩa ban đầu ở cọc A (cọc B và C trống). Gọi Sn là số lần chuyển đĩa để chơi xong trò chơi v ới n đĩa.Nếu n=1 thì rõ ràng là S1=1.
Nếu n>1 thì trước hết ta chuyển n-1 đĩa bên trên sang cọc B (giữ yên đĩa thứ n ở dưới cùng của cọc A). Số lần chuyển n -1 đĩa là Sn-1. Sau đó ta chuyển đĩa thứ n từ cọc A sang cọc C. Cuối cùng, ta chuyển n -1 đĩa từ cọc B sang cọc C (số lần chuyển là Sn-1).
Như vậy, số lần chuyển n đĩa từ A sang C là:
Thuật toán về trò ch ơi “Tháp Hà Nội” đòi hỏi 2^64-1 lần chuyển đĩa (xấp xỉ 18,4 tỉ tỉ lần). Nếu mỗi lần chuyển đĩa mất 1 giây thì thời gian thực hiện thuật toán xấp xỉ 585 tỉ năm!1.3.2. So sánh độ phức tạp của các thuật toán:
Hai thí dụ trên cho th ấy rằng: một thuật toán phải kết thúc sau một số hữu hạn bước, nhưng nếu số hữu hạn này quá lớn thì thuật toán không thể thực hiện được trong thực tế.
Ta nói: thuật toán trong Thí dụ 3 có độ phức tạp là n-1 và là một thuật toán hữu hiệu (hay thuật toán nhanh); thuật toán trong Thí dụ 4 có độ phức tạp là 2^n-1 và đó là một thuật toán không hữu hiệu (hay thuật toán chậm).
Một bài toán th ường có nhiều cách giải, có nhiều thuật toán để giải, các thuật toán đó có độ phức tạp khác nhau.
Xét bài toán: Tính giá trị của đa thức P(x)=a
Ta hãy xét độ phức tạp của hai thuật toán trên.
Đối với thuật toán 1: ở bước 2, phải thực hiện 1 phép nhân và 1 phép cộng với i=1; 2 phép nhân và 1 phép cộng với i=2, ..., n phép nhân và 1 phép cộng với i=n. Vậy số phép tính (nhân và cộng) mà thuật toán 1 đòi hỏi là:
Đối với thuật toán 2, bước 2 phải thực hiện n lần, mỗi lần đòi hỏi 2 phép tính (nhân rồi cộng), do đó số phép tính (nhân và cộng) mà thuật toán 2 đòi hỏi l à 2n.
Nếu coi thời gian thực hiện mỗi phép tính nhân và c ộng là nh ư nhau và là một đơn vị thời gian thì với mỗi n cho trước, thời gian thực hiện thuật toán 1 là n(n+3)/2, còn thời gian thực hiện thuật toán 2 là 2n.1.3.3. Đánh giá độ phức tạp của một thuật toán:
Rõ ràng là thời gian thực hiện thuật toán 2 ít h ơn so v ới thời gian thực hiện thuật toán 1. Hàm f1(n)=2n là hàm bậc nhất, tăng chậm hơn nhiều so với hàm bậc hai f2(n)=n(n+3)/2.
Ta nói rằng thuật toán 2 (có độ phức tạp là 2n) là thuật toán hữu hiệu h ơn (hay nhanh hơn) so với thuật toán 1 (có độ phức tạp là n(n+3)/2).
Để so sánh độ phức tạp của các thuật toán, đi ều tiện lợi là coi độ phức tạp của mỗi thuật toán như là cấp của hàm biểu hiện thời gian thực hiện thuật toán ấy. Các hàm xét sau đây đều là hàm của biến số tự nhiên n>0.
Định nghĩa 1:Ta nói hàm f(n) có cấp thấp hơn hay bằng hàm g(n) nếu tồn tại hằng số C>0 và một số tự nhiên n0sao cho |f(n)| <= C|g(n)| với mọi n <= n0.
Ta viết f(n)=O(g(n)) và còn nói f(n) thoả mãn quan hệ big -O đối với g(n).
Theo định nghĩa này, hàm g(n) là một hàm đơn giản nhất có thể được, đại diện cho “sự biến thiên” của f(n).
Khái niệm big-O đã được dùng trong toán học đã gần một thế kỷ nay. Trong tinhọc, nó được sử dụng rộng rãi để phân tích các thuật toán. Nhà toán học người Đức Paul Bachmann là người đầu tiên đưa ra khái niệm big-O vào năm 1892.
Định nghĩa 2: Nếu một thuật toán có độ phức tạp là f(n) v ới f(n)=O(g(n)) thì ta c ũng
nói thuật toán có độ phức tạp O(g(n)).
Nếu có hai thuật toán giải cùng m ột bài toán, thuật toán 1 có độ phức tạp
O(g1(n)), thuật toán 2 có độ phức tạp O(g2(n)), mà g1(n) có cấp thấp hơn g2(n), thì ta nói rằng thuật toán 1 hữu hiệu hơn (hay nhanh hơn) thuật toán 2.
1) Thuật toán tìm ki ếm tuyến tính:
Số các phép so sánh được dùng trong thuật toán n ày cũng sẽ được xem như thước đo độ phức tạp thời gian của nó. Ở mỗi một bước của vòng l ặp trong thuật toán, có hai phép so sánh được thực hiện: một để xem đã tới cuối bảng chưa và một để so sánh phần tử x với một số hạng của bảng. Cuối cùng còn một phép so sánh nữa làm ở ngoài vòng lặp. Do đó, nếu x=ai, thì đã có 2i+1 phép so sánh được sử dụng. Số phép so sánh nhiều nhất, 2n+2, đòi h ỏi phải được sử dụng khi phần tử x không có mặt trong bảng. Từ đó, thuật toán t ìm ki ếm tuyến tính có độ phức tạp là O(n).
2) Thuật toán tìm ki ếm nhị phân:
Để đ ơn gi ản, ta giả sử rằng có n=2kphần tử trong bảng liệt kê a1,a2,...,an, với k là số nguy ên không âm (nếu n không phải là l ũy thừa của 2, ta có thể xem bảng là một phần của bảng gồm 2k+1 phần tử, trong đó k là số nguy ên nhỏ nhấ t sao cho n < 2k+1).
Ở mỗi giai đoạn của thuật toán vị trí của số hạng đầu tiên i và số hạng cuối cùng j của bảng con hạn chế tìm kiếm ở giai đoạn đó được so sánh để xem bảng con này còn nhiều hơn một phần tử hay không. Nếu i < j, một phép so sánh sẽ được làm để xác định x có lớn hơn s ố hạng ở giữa của bảng con hạn chế hay không. Như vậy ở mỗi giai đoạn, có sử dụng hai phép so sánh. Khi trong bảng chỉ còn một phần tử, một phép so sánh sẽ cho chúng ta biết rằng không còn một phần tử nào thêm n ữa và một phép so sánh nữa cho biết số hạng đó có phải là x hay không. Tóm lại cần phải có nhiều nhất 2k+2=2log 2n+2 phép so sánh để thực hiện phép t ìm kiếm nhị phân (nếu n không phải là
lũy thừa của 2, bảng gốc sẽ được mở rộng tới bảng có 2k+1 phần tử, với k=[log2n] và sự tìm kiếm đòi hỏi phải thực hiện nhiều nhất 2[log2n]+2 phép so sánh). Do đó thuật toán tìm kiếm nhị phân có độ phức tạp là O(log2n). Từ sự phân tích ở trên suy ra rằng thuậttoán tìm kiếm nhị phân, ngay cả trong trường hợp xấu nhất, cũng hiệu quả hơn thuậttoán tìm kiếm tuyến tính.
3) Chú ý: Một điều quan trọng cần phải biết là máy tính phải cần bao lâu để giải xong một bài toán. Thí dụ, nếu một thuật toán đòi hỏi 10 giờ, thì có thể còn đáng chi phí thời gian máy tính đòi h ỏi để giải bài toán đó. Nh ưng nếu một thuật toán đòi hỏi 10 tỉ năm để giải một bài toán, thì thực hiện thuật toán đó sẽ là một điều phi lý. Một trong những hiện tượng lý thú nhất của công nghệ hiện đại là sự tăng ghê gớm của tốc độ và lượng bộ nhớ trong máy tính. Một nhân tố quan t rọng khác làm giảm thời gian cần thiết để giải một bài toán là sự xử lý song song - đây là kỹ thuật thực hiện đồng thời các dãy phép tính. Do sự tăng tốc độ tính toán và dung lượng bộ nhớ của máy tính, cũng như nh ờ việc
dùng các thuật toán lợi dụng được ưu thế của kỹ thuật xử lý song song, các bài toán vài năm trước đây được xem là không thể giải được, thì bây gi ờ có thể giải bình thường.
1. Các thuật ngữ thường dùng cho độ phức tạp của một thuật toán:
Độ phức tạp
O(1) Độ phức tạp hằng số
O(logn) Độ phức tạp lôgarit
O(n) Độ phức tạp tuyến tính
O(nlogn) Độ phức tạp nlogn
O(nb) Độ phức tạp đa thức
O(bn) (b>1) Độ phức tạp hàm mũ
O(n!) Độ phức tạp giai thừa
2. Thời gian máy tính được dùng bởi một thuật toán: