30/09/2018, 17:03

[Chia để trị] Tính lũy thừa a^n

Giờ rảnh được chút, làm thêm một bài nữa về một thuật toán cơ bản: tính lũy thừa an bằng phương pháp chia để trị.
Chắc mọi người đa số hay dùng vòng lặp nhỉ (cứ ừ đại đi), bây giờ đổi gió xíu nhé

Vào luôn vấn đề, như mọi người đã biết, an được tính bằng cách nhân n lần số a (nói vậy cho gọn đi)
Mình lấy ví dụ:

a8 = a . a . a . a . a . a . a . a (8 chữ a lận nhé)

Ta nhận thấy rằng có thể chia nửa cái phép tính trên kia ra, như thế này:

a8 = a4 + 4 = a4 . a4

trong đó, a4 có thể được tính:

a4 = a2 . a2

Lại một lần nữa chia nhỏ :

a2 = a1 . a1 = a . a

Đến đây ra có thể dễ dàng đưa ra a (a cùng n là input của bài toán này).

Vậy ta nhận ra được phần base case của thuật đệ quy ở đây (hay nhiều sách tiếng Việt gọi là phần neo ấy, mình học trên Khan nên đọc quen rồi) chính là khi số mũ đưa về 1, ta có thể đưa thẳng ra giá trị a

  • Từ đó ta suy ra cách giải quyết bài toán thế này: chia nhỏ số mũ n ra cho đến khi n = 1

Vậy hàm pow với tham số : int pow(int a, int n)
Sẽ có phần base case:

if(n == 1)
    return a;

Thế còn recursive case (phần đệ quy) ? Thì theo như ví dụ ở trên, ta có công thức:

an = an/2 . an/2

Chuyển ra code:

return pow(a, n/2) * pow(a, n/2);

Thế nhưng nếu n là một số lẻ thì sao? Ví dụ như n = 3:

a5 = a . a . a . a . a (1)
Thì trong trường hợp này, n/2 sẽ bằng 5/2 = 2 (do n kiểu int nên số sẽ tự bỏ đi phần thập phân)
(1) = a2 . a2 . a
do là số lẻ nên khi khi mod 2 (chia lấy dư) thì số dư sẽ luôn luôn là 1
=> Với n là số lẻ, khi chia ra sẽ luôn thừa một số a

Vậy ta suy ra công thức:

an = an/2 . an/2 với n là số lẻ

hay chuyển ra code:

return pow(a, n/2) * pow(a, n/2) * a;

Tổng hợp lại công thức tổng quát:

an =

  • an/2 * an/2 nếu n chẵn
  • an/2 * an/2 * a nếu n lẻ

Và cuối cùng là code, dài dòng lê thê lắm rồi:

int pow(int a, int n)
{
    if(n == 1) {
        return a;
    } else { 
        if(n % 2 == 0)
            return pow(a, n/2) * pow(a, n/2);
        else
            return pow(a, n/2) * pow(a, n/2) * a;
    }
}

Thế nhưng, ta lại thấy code trên có 1 điểm không tốt, đó chính là trong phần đệ quy, hàm pow được tính 2 lần trong khi chúng giống hệt nhau:

return pow(a, n/2) * pow(a, n/2);

Vậy ta sẽ gán chúng vào một biến, như thế thì sẽ đỡ phải tính lại thêm 1 lần:

int pow(int a, int n)
{
    if(n == 1) {
        return a;
    } else {
        int temp = pow(a, n/2);
        if(n % 2 == 0)
            return temp * temp;
        else
            return temp * temp * a;
    }
}

Code ngắn gọn hơn xíu:

int pow(int a, int n)
{
    if(n == 1) return a;
    int tmp = pow(a, n/2);
    return (n & 1) ? tmp * tmp * a : tmp * tmp;
}

Cập nhật:

Dùng vòng for(i=0->n): dpt=O(n)

Dùng chia để trị: T(n)= T(n/2)+O(1) => dpt=O(logn)

=> Dùng phương pháp chia để trị nhanh hơn là dùng vòng lặp

Kết thúc bài chưa có kinh nghiệm nên viết bài có vẻ lê thê quá, mọi người thông cảm

nhatlonggunz viết 19:15 ngày 30/09/2018

À đúng rồi anh @Gio , anh xem giùm em độ phức tạp khi sử dụng cách này so với khi dùng vòng lặp với. Để em bổ sung vô cho mọi người có cái nhìn trực quan

... viết 19:14 ngày 30/09/2018

số mũ lẻ tính có chính xác không?


