Chuyển đến nội dung chính

TÔI ĐÁNH GIÁ THỊ TRƯỜNG CNTT NĂM 2025 CHO BẠN

  TÔI ĐÁNH GIÁ THỊ TRƯỜNG CNTT NĂM 2025 Cũng như năm 2024, thị trường ngành CNTT trong năm 2025 không có quá nhiều khởi sắc. Tuy nhiên, mình sẽ phân tích một số điểm lưu ý để các bạn nắm bắt được thị trường, đặc biệt đối với các bạn sinh viên đang chuẩn bị ra trường có cơ hội tốt hơn.  Trước khi đi vào bài đọc, các bạn có thể tham khảo hai bài trước để nắm rõ thị trường trong những năm gần đây như thế nào nhé: Bài viết 1: Vì sao xuất hiện tình trạng layoff trong ngành CNTT? Bài viết 2: TÔI DÀNH 3 NGÀY ĐÁNH GIÁ THỊ TRƯỜNG CNTT CUỐI NĂM 2024 CHO BẠN. Mở đầu bài viết, ta cùng tìm hiểu về trending trên TrueUP xem như thế nào nhé (Nhắc lại cho các bạn thì TrueUp crawl các data từ các bài tuyển dụng của các tập đoàn công nghệ lớn) Như các bạn thấy thì sau đợt layoff vào năm 2023, xu hướng tuyển dụng đang dần phục hồi, tuy nhiên vẫn không quá lớn. Theo dữ liệu của Gartner, vốn đầu tư vào ngành IT sẽ tăng 57.4 tỷ đô, tương ứng 9.3% tăng trưởng so với cùng kỳ năm 2024.  Về nguyên ...

GIẢI THÍCH MỘT SỐ DẠNG BIG O: O(LOGN)

GIẢI THÍCH MỘT SỐ DẠNG BIG O: O(LOGN)

Đây là một độ phức tạp điển hình, có rất nhiều trong các dạng cấu trúc dữ liệu và giải thuật cơ bản. Nắm được cách tính độ phức tạp O(logn) của các thuật toán sẽ giúp bạn tự tin hơn được 50% trong hầu hết các dạng big O. Trước khi bắt đầu bài viết, mình muốn các bạn cần tìm hiểu qua về hai bài viết cũ của mình về Big O là gì và cách tính độ phức tạp của Big O cho hàm đệ quy. Bạn tham khảo qua hai bài viết sau:


Bài viết 1: Tìm hiểu độ phức tạp thời gian (Big O) cho người mới chi tiết

Bài viết 2: Tính toán big O cho hàm đệ quy

Nếu không nắm cơ bản về big O thì đọc bài này sẽ rối nhé.

Trước khi vào bài mình sẽ mục lục hóa lại bài viết để cho các bạn dễ hình dung

1.    Giải thích về log - Giải thích về log2

2.    Giải thích về cách tính O(logn)

3.    Tiếp cận các bài toán tìm kiếm với O(logn) và các big O khác.

4.    Tiếp cận bài toán về O(logn)

Bắt đầu thôi!

1.    Giải thích về log - Giải thích về log2

Đầu tiên ta hiểu rằng số mũ là số chỉ số lượng thừa số nhân với nhau, ví dụ

31 = 3

32 = 3*3 (số 3 nhân với nhau 2 lần)

33 = 3*3*3 (số 3 nhân với nhau 3 lần)

….

3n = 3*3*….*3 (số 3 nhân với nhau n lần)

 

Như vậy, đặt ra bài toán sau:

Tìm giá trị x trong trường hợp

3x = 27

Nghĩa là ta đang muốn tìm kiếm rằng số 3 nhân với nhau bao nhiêu lần để trở thành số 27. Ta viết lại bài toán này thành:

log3(27)

Như vậy, hàm log là hàm dùng để tìm hàm mũ, hay nói dễ hiểu hơn là tìm xem số 3 phải nhân với chính nó bao nhiêu lần để trở thành số 27.

Ta có dạng tổng quát như sau

loga(b)

