Làm thế nào để bạn viết một thuật toán trong python?
Sắp xếp là một khối xây dựng cơ bản mà nhiều thuật toán khác được xây dựng dựa trên. Nó liên quan đến một số ý tưởng thú vị mà bạn sẽ thấy trong suốt sự nghiệp lập trình của mình. Hiểu cách các thuật toán sắp xếp trong Python hoạt động đằng sau hậu trường là một bước cơ bản để triển khai các thuật toán chính xác và hiệu quả để giải quyết các vấn đề trong thế giới thực Show
Trong hướng dẫn này, bạn sẽ học
Đến cuối hướng dẫn này, bạn sẽ hiểu các thuật toán sắp xếp từ cả quan điểm lý thuyết và thực tế. Quan trọng hơn, bạn sẽ hiểu sâu hơn về các kỹ thuật thiết kế thuật toán khác nhau mà bạn có thể áp dụng cho các lĩnh vực khác trong công việc của mình. Bắt đầu nào Tải xuống miễn phí. Nhận một chương mẫu từ Thủ thuật Python. Cuốn sách chỉ cho bạn các phương pháp hay nhất về Python với các ví dụ đơn giản mà bạn có thể áp dụng ngay lập tức để viết mã Pythonic + đẹp hơn Tầm quan trọng của thuật toán sắp xếp trong PythonSắp xếp là một trong những thuật toán được nghiên cứu kỹ lưỡng nhất trong khoa học máy tính. Có hàng tá triển khai và ứng dụng sắp xếp khác nhau mà bạn có thể sử dụng để làm cho mã của mình hiệu quả và hiệu quả hơn Bạn có thể sử dụng sắp xếp để giải quyết một loạt các vấn đề
Từ các ứng dụng thương mại đến nghiên cứu học thuật và mọi nơi khác, có vô số cách bạn có thể sử dụng sắp xếp để tiết kiệm thời gian và công sức Loại bỏ các quảng cáoThuật toán sắp xếp tích hợp sẵn của PythonNgôn ngữ Python, giống như nhiều ngôn ngữ lập trình cấp cao khác, cung cấp khả năng sắp xếp dữ liệu ngay lập tức bằng cách sử dụng 6. Đây là một ví dụ về sắp xếp một mảng số nguyên>>>
Bạn có thể sử dụng 6 để sắp xếp bất kỳ danh sách nào miễn là các giá trị bên trong có thể so sánh đượcGhi chú. Để tìm hiểu sâu hơn về cách chức năng sắp xếp tích hợp sẵn của Python hoạt động, hãy xem Cách sử dụng sorted() và sort() trong Python và Sắp xếp dữ liệu với Python Ý nghĩa của thời gian phức tạpHướng dẫn này bao gồm hai cách khác nhau để đo thời gian chạy của thuật toán sắp xếp
Thời gian mã của bạnKhi so sánh hai thuật toán sắp xếp trong Python, sẽ luôn hữu ích khi xem mỗi thuật toán chạy trong bao lâu. Thời gian cụ thể mà mỗi thuật toán sử dụng sẽ được xác định một phần bởi phần cứng của bạn, nhưng bạn vẫn có thể sử dụng thời gian tỷ lệ thuận giữa các lần thực thi để giúp bạn quyết định cách triển khai nào hiệu quả hơn về thời gian Trong phần này, bạn sẽ tập trung vào một cách thực tế để đo thời gian thực cần thiết để chạy các thuật toán sắp xếp của bạn bằng cách sử dụng mô-đun 5. Để biết thêm thông tin về các cách khác nhau mà bạn có thể tính thời gian thực thi mã trong Python, hãy xem Hàm hẹn giờ Python. Ba cách để theo dõi mã của bạnĐây là một hàm bạn có thể sử dụng để tính thời gian cho các thuật toán của mình
Trong ví dụ này, 0 nhận tên thuật toán và mảng đầu vào cần sắp xếp. Đây là giải thích từng dòng về cách thức hoạt động của nó
Ghi chú. Một quan niệm sai lầm phổ biến là bạn nên tìm thời gian trung bình của mỗi lần chạy thuật toán thay vì chọn thời gian ngắn nhất. Các phép đo thời gian bị nhiễu vì hệ thống chạy đồng thời các quy trình khác. Thời gian ngắn nhất luôn ít nhiễu nhất, giúp nó thể hiện tốt nhất thời gian chạy thực của thuật toán Đây là một ví dụ về cách sử dụng 0 để xác định thời gian cần thiết để sắp xếp một mảng mười nghìn giá trị nguyên bằng cách sử dụng 6
Nếu bạn lưu đoạn mã trên vào tệp 6, thì bạn có thể chạy nó từ thiết bị đầu cuối và xem đầu ra của nó
Hãy nhớ rằng thời gian tính bằng giây của mọi thử nghiệm phụ thuộc một phần vào phần cứng bạn sử dụng, do đó, bạn có thể sẽ thấy kết quả hơi khác khi chạy mã Ghi chú. Bạn có thể tìm hiểu thêm về mô-đun 5 trong tài liệu Python chính thứcLoại bỏ các quảng cáoĐo lường hiệu quả với ký hiệu Big OThời gian cụ thể mà một thuật toán cần để chạy không đủ thông tin để có được bức tranh đầy đủ về độ phức tạp về thời gian của nó. Để giải quyết vấn đề này, bạn có thể sử dụng ký hiệu Big O (phát âm là “big oh”). Big O thường được sử dụng để so sánh các cách triển khai khác nhau và quyết định cách triển khai nào hiệu quả nhất, bỏ qua các chi tiết không cần thiết và tập trung vào điều quan trọng nhất trong thời gian chạy của thuật toán Thời gian tính bằng giây cần thiết để chạy các thuật toán khác nhau có thể bị ảnh hưởng bởi một số yếu tố không liên quan, bao gồm tốc độ bộ xử lý hoặc bộ nhớ khả dụng. Mặt khác, Big O cung cấp một nền tảng để thể hiện độ phức tạp của thời gian chạy theo thuật ngữ bất khả tri về phần cứng. Với Big O, bạn thể hiện độ phức tạp theo thời gian chạy thuật toán của bạn tăng nhanh như thế nào so với kích thước của đầu vào, đặc biệt là khi đầu vào tăng lớn tùy ý Giả sử rằng n là kích thước của đầu vào cho một thuật toán, ký hiệu Big O biểu thị mối quan hệ giữa n và số bước mà thuật toán thực hiện để tìm giải pháp. Big O sử dụng chữ in hoa “O” theo sau là mối quan hệ này bên trong dấu ngoặc đơn. Ví dụ: O(n) đại diện cho các thuật toán thực hiện một số bước tỷ lệ với kích thước đầu vào của chúng Mặc dù hướng dẫn này sẽ không đi sâu vào chi tiết của ký hiệu Big O, đây là năm ví dụ về độ phức tạp thời gian chạy của các thuật toán khác nhau Big OComplexityDescriptionO(1)constantThời gian chạy không đổi bất kể kích thước của đầu vào. Tìm một phần tử trong bảng băm là một ví dụ về thao tác có thể được thực hiện trong thời gian không đổi. O(n)linearThời gian chạy phát triển tuyến tính với kích thước của đầu vào. Hàm kiểm tra một điều kiện trên mọi mục của danh sách là một ví dụ về thuật toán O(n). O(n2)quadraticThời gian chạy là một hàm bậc hai của kích thước đầu vào. Một triển khai ngây thơ của việc tìm các giá trị trùng lặp trong một danh sách, trong đó mỗi mục phải được kiểm tra hai lần, là một ví dụ về thuật toán bậc hai. O(2n)exponentialThời gian chạy tăng theo cấp số nhân với kích thước của đầu vào. Các thuật toán này được coi là cực kỳ kém hiệu quả. Một ví dụ về thuật toán hàm mũ là bài toán ba màu. O(log n)logaritThời gian chạy tăng tuyến tính trong khi kích thước của đầu vào tăng theo cấp số nhân. Ví dụ: nếu mất một giây để xử lý một nghìn phần tử, thì sẽ mất hai giây để xử lý mười nghìn, ba giây để xử lý một trăm nghìn, v.v. Tìm kiếm nhị phân là một ví dụ về thuật toán thời gian chạy logarit Hướng dẫn này bao gồm độ phức tạp thời gian chạy Big O của từng thuật toán sắp xếp được thảo luận. Nó cũng bao gồm một lời giải thích ngắn gọn về cách xác định thời gian chạy trên từng trường hợp cụ thể. Điều này sẽ giúp bạn hiểu rõ hơn về cách bắt đầu sử dụng Big O để phân loại các thuật toán khác Ghi chú. Để hiểu sâu hơn về Big O, cùng với một số ví dụ thực tế trong Python, hãy xem Ký hiệu Big O và Phân tích thuật toán với các ví dụ về Python Thuật toán sắp xếp bong bóng trong PythonSắp xếp bong bóng là một trong những thuật toán sắp xếp đơn giản nhất. Tên của nó xuất phát từ cách thức hoạt động của thuật toán. Với mỗi lần vượt qua mới, phần tử lớn nhất trong danh sách sẽ “nổi bong bóng” về đúng vị trí của nó Sắp xếp bong bóng bao gồm thực hiện nhiều lần duyệt qua một danh sách, so sánh từng phần tử một và hoán đổi các phần tử liền kề không theo thứ tự Triển khai Sắp xếp bong bóng trong PythonĐây là cách triển khai thuật toán sắp xếp bong bóng trong Python 7Vì cách triển khai này sắp xếp mảng theo thứ tự tăng dần, nên mỗi bước sẽ “xếp bong bóng” phần tử lớn nhất vào cuối mảng. Điều này có nghĩa là mỗi lần lặp mất ít bước hơn so với lần lặp trước vì phần lớn hơn liên tục của mảng được sắp xếp Các vòng lặp ở dòng 4 và 10 xác định cách thuật toán chạy qua danh sách. Lưu ý cách ban đầu 8 đi từ phần tử đầu tiên trong danh sách đến phần tử ngay trước phần tử cuối cùng. Trong lần lặp thứ hai, 8 chạy cho đến khi có hai mục từ mục cuối cùng, sau đó là ba mục từ mục cuối cùng, v.v. Ở cuối mỗi lần lặp, phần cuối của danh sách sẽ được sắp xếpKhi các vòng lặp tiến triển, dòng 15 so sánh từng phần tử với giá trị liền kề của nó và dòng 18 hoán đổi chúng nếu chúng không đúng thứ tự. Điều này đảm bảo một danh sách được sắp xếp ở cuối hàm Ghi chú. Cờ 70 trong các dòng 13, 23 và 27 của mã ở trên là một tối ưu hóa cho thuật toán và không bắt buộc phải có trong triển khai sắp xếp bong bóng đầy đủ chức năng. Tuy nhiên, nó cho phép chức năng lưu các bước không cần thiết nếu danh sách kết thúc được sắp xếp hoàn toàn trước khi vòng lặp kết thúcNhư một bài tập, bạn có thể loại bỏ việc sử dụng cờ này và so sánh thời gian chạy của cả hai cách triển khai Để phân tích chính xác cách thức hoạt động của thuật toán, hãy xem xét một danh sách có giá trị 71. Giả sử bạn đang sử dụng 72 từ phía trên. Đây là một hình minh họa mảng trông như thế nào ở mỗi lần lặp lại thuật toánQuá trình sắp xếp bong bóngBây giờ hãy xem từng bước điều gì đang xảy ra với mảng khi thuật toán tiến triển
Đo độ phức tạp thời gian chạy Big O của Bubble SortViệc triển khai sắp xếp bong bóng của bạn bao gồm hai vòng lặp 04 lồng nhau trong đó thuật toán thực hiện n - 1 phép so sánh, sau đó là n - 2 phép so sánh, v.v. cho đến khi phép so sánh cuối cùng được thực hiện. Điều này có tổng cộng (n - 1) + (n - 2) + (n - 3) + … + 2 + 1 = n(n-1)/2 phép so sánh, cũng có thể được viết là ½n2 - ½nTrước đó, bạn đã biết rằng Big O tập trung vào cách thời gian chạy phát triển so với kích thước của đầu vào. Điều đó có nghĩa là, để biến phương trình trên thành độ phức tạp Big O của thuật toán, bạn cần loại bỏ các hằng số vì chúng không thay đổi theo kích thước đầu vào Làm như vậy sẽ đơn giản hóa ký hiệu thành n2 - n. Vì n2 tăng nhanh hơn nhiều so với n, nên số hạng cuối cùng này cũng có thể bị loại bỏ, để lại sắp xếp bong bóng với độ phức tạp trung bình và trường hợp xấu nhất là O(n2) Trong trường hợp thuật toán nhận được một mảng đã được sắp xếp—và giả sử việc triển khai bao gồm tối ưu hóa cờ 70 được giải thích trước đó—độ phức tạp thời gian chạy sẽ giảm xuống O(n) tốt hơn nhiều vì thuật toán sẽ không cần truy cập bất kỳ phần tử nào nhiều hơn Khi đó, O(n) là độ phức tạp thời gian chạy trường hợp tốt nhất của sắp xếp bong bóng. Nhưng hãy nhớ rằng các trường hợp tốt nhất là một ngoại lệ và bạn nên tập trung vào trường hợp trung bình khi so sánh các thuật toán khác nhau Thời gian thực hiện sắp xếp bong bóng của bạnSử dụng 0 của bạn từ trước đó trong hướng dẫn này, đây là thời gian cần thiết để sắp xếp bong bóng xử lý một mảng có mười nghìn phần tử. Dòng 8 thay thế tên của thuật toán và mọi thứ khác giữ nguyên 0Bây giờ bạn có thể chạy tập lệnh để nhận thời gian thực hiện của 07 0Mất 408 giây để sắp xếp mảng có mười nghìn phần tử. Điều này đại diện cho việc thực hiện nhanh nhất trong số mười lần lặp lại mà 0 chạy. Thực thi tập lệnh này nhiều lần sẽ tạo ra kết quả tương tựGhi chú. Một lần thực hiện sắp xếp bong bóng mất 08 giây, nhưng thuật toán đã chạy mười lần bằng cách sử dụng 1. Điều này có nghĩa là mã của bạn sẽ mất khoảng 42 giây để chạy, giả sử rằng bạn có các đặc điểm phần cứng tương tự. Máy chậm hơn có thể mất nhiều thời gian hơn để hoàn thànhPhân tích điểm mạnh và điểm yếu của sắp xếp bong bóngƯu điểm chính của thuật toán sắp xếp bong bóng là tính đơn giản của nó. Thật đơn giản để thực hiện và hiểu. Đây có lẽ là lý do chính tại sao hầu hết các khóa học khoa học máy tính đều giới thiệu chủ đề sắp xếp bằng cách sử dụng sắp xếp bong bóng Như bạn đã thấy trước đây, nhược điểm của sắp xếp bong bóng là nó chậm, với độ phức tạp thời gian chạy là O(n2). Thật không may, điều này loại trừ nó như một ứng cử viên thực tế để sắp xếp các mảng lớn Thuật toán sắp xếp chèn trong PythonGiống như sắp xếp bong bóng, thuật toán sắp xếp chèn rất dễ thực hiện và hiểu. Nhưng không giống như sắp xếp bong bóng, nó xây dựng danh sách đã sắp xếp từng phần tử một bằng cách so sánh từng mục với phần còn lại của danh sách và chèn nó vào đúng vị trí của nó. Thủ tục “chèn” này đặt tên cho thuật toán Một sự tương tự tuyệt vời để giải thích sắp xếp chèn là cách bạn sắp xếp một cỗ bài. Hãy tưởng tượng rằng bạn đang cầm một nhóm thẻ trong tay và bạn muốn sắp xếp chúng theo thứ tự. Bạn sẽ bắt đầu bằng cách so sánh từng bước một thẻ với phần còn lại của thẻ cho đến khi bạn tìm thấy vị trí chính xác của nó. Khi đó, bạn hãy cắm thẻ vào đúng vị trí và bắt đầu lại với một thẻ mới, lặp lại cho đến khi tất cả các thẻ trên tay của bạn được sắp xếp Triển khai Sắp xếp chèn trong PythonThuật toán sắp xếp chèn hoạt động chính xác như ví dụ với cỗ bài. Đây là cách triển khai trong Python 4Không giống như sắp xếp bong bóng, việc triển khai sắp xếp chèn này xây dựng danh sách được sắp xếp bằng cách đẩy các mục nhỏ hơn sang trái. Hãy chia nhỏ 43 từng dòng
Đây là hình minh họa các lần lặp lại khác nhau của thuật toán khi sắp xếp mảng 71Quy trình sắp xếp chènBây giờ đây là tóm tắt các bước của thuật toán khi sắp xếp mảng
Đo độ phức tạp thời gian chạy Big O của Sắp xếp chènTương tự như cách triển khai sắp xếp bong bóng của bạn, thuật toán sắp xếp chèn có một vài vòng lặp lồng nhau đi qua danh sách. Vòng lặp bên trong khá hiệu quả vì nó chỉ đi qua danh sách cho đến khi tìm thấy đúng vị trí của một phần tử. Điều đó nói rằng, thuật toán vẫn có độ phức tạp thời gian chạy O(n2) trong trường hợp trung bình Trường hợp xấu nhất xảy ra khi mảng được cung cấp được sắp xếp theo thứ tự ngược lại. Trong trường hợp này, vòng lặp bên trong phải thực hiện mọi phép so sánh để đặt mọi phần tử vào đúng vị trí của nó. Điều này vẫn mang lại cho bạn độ phức tạp thời gian chạy O(n2) Trường hợp tốt nhất xảy ra khi mảng được cung cấp đã được sắp xếp. Ở đây, vòng lặp bên trong không bao giờ được thực thi, dẫn đến độ phức tạp thời gian chạy O(n), giống như trường hợp tốt nhất của sắp xếp bong bóng Mặc dù sắp xếp bong bóng và sắp xếp chèn có cùng độ phức tạp thời gian chạy Big O, nhưng trên thực tế, sắp xếp chèn hiệu quả hơn đáng kể so với sắp xếp bong bóng. Nếu bạn nhìn vào việc triển khai cả hai thuật toán, thì bạn có thể thấy cách sắp xếp chèn phải thực hiện ít phép so sánh hơn để sắp xếp danh sách Định thời gian thực hiện sắp xếp chèn của bạnĐể chứng minh khẳng định rằng sắp xếp chèn hiệu quả hơn sắp xếp nổi, bạn có thể tính thời gian cho thuật toán sắp xếp chèn và so sánh nó với kết quả của sắp xếp nổi. Để thực hiện việc này, bạn chỉ cần thay thế cuộc gọi thành 0 bằng tên triển khai sắp xếp chèn của bạn 6Bạn có thể thực thi tập lệnh như trước 3Lưu ý cách triển khai sắp xếp chèn mất khoảng 00 giây ít hơn so với triển khai sắp xếp bong bóng để sắp xếp cùng một mảng. Mặc dù cả hai đều là thuật toán O(n2), nhưng sắp xếp chèn hiệu quả hơnPhân tích điểm mạnh và điểm yếu của sắp xếp chènCũng giống như sắp xếp bong bóng, thuật toán sắp xếp chèn rất dễ thực hiện. Mặc dù sắp xếp chèn là thuật toán O(n2), nhưng trong thực tế, nó cũng hiệu quả hơn nhiều so với các triển khai bậc hai khác như sắp xếp bong bóng Có nhiều thuật toán mạnh hơn, bao gồm sắp xếp hợp nhất và Quicksort, nhưng các triển khai này là đệ quy và thường không đánh bại được sắp xếp chèn khi làm việc trên các danh sách nhỏ. Một số triển khai Quicksort thậm chí sử dụng sắp xếp chèn bên trong nếu danh sách đủ nhỏ để cung cấp triển khai tổng thể nhanh hơn. Timsort cũng sử dụng sắp xếp chèn bên trong để sắp xếp các phần nhỏ của mảng đầu vào Điều đó nói rằng, sắp xếp chèn không thực tế đối với các mảng lớn, mở ra cơ hội cho các thuật toán có thể mở rộng theo những cách hiệu quả hơn Thuật toán sắp xếp hợp nhất trong PythonHợp nhất sắp xếp là một thuật toán sắp xếp rất hiệu quả. Nó dựa trên phương pháp chia để trị, một kỹ thuật thuật toán mạnh mẽ được sử dụng để giải quyết các vấn đề phức tạp Để hiểu đúng cách phân chia và chinh phục, trước tiên bạn nên hiểu khái niệm đệ quy. Đệ quy liên quan đến việc chia vấn đề thành các bài toán con nhỏ hơn cho đến khi chúng đủ nhỏ để quản lý. Trong lập trình, đệ quy thường được thể hiện bằng một hàm gọi chính nó Ghi chú. Hướng dẫn này không khám phá sâu về đệ quy. Để hiểu rõ hơn về cách đệ quy hoạt động và xem nó hoạt động bằng Python, hãy xem Tư duy đệ quy trong Python và Đệ quy trong Python. Một lời giới thiệu Các thuật toán chia để trị thường tuân theo cùng một cấu trúc
Trong trường hợp sắp xếp hợp nhất, phương pháp chia để trị chia tập hợp các giá trị đầu vào thành hai phần có kích thước bằng nhau, sắp xếp đệ quy từng nửa và cuối cùng hợp nhất hai phần đã sắp xếp này thành một danh sách được sắp xếp duy nhất Loại bỏ các quảng cáoTriển khai sắp xếp hợp nhất trong PythonViệc triển khai thuật toán sắp xếp hợp nhất cần hai phần khác nhau
Đây là mã để hợp nhất hai mảng khác nhau 0 01 nhận được hai mảng được sắp xếp khác nhau cần được hợp nhất với nhau. Quá trình để thực hiện điều này là đơn giản
Với chức năng trên, phần còn thiếu duy nhất là một hàm chia đệ quy mảng đầu vào thành một nửa và sử dụng 01 để tạo ra kết quả cuối cùng 1Đây là một bản tóm tắt nhanh chóng của mã
Lưu ý cách hàm này gọi chính nó theo cách đệ quy, chia đôi mảng mỗi lần. Mỗi lần lặp xử lý một mảng ngày càng thu hẹp cho đến khi còn lại ít hơn hai phần tử, nghĩa là không còn gì để sắp xếp. Tại thời điểm này, 01 tiếp quản, hợp nhất hai nửa và tạo ra một danh sách được sắp xếpHãy xem phần trình bày về các bước mà sắp xếp hợp nhất sẽ thực hiện để sắp xếp mảng 71Quá trình sắp xếp hợp nhấtHình sử dụng các mũi tên màu vàng để biểu thị việc chia đôi mảng ở mỗi cấp độ đệ quy. Các mũi tên màu xanh lá cây đại diện cho việc hợp nhất từng mảng con lại với nhau. Các bước có thể được tóm tắt như sau
Đo lường độ phức tạp Big O của Sắp xếp hợp nhấtĐể phân tích mức độ phức tạp của sắp xếp hợp nhất, bạn có thể xem xét riêng hai bước của nó
Thật thú vị, O(n log2n) là thời gian chạy trong trường hợp xấu nhất có thể đạt được bằng thuật toán sắp xếp Loại bỏ các quảng cáoThời gian thực hiện sắp xếp hợp nhất của bạnĐể so sánh tốc độ sắp xếp hợp nhất với hai lần triển khai trước đó, bạn có thể sử dụng cơ chế tương tự như trước và thay thế tên của thuật toán trong dòng 8 2Bạn có thể thực thi tập lệnh để có thời gian thực hiện là 39 3So với sắp xếp bong bóng và sắp xếp chèn, việc triển khai sắp xếp hợp nhất cực kỳ nhanh, sắp xếp mảng mười nghìn phần tử trong chưa đầy một giây Phân tích điểm mạnh và điểm yếu của sắp xếp hợp nhấtNhờ độ phức tạp thời gian chạy của nó là O(n log2n), sắp xếp hợp nhất là một thuật toán rất hiệu quả, chia tỷ lệ tốt khi kích thước của mảng đầu vào tăng lên. Việc song song hóa cũng đơn giản vì nó chia mảng đầu vào thành các phần có thể được phân phối và xử lý song song nếu cần Điều đó nói rằng, đối với các danh sách nhỏ, chi phí thời gian của đệ quy cho phép các thuật toán như sắp xếp bong bóng và sắp xếp chèn nhanh hơn. Ví dụ: chạy thử nghiệm với danh sách mười phần tử sẽ cho kết quả như sau 4Cả sắp xếp bong bóng và sắp xếp chèn đều đánh bại sắp xếp hợp nhất khi sắp xếp danh sách mười phần tử Một nhược điểm khác của sắp xếp hợp nhất là nó tạo ra các bản sao của mảng khi gọi chính nó một cách đệ quy. Nó cũng tạo một danh sách mới bên trong 01 để sắp xếp và trả về cả hai nửa đầu vào. Điều này làm cho sắp xếp hợp nhất sử dụng nhiều bộ nhớ hơn so với sắp xếp bong bóng và sắp xếp chèn, cả hai đều có thể sắp xếp danh sách tại chỗDo giới hạn này, bạn có thể không muốn sử dụng sắp xếp hợp nhất để sắp xếp các danh sách lớn trong phần cứng hạn chế bộ nhớ Thuật toán Quicksort trong PythonCũng giống như sắp xếp hợp nhất, thuật toán Quicksort áp dụng nguyên tắc chia để trị để chia mảng đầu vào thành hai danh sách, danh sách đầu tiên chứa các mục nhỏ và danh sách thứ hai chứa các mục lớn. Thuật toán sau đó sắp xếp đệ quy cả hai danh sách cho đến khi danh sách kết quả được sắp xếp hoàn toàn Chia danh sách đầu vào được gọi là phân vùng danh sách. Quicksort trước tiên chọn một phần tử 41 và phân vùng danh sách xung quanh 41, đặt mọi phần tử nhỏ hơn vào một mảng 43 và mọi phần tử lớn hơn vào một mảng 44Đặt mọi phần tử từ danh sách 43 sang bên trái của 41 và mọi phần tử từ danh sách 44 sang bên phải định vị chính xác vị trí của 41 trong danh sách được sắp xếp cuối cùng. Điều này có nghĩa là bây giờ hàm có thể áp dụng đệ quy quy trình tương tự cho 43 và sau đó là 44 cho đến khi toàn bộ danh sách được sắp xếpTriển khai Quicksort trong PythonĐây là một triển khai khá nhỏ gọn của Quicksort 5Đây là một bản tóm tắt của mã
Dưới đây là hình minh họa các bước mà Quicksort thực hiện để sắp xếp mảng 71Quy trình sắp xếp nhanhCác dòng màu vàng biểu thị phân vùng của mảng thành ba danh sách. 43, 55 và 44. Các đường màu xanh lá cây đại diện cho việc sắp xếp và sắp xếp các danh sách này lại với nhau. Dưới đây là giải thích ngắn gọn về các bước
Chọn phần tử 1from random import randint 2from timeit import repeat 3 4def run_sorting_algorithm(algorithm, array): 5 # Set up the context and prepare the call to the specified 6 # algorithm using the supplied array. Only import the 7 # algorithm function if it's not the built-in `sorted()`. 8 setup_code = f"from __main__ import {algorithm}" \ 9 if algorithm != "sorted" else "" 10 11 stmt = f"{algorithm}({array})" 12 13 # Execute the code ten different times and return the time 14 # in seconds that each execution took 15 times = repeat(setup=setup_code, stmt=stmt, repeat=3, number=10) 16 17 # Finally, display the name of the algorithm and the 18 # minimum time it took to run 19 print(f"Algorithm: {algorithm}. Minimum execution time: {min(times)}") 41Tại sao việc triển khai ở trên lại chọn ngẫu nhiên phần tử 41? Do cách thức hoạt động của thuật toán Quicksort, số lượng cấp độ đệ quy phụ thuộc vào vị trí mà 41 kết thúc trong mỗi phân vùng. Trong trường hợp tốt nhất, thuật toán luôn chọn phần tử trung vị là 41. Điều đó sẽ làm cho mỗi bài toán con được tạo ra có kích thước chính xác bằng một nửa so với bài toán trước đó, dẫn đến hầu hết các mức log2nMặt khác, nếu thuật toán luôn chọn phần tử nhỏ nhất hoặc lớn nhất của mảng là 41, thì các phân vùng được tạo sẽ càng không bằng nhau càng tốt, dẫn đến cấp độ đệ quy n-1. Đó sẽ là trường hợp xấu nhất đối với QuicksortNhư bạn có thể thấy, hiệu quả của Quicksort thường phụ thuộc vào lựa chọn 41. Nếu mảng đầu vào chưa được sắp xếp, thì việc sử dụng phần tử đầu tiên hoặc cuối cùng là 41 sẽ hoạt động giống như một phần tử ngẫu nhiên. Nhưng nếu mảng đầu vào được sắp xếp hoặc sắp xếp gần hết, việc sử dụng phần tử đầu tiên hoặc cuối cùng làm 41 có thể dẫn đến trường hợp xấu nhất. Việc chọn ngẫu nhiên 41 khiến nhiều khả năng Quicksort sẽ chọn một giá trị gần với giá trị trung bình hơn và kết thúc nhanh hơnMột tùy chọn khác để chọn 41 là tìm giá trị trung bình của mảng và buộc thuật toán sử dụng nó làm 41. Điều này có thể được thực hiện trong thời gian O(n). Mặc dù quá trình này phức tạp hơn một chút, nhưng việc sử dụng giá trị trung bình là 41 cho Quicksort đảm bảo bạn sẽ có kịch bản Big O tốt nhấtĐo độ phức tạp Big O của QuicksortVới Quicksort, danh sách đầu vào được phân vùng theo thời gian tuyến tính, O(n) và quá trình này lặp lại đệ quy trung bình log2n lần. Điều này dẫn đến độ phức tạp cuối cùng là O(n log2n) Điều đó nói rằng, hãy nhớ cuộc thảo luận về cách lựa chọn 41 ảnh hưởng đến thời gian chạy của thuật toán. Kịch bản trường hợp tốt nhất O(n) xảy ra khi 41 được chọn gần với giá trị trung bình của mảng và kịch bản O(n2) xảy ra khi 41 là giá trị nhỏ nhất hoặc lớn nhất của mảngVề mặt lý thuyết, nếu thuật toán tập trung đầu tiên vào việc tìm giá trị trung bình và sau đó sử dụng nó làm phần tử 41, thì độ phức tạp trong trường hợp xấu nhất sẽ giảm xuống còn O(n log2n). Giá trị trung bình của một mảng có thể được tìm thấy trong thời gian tuyến tính và sử dụng nó làm 41 đảm bảo phần Quicksort của mã sẽ hoạt động trong O(n log2n)Bằng cách sử dụng giá trị trung bình là 41, bạn sẽ có thời gian chạy cuối cùng là O(n) + O(n log2n). Bạn có thể đơn giản hóa điều này xuống O(n log2n) vì phần logarit tăng nhanh hơn nhiều so với phần tuyến tínhGhi chú. Mặc dù có thể đạt được O(n log2n) trong trường hợp xấu nhất của Quicksort nhưng cách tiếp cận này hiếm khi được sử dụng trong thực tế. Các danh sách phải khá lớn để triển khai nhanh hơn so với lựa chọn ngẫu nhiên đơn giản của 41Chọn ngẫu nhiên 41 làm cho trường hợp xấu nhất rất khó xảy ra. Điều đó làm cho lựa chọn ngẫu nhiên 41 đủ tốt cho hầu hết các triển khai thuật toánĐịnh thời gian triển khai Quicksort của bạnĐến bây giờ, bạn đã quen thuộc với quy trình định thời gian chạy của thuật toán. Chỉ cần thay đổi tên của thuật toán trong dòng 8 6Bạn có thể thực thi tập lệnh như trước đây 7Quicksort không chỉ hoàn thành trong chưa đầy một giây mà còn nhanh hơn nhiều so với sắp xếp hợp nhất ( 18 giây so với 19 giây). Việc tăng số lượng phần tử được chỉ định bởi 20 từ 21 lên 22 và chạy lại tập lệnh kết thúc bằng việc sắp xếp hợp nhất hoàn tất sau 23 giây, trong khi Quicksort sắp xếp danh sách chỉ trong 24 giâyPhân tích điểm mạnh và điểm yếu của QuicksortĐúng như tên gọi, Quicksort rất nhanh. Mặc dù về mặt lý thuyết, trường hợp xấu nhất của nó là O(n2), nhưng trên thực tế, việc triển khai Quicksort tốt sẽ đánh bại hầu hết các triển khai sắp xếp khác. Ngoài ra, giống như sắp xếp hợp nhất, Quicksort dễ dàng song song hóa Một trong những nhược điểm chính của Quicksort là thiếu đảm bảo rằng nó sẽ đạt được độ phức tạp thời gian chạy trung bình. Mặc dù trường hợp xấu nhất rất hiếm xảy ra, nhưng một số ứng dụng nhất định không thể chấp nhận rủi ro về hiệu suất kém, vì vậy chúng chọn các thuật toán nằm trong O(n log2n) bất kể đầu vào là gì. Giống như sắp xếp hợp nhất, Quicksort cũng đánh đổi dung lượng bộ nhớ để lấy tốc độ. Điều này có thể trở thành một hạn chế để sắp xếp danh sách lớn hơn Một thử nghiệm nhanh sắp xếp danh sách mười yếu tố dẫn đến kết quả sau 8Kết quả cho thấy Quicksort cũng phải trả giá bằng đệ quy khi danh sách đủ nhỏ, mất nhiều thời gian hơn để hoàn thành so với cả sắp xếp chèn và sắp xếp bong bóng Loại bỏ các quảng cáoThuật toán Timsort trong PythonThuật toán Timsort được coi là thuật toán sắp xếp lai vì nó sử dụng sự kết hợp tốt nhất của cả hai thế giới giữa sắp xếp chèn và sắp xếp hợp nhất. Timsort gần gũi và thân thiết với cộng đồng Python vì nó được Tim Peters tạo ra vào năm 2002 để sử dụng làm thuật toán sắp xếp tiêu chuẩn của ngôn ngữ Python Đặc điểm chính của Timsort là nó tận dụng các phần tử đã được sắp xếp tồn tại trong hầu hết các bộ dữ liệu trong thế giới thực. Chúng được gọi là chạy tự nhiên. Sau đó, thuật toán lặp lại danh sách, thu thập các phần tử thành các lần chạy và hợp nhất chúng thành một danh sách được sắp xếp duy nhất Triển khai Timsort trong PythonTrong phần này, bạn sẽ tạo một triển khai Python cơ bản minh họa tất cả các phần của thuật toán Timsort. Nếu bạn quan tâm, bạn cũng có thể kiểm tra triển khai C ban đầu của Timsort Bước đầu tiên trong việc triển khai Timsort là sửa đổi việc triển khai 43 từ trước đó 9Việc triển khai đã sửa đổi này thêm một vài tham số, 26 và 27, cho biết phần nào của mảng sẽ được sắp xếp. Điều này cho phép thuật toán Timsort sắp xếp một phần của mảng tại chỗ. Sửa đổi hàm thay vì tạo một hàm mới có nghĩa là nó có thể được sử dụng lại cho cả sắp xếp chèn và TimsortBây giờ hãy xem việc triển khai Timsort 0Mặc dù cách triển khai phức tạp hơn một chút so với các thuật toán trước nhưng chúng ta có thể tóm tắt nhanh như sau
Lưu ý cách thức, không giống như sắp xếp hợp nhất, Timsort hợp nhất các mảng con đã được sắp xếp trước đó. Làm như vậy sẽ giảm tổng số so sánh cần thiết để tạo danh sách được sắp xếp. Ưu điểm này so với sắp xếp hợp nhất sẽ trở nên rõ ràng khi chạy thử nghiệm sử dụng các mảng khác nhau Cuối cùng, dòng 2 định nghĩa 32. Có hai lý do để sử dụng 31 làm giá trị ở đây
Kết hợp cả hai điều kiện trên cung cấp một số tùy chọn cho 34. Việc triển khai trong hướng dẫn này sử dụng 32 như một trong những khả năngGhi chú. Trong thực tế, Timsort thực hiện điều gì đó phức tạp hơn một chút để tính toán 34. Nó chọn một giá trị bao gồm từ 32 đến 64, sao cho độ dài của danh sách chia cho 34 chính xác là lũy thừa của 2. Nếu điều đó là không thể, nó sẽ chọn một giá trị gần bằng, nhưng hoàn toàn nhỏ hơn, lũy thừa 2Nếu bạn tò mò, bạn có thể đọc phân tích đầy đủ về cách chọn 34 trong phần Máy tính minrunĐo độ phức tạp Big O của TimsortTrung bình, độ phức tạp của Timsort là O(n log2n), giống như sắp xếp hợp nhất và Quicksort. Phần logarit đến từ việc nhân đôi kích thước của lần chạy để thực hiện từng thao tác hợp nhất tuyến tính Tuy nhiên, Timsort hoạt động đặc biệt tốt trên các danh sách đã được sắp xếp hoặc sắp xếp gần, dẫn đến trường hợp tốt nhất là O(n). Trong trường hợp này, Timsort rõ ràng đánh bại sắp xếp hợp nhất và phù hợp với trường hợp tốt nhất cho Quicksort. Nhưng trường hợp xấu nhất đối với Timsort cũng là O(n log2n), vượt qua O(n2) của Quicksort Loại bỏ các quảng cáoĐịnh thời gian thực hiện Timsort của bạnBạn có thể sử dụng 0 để xem cách Timsort thực hiện sắp xếp mảng mười nghìn phần tử 1Bây giờ hãy thực thi tập lệnh để có được thời gian thực hiện của 43 2Tại thời điểm 44 giây, quá trình triển khai Timsort này mất đầy đủ 45 giây hoặc 17 phần trăm, nhanh hơn so với sắp xếp hợp nhất, mặc dù nó không khớp với 18 của Quicksort. Nó cũng nhanh hơn 11.000 phần trăm so với sắp xếp chènBây giờ hãy thử sắp xếp một danh sách đã được sắp xếp bằng bốn thuật toán này và xem điều gì sẽ xảy ra. Bạn có thể sửa đổi phần 47 của mình như sau 3Nếu bạn thực thi tập lệnh ngay bây giờ, thì tất cả các thuật toán sẽ chạy và xuất thời gian thực hiện tương ứng của chúng 4Lần này, Timsort xuất hiện với tốc độ khổng lồ nhanh hơn ba mươi bảy phần trăm so với sắp xếp hợp nhất và nhanh hơn năm phần trăm so với Quicksort, linh hoạt khả năng tận dụng các lần chạy đã được sắp xếp sẵn Lưu ý cách Timsort được hưởng lợi từ hai thuật toán chậm hơn nhiều khi được sử dụng riêng. Thiên tài của Timsort là kết hợp các thuật toán này và phát huy thế mạnh của chúng để đạt được kết quả ấn tượng Phân tích điểm mạnh và điểm yếu của TimsortNhược điểm chính của Timsort là sự phức tạp của nó. Mặc dù triển khai một phiên bản rất đơn giản của thuật toán ban đầu, nó vẫn yêu cầu nhiều mã hơn vì nó dựa trên cả 43 và 01Một trong những lợi thế của Timsort là khả năng dự đoán hiệu suất trong O(n log2n) bất kể cấu trúc của mảng đầu vào. Ngược lại với Quicksort, có thể suy giảm xuống O(n2). Timsort cũng rất nhanh đối với các mảng nhỏ vì thuật toán biến thành sắp xếp chèn đơn Đối với việc sử dụng trong thế giới thực, trong đó việc sắp xếp các mảng đã có một số thứ tự từ trước là phổ biến, thì Timsort là một lựa chọn tuyệt vời. Khả năng thích ứng của nó làm cho nó trở thành một lựa chọn tuyệt vời để sắp xếp các mảng có độ dài bất kỳ Sự kết luậnSắp xếp là một công cụ thiết yếu trong bất kỳ bộ công cụ nào của Pythonista. Với kiến thức về các thuật toán sắp xếp khác nhau trong Python và cách tối đa hóa tiềm năng của chúng, bạn đã sẵn sàng triển khai các ứng dụng và chương trình nhanh hơn, hiệu quả hơn Trong hướng dẫn này, bạn đã học
Bạn cũng đã học về các kỹ thuật khác nhau như đệ quy, chia để trị và ngẫu nhiên hóa. Đây là những nền tảng cơ bản để giải quyết một danh sách dài các thuật toán khác nhau và chúng sẽ xuất hiện lặp đi lặp lại khi bạn tiếp tục nghiên cứu Lấy mã được trình bày trong hướng dẫn này, tạo các thử nghiệm mới và khám phá thêm các thuật toán này. Tốt hơn nữa, hãy thử triển khai các thuật toán sắp xếp khác trong Python. Danh sách này rất rộng, nhưng sắp xếp lựa chọn, sắp xếp theo đống và sắp xếp theo cây là ba tùy chọn tuyệt vời để bắt đầu Đánh dấu là đã hoàn thành Xem ngay Hướng dẫn này có một khóa học video liên quan do nhóm Real Python tạo. Xem nó cùng với hướng dẫn bằng văn bản để hiểu sâu hơn. Giới thiệu về thuật toán sắp xếp trong Python 🐍 Thủ thuật Python 💌 Nhận một Thủ thuật Python ngắn và hấp dẫn được gửi đến hộp thư đến của bạn vài ngày một lần. Không có thư rác bao giờ. Hủy đăng ký bất cứ lúc nào. Được quản lý bởi nhóm Real Python Gửi cho tôi thủ thuật Python » Giới thiệu về Santiago Valdarrama Santiago là một kỹ sư phần mềm và máy học chuyên xây dựng các ứng dụng phần mềm doanh nghiệp » Thông tin thêm về SantiagoMỗi hướng dẫn tại Real Python được tạo bởi một nhóm các nhà phát triển để nó đáp ứng các tiêu chuẩn chất lượng cao của chúng tôi. Các thành viên trong nhóm đã làm việc trong hướng dẫn này là Aldren Geir Arne Jon Joanna Gia-cốp Bậc thầy Kỹ năng Python trong thế giới thực Với quyền truy cập không giới hạn vào Python thực Tham gia với chúng tôi và có quyền truy cập vào hàng nghìn hướng dẫn, khóa học video thực hành và cộng đồng các Pythonistas chuyên gia Nâng cao kỹ năng Python của bạn » Bậc thầy Kỹ năng Python trong thế giới thực Tham gia với chúng tôi và có quyền truy cập vào hàng ngàn hướng dẫn, khóa học video thực hành và cộng đồng các chuyên gia Pythonistas Nâng cao kỹ năng Python của bạn » Bạn nghĩ sao? Đánh giá bài viết này Tweet Chia sẻ Chia sẻ EmailBài học số 1 hoặc điều yêu thích mà bạn đã học được là gì? Mẹo bình luận. Những nhận xét hữu ích nhất là những nhận xét được viết với mục đích học hỏi hoặc giúp đỡ các sinh viên khác. Nhận các mẹo để đặt câu hỏi hay và nhận câu trả lời cho các câu hỏi phổ biến trong cổng thông tin hỗ trợ của chúng tôi Thuật toán với ví dụ trong Python là gì?Các thuật toán trong Python là gì? . Vì các thuật toán không dành riêng cho ngôn ngữ nên chúng có thể được thực hiện bằng một số ngôn ngữ lập trình. Không có quy tắc chuẩn nào hướng dẫn viết thuật toán
Các thuật toán có được viết bằng Python không?Python có tốt cho việc phát triển và triển khai các thuật toán không? . Python là một trong những ngôn ngữ lập trình mạnh mẽ nhưng dễ tiếp cận nhất hiện có và nó rất tốt để triển khai các thuật toán. Yes, Python is a powerful programming language that handles all aspects of algorithms very well. Python is one of the most powerful, yet accessible, programming languages in existence, and it's very good for implementing algorithms.
Làm cách nào tôi có thể tạo thuật toán của riêng mình?Cách xây dựng thuật toán theo sáu bước . Bước 1. Xác định mục tiêu của thuật toán Bước 2. Truy cập dữ liệu lịch sử và hiện tại Bước 3. Chọn các mô hình phù hợp Bước 4. tinh chỉnh Bước 5. Trực quan hóa kết quả của bạn Bước 6. Chạy thuật toán của bạn liên tục Thuật toán được viết như thế nào?Một thuật toán không phải là mã máy tính; . Nó không vòng vo. Nó rất rõ ràng và hiệu quả, đồng thời có phần mở đầu, phần giữa và phần cuối. written in plain English and may be in the form of a flowchart with shapes and arrows, a numbered list, or pseudocode (a semi-programming language). It doesn't beat around the bush. It's very clear and efficient, and it has a start, middle, and end. |