À, đúng rồi.
Gió viết 19:06 ngày 30/09/2018
  • Dùng vòng for(i=0->n): dpt=O(n)
  • Dùng chia để trị: T(n)= T(n/2)+O(1) => dpt=O(logn)
nhatlonggunz viết 19:07 ngày 30/09/2018

Vậy cái này lâu hơn hay nhanh hơn hả anh, em chưa học log nên cũng không biết nhiều

Gió viết 19:19 ngày 30/09/2018

Tất nhiên là O(logn) sẽ nhanh hơn.

p/s: log_a(b)=x <=> a^x=b viết log(n) <=> log_2(n) VD: log(16)=4

nhatlonggunz viết 19:08 ngày 30/09/2018

Cám ơn anh
Bữa em học bên Khan một chút về logab = x mà thấy nước ta toàn viết log(n), thấy ngộ ngộ, đâu mất phần base (em không biets nó gọi là gì nữa, logab người ta đọc là log base a of b)
Cám ơn anh

Thành Phạm viết 19:04 ngày 30/09/2018

@nhatlonggunz với anh @Gio nghiên cứu viết tut tính độ phức tạp thật toán đê, muốn tính cho bằng anh em mà không biết tính

nhatlonggunz viết 19:06 ngày 30/09/2018

Bên Khan có dạy mà em chưa học, để khi nảo rảnh em vừa học vừa dịch luôn thử.

Thành Phạm viết 19:04 ngày 30/09/2018

Mà cái độ phức tạp này ý nghĩa thực tế thế nào nhỉ, nhìn nó rất Toán, không biết có tính được ra tốc độ chạy code không

nhatlonggunz viết 19:12 ngày 30/09/2018

Theo em hiểu sơ sơ (không học nhưng cũng lướt lướt) thì cái đó tính thời gian chạy của thuật toán (ví dụ cho i lặp từ 1 đến n, mỗi lần lặp là 1 bước, n lần lặp là n bước), space (gọi thế nào cho chuẩn nhỉ, chắc là khoảng tài nguyên sử dụng), rồi còn 1 cái nữa là gì ấy em không biết.

Và theo em nhìn qua thì đúng là nó

rất Toán

X viết 19:15 ngày 30/09/2018

Mà cái độ phức tạp này ý nghĩa thực tế thế nào nhỉ, nhìn nó rất Toán, không khiết có tính được ra tốc độ chạy code không

tính độ phức tạp này đâu có gì khó đâu mà “độ phức tạp” với “tốc độ chạy code” (thời gian) là khác nhau

Thành Phạm viết 19:09 ngày 30/09/2018

Thể nó tính được gì ạ ?

nhatlonggunz viết 19:08 ngày 30/09/2018

Có lẽ là số bước ?

20 char

Thành Phạm viết 19:18 ngày 30/09/2018

Log hay lẻ lắm, số bước thì chắc phải nguyên chứ nhỉ

nhatlonggunz viết 19:14 ngày 30/09/2018

Em đang viết bài về vụ này, có lẽ trong nay mai sẽ được thôi. Cái này rắc rối quá, đọc đi đọc lại nãy giờ. Giờ phải viết làm sao cho người ta hiểu.

Thành Phạm viết 19:11 ngày 30/09/2018

A, anh tìm được tài liệu tiếng việt rồi, nhưng cái này phải biết giới hạn (lim: 1 khái niệm toán nhá, lớp 12 học),

nhatlonggunz viết 19:15 ngày 30/09/2018

Em đang làm bài về Asymptotic Notation (tiệm cận), không biết nó có liên quan đến độ phức tạp (complexity không)

Gió viết 19:12 ngày 30/09/2018

Cái này mang tính học thuật quá. Nói chung là ước lượng thời gian chạy: O lớn, o nhỏ, Omega… nói chung là để tính thời gian chạy trong TH tốt, xấu, trung bình…

Gió viết 19:13 ngày 30/09/2018

Thực ra là làm tròn lên, nhưng khi làm tròn nó chỉ dc cộng 1 số 0<=c< 1 nên bị bỏ qua.

Thành Phạm viết 19:07 ngày 30/09/2018

@Gio
@nhatlonggunz Em xem tài liệu này xem, có nói chi tiết về tính dpt đấy, có cả phương pháp chia để trị luôn

http://download.tlvnimg.com//520444db935e8a3a04520323cb55c15b/55422750/source/2011/20111203/conngaygaplai/thiet_ke_va_danh_gia_thuat_toan_9853.pdf

Bài liên quan
0