Ta đọc là logarit của b với cơ số a (Tiếng Anh: logarithm of “b” to the base “a”). Hiểu nôm na là số a nhân với chính nó bao nhiêu lần để trở thành số b.

 

Ở toán học, khi người ta ghi log mà không sử dụng base (ví dụ: log 27) thì có nghĩa là họ sử dụng cơ số e, với e xấp xỉ =  2.71828 (log 27 = loge(27) ). Còn trong lập trình xét về trường hợp tính độ phức tạp, thì người ta sử dụng cơ số 2 (log 27 = log2(27)) vì đây là trường hợp độ phức tạp xuất hiện phổ biến nhất.

Như vậy, bạn có thể hiểu tiêu đề bài viết rằng, khi chúng ta viết O(logn) cũng có nghĩa là O(log2n).

Trước khi đi vào bài toán về O(logn), ta cùng tìm hiểu lại một chút về big O.

Giả sử ta có các đồ thị thời gian của các bài toán như sau

(Hình này chưa thể hiện chính xác số liệu, chỉ mang tính minh họa)

Như các bạn biết rằng, khi lập trình, chúng ta cực kỳ quan tâm về tỉ lệ giữa số lượng phần tử chúng ta đưa vào và thời gian để thực hiện chúng. Ví dụ, khi hệ thống của bạn có 10 user cùng đăng nhập, 20 user cùng đăng nhập, 100 user cùng đăng nhập, thì thời gian thực hiện là bao lâu.

Câu hỏi về “Thời gian thực hiện là bao lâu này” khá là mơ hồ, vì rất khó để vẽ chính xác biểu đồ thời gian như hình vẽ đối với từng loại bài toán. Vì tốc độ nhanh hay chậm phụ thuộc vào rất nhiều yếu tố tùy vào từng môi trường thực thi, ngôn ngữ lập trình, từng máy tính, kết nối mạng,… Tuy nhiên, có một điểm chung rằng các đồ thị này tăng tuyến tính theo thời gian. Ta gọi chung sự tăng tuyến tính này là O(n). Bản chất của sự tuyến tính trong phương trình y=ax+b không phụ thuộc vào a, b nên ta thường không quan tâm số hạng của chúng.

Cho x = n, ta gọi tên bằng O(y) = O(f(n)) = O(n). Đây là dạng big O phổ biến các bạn hay nhìn thấy. Khi nhìn big O này, các bạn hiểu rằng số lượng phần tử càng tăng, thì thời gian càng tăng dần đều lên. Đây là dạng độ phức tạp thường xuất hiện nhiều trong duyệt mảng tuyến tính.

 

(Vẫn lưu ý là hình minh họa)

Với trường hợp này tương tự ta loại bỏ các hằng số (lý do đã nêu trên) sẽ thu được là y = x2. Với x = n, ta gọi tên O(y) = O(f(n)) = O(n2). Có thể hiểu rằng các thuật toán có dạng O(n2), thì với số lượng phần tử càng tăng, thời gian sẽ tăng lên cấp bình phương. Bạn có thể nhìn lại hình phía trên để hiểu mức độ khủng của các đồ thị hàm mũ khi số lượng phần tử tăng dần.

Các thuật toán với O(n2) thường xuất hiện đối với hai vòng lặp lồng nhau khi phải thực hiện lặp n lần *  n vòng lặp con (n*n = n2) (tuy nhiên không phải cứ hai vòng lặp lồng nhau là O(n2) mà chỉ đang nói trường hợp chung). Do nguồn dữ liệu hiện nay cực kỳ khổng lồ cộng với thời gian chạy tăng “đột biến” khi n rất lớn, nên thông thường các thuật toán sẽ cố gắng hạn chế tối đa sự xuất hiện của các dạng độ phức tạp mũ như O(n2), O(n3),…

Bây giờ, chúng ta cùng tìm hiểu về O(logn) nhé.

 

1.     Giải thích về cách tính O(logn)

 

Như đã giải thích ở trên, log n có nghĩa là log2n, nghĩa số 2 phải nhân với nhau logn lần để thành số n. Hoặc bạn có thể hiểu ngược lại rằng, số n chia cho 2 logn lần để thành số 1 (*). Mình sẽ vẽ hình dung hai cách hiểu bản chất log n như sau.

Với cách 1, ta bắt đầu từ số 2, và nhân với nhau logn = 3 lần để được số 8

Với cách 2, ta bắt đầu từ số 8, và chia đôi nó logn = 3 lần để trở về số 1.

Cách hiểu thứ hai được sử dụng phổ biến hơn để tìm kiếm O(logn) của bài toán. Nếu có bài toán bảo rằng: “ Cho một số n, mỗi lần chạy hãy chia số n đó cho 2, cho đến khi n = 1” thì chắc chắn độ phức tạp của thuật toán là O(logn)

Ta thấy rằng chương trình có độ phức tạp là O(logn) vì mỗi lần chạy nó sẽ chia đôi giá trị n cho tới khi bằng 1 theo như cách 2 đã nêu trên.

Và đây là đồ thị đối với log n (lưu ý thêm một lần nữa là log n chính là log2n trong big O nhé)

Với trục tung là thời gian, trục hoành là số n. Ta có thể thấy rằng càng có nhiều phần tử thì thời gian phát triển càng chậm. Ví dụ về sự tăng trưởng n như sau:

-        n = 8 thì log(n) = 3

-        n = 16 thì log(n) = 4

-        n = 32 thì log(n) = 5

-        n = 64 thì log(n) = 6

-        n = 128 thì log(n) = 7

-        n = 256 thì log(n) = 8

-       

Bạn thấy rằng số n tăng càng ngày càng nhanh nhưng log n tăng theo rất chậm. Chứng tỏ rằng độ phức tạp O(logn) là độ phức tạp tuyệt vời hơn hẳn cả O(n2) và kể cả O(n) đối với trường hợp n là một con số cực lớn.  

Như vậy, ta đã hiểu rõ bản chất của O(logn) rồi. Bây giờ ta sẽ xem xét các bài toán cụ thể và cách tính O(logn) một cách nhanh gọn mà không cần phải xét từng dòng code nhé.

1.    Tiếp cận các bài toán tìm kiếm với O(logn) và các big O khác.

Ở mục này, khuyến khích bạn nên tự xem trước một số thuật toán sắp xếp nhé. Ở đây mình lựa chọn hai thuật toán để so sánh về độ phức tạp, đó là Selection Sort và Quick Sort (Các thuật toán đều sắp xếp theo thứ tự tăng dần)

1.1  Selection Sort.

Thuật toán Selection Sort tiến hành mỗi lần sẽ duyệt toàn bộ mảng để tìm phần tử nhỏ nhất đưa lên vị trí đầu. Phần tử nhỏ thứ hai sẽ nằm ở vị trí thứ hai của mảng. (Bạn tự tìm hiểu thêm Selection Sort nhé)

Selection Sort | tejakummarikuntla

Đây là mã giả của selection sort, bạn thấy rằng có hai vòng for và vòng for ngoài duyệt n lần, mỗi vòng for trong duyệt từ j +1 đến n lần. Như vậy tổng số lần duyệt sẽ là

y = f(n) = n + n-1 + n-2 + … + 2

=  (n(n+1)/2) - 1 [Áp dụng công thức tính tổng n số tự nhiên]

= n2 + n [Bỏ qua các hệ số]

ð O(y) = O(f(n)) = O(n2) [Chọn biến số có độ biến thiên lớn nhất]

Như vậy, độ phức tạp của Selection Sort là O(n2). Độ phức tạp này tương tự đối với Bubble Sort sử dụng hai vòng lặp for với vòng lặp thứ hai duyệt từ j+1 đến n lần. Đây tất nhiên không phải là một thuật toán sắp xếp tối ưu bạn nên sử dụng đối với lượng data khổng lồ, vì nó có độ phức tạp rất tệ.

1.1  Quick Sort

Chính vì các thuật toán sắp xếp cổ điển trên có độ phức tạp rất tệ O(n2) dẫn đến khó xử lý các bài toán có lượng dữ liệu lớn. Quick Sort ra đời để giải quyết vấn đề này.

Mấu chốt của thuật toán Quick sort là chọn một số trong mảng (gọi là pivot) và sắp xếp làm sao để cho các phần tử bên trái nhỏ hơn pivot, các phần tử bên phải lớn hơn pivot. Sau khi hoàn thành thì vị trí pivot là vị trí đúng. Ta cắt mảng ra làm hai phần và mỗi phần đó tiếp tục thực hiện Quick Sort (sử dụng đệ quy). // Cái này các bạn tìm hiểu thêm nhé.

Giới thiệu thuật Toán Quick Sort - NCC ANT

Và đây là mã giả của thuật toán

Ở đây, bạn biết rằng việc lựa chọn pivot ở left, right hay middle cũng như giá trị mảng đưa vào sẽ ảnh hưởng đến tốc độ chạy của chương trình. Ở đây chúng ta xét trường hợp tốt nhất và trường hợp xấu nhất của chương trình.

Với trường hợp tốt nhất, pivot của mảng luôn chia làm hai nửa bằng nhau.

Khi đó, ta sẽ có cây như hình trên. Ta để ý rằng ở trên hình, số tầng của cây là log(n)mỗi lần nó sẽ chia cho 2 cho đến khi chỉ còn 1 đơn vị, và cũng như là đối với mỗi tầng, ta đều duyệt qua hết tất cả các ô trên đồ thị. Theo hình, ta lấy độ sâu của cây * thời gian duyệt mỗi cấp = log n * n . Số lần duyệt cũng chính là độ phức tạp của thuật toán là O(nlogn).

Trong trường hợp xấu nhất, pivot của chúng ta chọn luôn nằm ở vị trí điểm đầu hoặc điểm cuối của mảng. Trường hợp này xuất hiện khi gặp mảng đã được sắp xếp từ trước. 

Khi đó, giống với trường hợp của Selection Sort hoặc Bubble Sort, ta duyệt tổng cộng là n + (n-1) + (n-2) + … + 2 thì độ phức tạp sẽ là O(n2).

Có một số kỹ thuật để hạn chế xuất hiện trường hợp xấu nhất như random pivot hoặc kiểm tra trước xem mảng có được sắp xếp hay không. Nhưng tóm lại, độ phức tạp của Quick Sort sẽ dao động trong khoảng O(nlogn) cho đến O(n2). => Có một sự tối ưu rất lớn so với các thuật toán sắp xếp truyền thống.

1.                    4. Tiếp cận bài toán khác về O(logn)

    Nếu các bài toán trên hơi phức tạp để bạn hiểu về O(log n), thì dưới đây là bài toán     quen thuộc để bạn hiểu hơn về O(log n). Đó là bài toán TÌM KIẾM NHỊ PHÂN.

    Tìm kiếm nhị phân là bài toán tìm kiếm một giá trị nằm trong một mảng đã được sắp     xếp từ trước, bằng cách so sánh giá trị cần tìm với phần tử ở giữa cộng với chia đôi     mảng để thu hẹp phạm vi tìm kiếm dần // Cái này các bạn tìm hiểu thêm nhé.

Get Started with Binary Search Today: Unraveling the Mystery 


Bạn thấy rằng, trong trường hợp xấu nhất, với ban đầu là n phần tử, mỗi lần mảng sẽ chia nửa ra cho đến khi bằng 1.   

Ở đây bạn để ý rằng cây có độ sâu là logn, và mỗi node nó chỉ thực hiện hằng số lần tác vụ (ở đây mình chọn 1) vì nó không duyệt qua các phần tử trong mảng mà chỉ so sánh phần tử tìm kiếm với phần tử ở giữa. Như vậy, theo như trên ta có ta lấy độ sâu của cây * thời gian duyệt mỗi cấp = log n * 1 . Như vậy, độ phức tạp trong tìm kiếm cây nhị phân là O(logn). Độ phức tạp này tối ưu hơn rất nhiều so với tìm kiếm thông thường khi mà phải duyệt hết toàn bộ mảng với độ phức tạp O(n).

 

Kết luận: Như vậy, thông qua bài blog này ta đã hiểu rõ hơn về độ phức tạp O(logn). Đây là độ phức tạp tối ưu hơn cả O(n) và xuất hiện rất phổ biến trong một số bài toán có thể mô phỏng dưới dạng cây. Cảm ơn các bạn đã đọc bài viết này. Bài viết tuy hơi dài xíu nhưng mình cũng cố gắng viết đảm bảo chất lượng nhất cho người đọc.

 

Nguồn tham khảo:

1.    Big O Notation Series #4: Bí quyết hiểu O (log n)! - YouTube

2.    how n * (n + 1) / 2 does "summation"? : r/askmath

3.    (5) Why is 'quicksort' called 'quicksort'? - Quora

4.    Logarithm Fundamentals | Ep. 6 Lockdown live math - YouTube

5.    Tài liệu nghiên cứu cá nhân

Người viết: Lê Công Diễn

Mang đi nhớ ghi nguồn

 

 

 

 

[Mẹo nhớ công thức tính tổng n số tự nhiên]

Bạn có thể hình dung công thức này bằng hình ảnh sau

Tạo một tam giác với dòng 1 là 1 khối, dòng 2 là 2 khối,… dòng n là n khối. Sau đó tạo một khối tam giác ngược tương tự để tạo ra hình chữ nhật. Diện tích của hình là n*(n+1). Bởi vì mình chỉ lấy nửa hình nên phải n*(n+1)/2

Nhận xét

Bài đăng phổ biến từ blog này

TÔI ĐÁNH GIÁ THỊ TRƯỜNG CNTT NĂM 2025 CHO BẠN

  TÔI ĐÁNH GIÁ THỊ TRƯỜNG CNTT NĂM 2025 Cũng như năm 2024, thị trường ngành CNTT trong năm 2025 không có quá nhiều khởi sắc. Tuy nhiên, mình sẽ phân tích một số điểm lưu ý để các bạn nắm bắt được thị trường, đặc biệt đối với các bạn sinh viên đang chuẩn bị ra trường có cơ hội tốt hơn.  Trước khi đi vào bài đọc, các bạn có thể tham khảo hai bài trước để nắm rõ thị trường trong những năm gần đây như thế nào nhé: Bài viết 1: Vì sao xuất hiện tình trạng layoff trong ngành CNTT? Bài viết 2: TÔI DÀNH 3 NGÀY ĐÁNH GIÁ THỊ TRƯỜNG CNTT CUỐI NĂM 2024 CHO BẠN. Mở đầu bài viết, ta cùng tìm hiểu về trending trên TrueUP xem như thế nào nhé (Nhắc lại cho các bạn thì TrueUp crawl các data từ các bài tuyển dụng của các tập đoàn công nghệ lớn) Như các bạn thấy thì sau đợt layoff vào năm 2023, xu hướng tuyển dụng đang dần phục hồi, tuy nhiên vẫn không quá lớn. Theo dữ liệu của Gartner, vốn đầu tư vào ngành IT sẽ tăng 57.4 tỷ đô, tương ứng 9.3% tăng trưởng so với cùng kỳ năm 2024.  Về nguyên ...

TÔI DÀNH 3 NGÀY ĐÁNH GIÁ THỊ TRƯỜNG CNTT CUỐI NĂM 2024 CHO BẠN.

  TÔI DÀNH 3 NGÀY ĐÁNH GIÁ THỊ TRƯỜNG CNTT CUỐI NĂM 2024 CHO BẠN.   CNTT là một ngành được xem là “xu thế” trong thời đại hiện nay, với lượng nhu cầu công việc nhiều cũng như mức lương trung bình cao hơn so với các ngành khác. Tuy nhiên trong những năm trở lại đây, ngành đang có nhiều dấu hiệu tụt dốc, tiêu biểu như làn sóng layoff (sa thải) trong năm 2023 cực lớn. Chúng ta cùng đánh giá thị trường hiện nay để đưa ra hướng đi phù hợp nhé.