Người nói là sai trong trường hợp này. Chi phí thực tế là
import random
data = [random.uniform[0, 1] for _ in range[10_000_000]]
sorted[data][:10]
8. Heapify chỉ được gọi trên các yếu tố import random
data = [random.uniform[0, 1] for _ in range[10_000_000]]
sorted[data][:10]
9 đầu tiên của Itable. Đó là [0.22276864861022627,
0.34177789369722766,
0.37737542995066387,
0.45103237357931525,
0.47123333399847134,
0.47861174271801377,
0.4913566066457804,
0.49333286645473995,
0.584151135096475,
0.7235123209516772]
0, nhưng không đáng kể nếu import random
data = [random.uniform[0, 1] for _ in range[10_000_000]]
sorted[data][:10]
9 nhỏ hơn nhiều so với [0.22276864861022627,
0.34177789369722766,
0.37737542995066387,
0.45103237357931525,
0.47123333399847134,
0.47861174271801377,
0.4913566066457804,
0.49333286645473995,
0.584151135096475,
0.7235123209516772]
2. Sau đó, tất cả các yếu tố còn lại được thêm vào "đống nhỏ" này thông qua [0.22276864861022627,
0.34177789369722766,
0.37737542995066387,
0.45103237357931525,
0.47123333399847134,
0.47861174271801377,
0.4913566066457804,
0.49333286645473995,
0.584151135096475,
0.7235123209516772]
3, mỗi lần một. Điều đó mất [0.22276864861022627,
0.34177789369722766,
0.37737542995066387,
0.45103237357931525,
0.47123333399847134,
0.47861174271801377,
0.4913566066457804,
0.49333286645473995,
0.584151135096475,
0.7235123209516772]
4 thời gian cho mỗi lần gọi [0.22276864861022627,
0.34177789369722766,
0.37737542995066387,
0.45103237357931525,
0.47123333399847134,
0.47861174271801377,
0.4913566066457804,
0.49333286645473995,
0.584151135096475,
0.7235123209516772]
3. Độ dài của đống vẫn còn import random
data = [random.uniform[0, 1] for _ in range[10_000_000]]
sorted[data][:10]
9 trong suốt. Cuối cùng, đống được sắp xếp, có giá [0.22276864861022627,
0.34177789369722766,
0.37737542995066387,
0.45103237357931525,
0.47123333399847134,
0.47861174271801377,
0.4913566066457804,
0.49333286645473995,
0.584151135096475,
0.7235123209516772]
7, nhưng điều đó cũng không đáng kể nếu import random
data = [random.uniform[0, 1] for _ in range[10_000_000]]
sorted[data][:10]
9 nhỏ hơn nhiều so với [0.22276864861022627,
0.34177789369722766,
0.37737542995066387,
0.45103237357931525,
0.47123333399847134,
0.47861174271801377,
0.4913566066457804,
0.49333286645473995,
0.584151135096475,
0.7235123209516772]
2.Vui vẻ với lý thuyết ;-]
Có nhiều cách dễ dàng để tìm ra yếu tố lớn nhất trong thời gian
from heapq import nsmallest
nsmallest[10, data]
0 dự kiến; Ví dụ, xem ở đây. Có nhiều cách khó hơn để làm điều đó trong trường hợp xấu nhất from heapq import nsmallest
nsmallest[10, data]
0 thời gian. Sau đó, trong một lần vượt qua đầu vào, bạn có thể xuất ra các yếu tố import random
data = [random.uniform[0, 1] for _ in range[10_000_000]]
sorted[data][:10]
9> = lớn nhất [với các biến chứng tẻ nhạt trong trường hợp trùng lặp]. Vì vậy, toàn bộ công việc có thể được thực hiện trong thời gian from heapq import nsmallest
nsmallest[10, data]
0.Nhưng những cách đó yêu cầu bộ nhớ
from heapq import nsmallest
nsmallest[10, data]
0 quá. Python không sử dụng chúng. Một lợi thế của những gì thực sự được thực hiện là gánh nặng bộ nhớ "thêm" trong trường hợp xấu nhất là [0.22276864861022627,
0.34177789369722766,
0.37737542995066387,
0.45103237357931525,
0.47123333399847134,
0.47861174271801377,
0.4913566066457804,
0.49333286645473995,
0.584151135096475,
0.7235123209516772]
0 và điều đó có thể rất có ý nghĩa khi đầu vào, ví dụ, một trình tạo tạo ra nhiều giá trị lớn. Steve bước vào phòng cấp cứu lúc 2 giờ chiều với cơn đau bụng dữ dội. Vào lúc 2:01, Allen bị ho nặng. Vào lúc 2:02, Shirley vấp ngã với một vết thương do dao ở bên cạnh. Một khi bác sĩ được tự do, ai nên được nhìn thấy trước? Hầu hết chúng ta sẽ nói rằngTham khảo Python
Tổng quan và hướng dẫn về mô -đun Python
Nhìn vào thư viện Python Heapq
6from heapq import nsmallest
nsmallest[10, data]
Quay trở lại blog
Bởi John Lekberg vào ngày 01 tháng 11 năm 2020.
Bài đăng trên blog Python tuần này là về mô -đun HEAPQ của Python. Bạn sẽ học:
- Tại sao bạn nên quan tâm đến đống và
6.from heapq import nsmallest nsmallest[10, data]
- Làm thế nào để có được hiệu quả các bản ghi nhỏ nhất [và lớn nhất] từ bộ dữ liệu.
- Cách hợp nhất nhiều bộ dữ liệu được sắp xếp thành một bộ dữ liệu được sắp xếp.
- Làm thế nào để tạo và thao tác đống.
Tại sao bạn nên quan tâm đến đống và from heapq import nsmallest
nsmallest[10, data]
6
from heapq import nsmallest
nsmallest[10, data]
Bạn nên quan tâm đến đống vì chúng là một cấu trúc dữ liệu cho phép bạn nhanh chóng truy cập các yếu tố nhỏ nhất [hoặc lớn nhất] của bộ dữ liệu mà không cần phải sắp xếp toàn bộ bộ dữ liệu.
Ví dụ. Tôi muốn nhận được 10 số nhỏ nhất từ bộ dữ liệu gồm 10.000.000 số. Sắp xếp và trượt đều mất 3,8 giây, nhưng sử dụng một đống mất 0,3 s!
import cProfile import heapq import random data = [random.uniform[0, 1] for _ in range[10_000_000]] cProfile.run["sorted[data][:10]"]
4 function calls in 3.816 seconds
cProfile.run['heapq.nsmallest[10, data]']
143 function calls in 0.289 seconds
Mô-đun HEAPQ của Python thực hiện các bản heaps nhị phân bằng danh sách. Nó cung cấp một API để trực tiếp tạo và thao tác các đống, cũng như một bộ chức năng tiện ích cấp cao hơn: heapq.nsmallest, heapq.nlargest và heapq.merge.
Có được các bản ghi nhỏ nhất [và lớn nhất] từ bộ dữ liệu
Nếu bạn có bộ dữ liệu, bạn có thể có được K nhỏ nhất hoặc lớn nhất bằng cách sắp xếp và cắt nó. Nhưng, nó có thể hiệu quả hơn khi sử dụng heapq.nsmallest và heapq.nlargest.
Ví dụ. Tôi muốn 10 bản ghi nhỏ nhất từ bộ dữ liệu với 10.000.000 bản ghi:
import random data = [random.uniform[0, 1] for _ in range[10_000_000]] sorted[data][:10]
[0.22276864861022627,
0.34177789369722766,
0.37737542995066387,
0.45103237357931525,
0.47123333399847134,
0.47861174271801377,
0.4913566066457804,
0.49333286645473995,
0.584151135096475,
0.7235123209516772]
Và, tôi cũng có thể sử dụng Heapq.nsmallest để làm điều này:
from heapq import nsmallest nsmallest[10, data]
[2.4336311188477566e-08,
2.4653716934608383e-07,
2.484063295060679e-07,
3.0462511246831525e-07,
3.9886920166765094e-07,
4.797817356738676e-07,
5.917942023092593e-07,
8.555260716525126e-07,
1.207491232779745e-06,
1.2146108545607603e-06]
nsmallest[10, data] == sorted[data][:10]
True
Nhưng Heapq.nsmallest chạy một thứ tự nhanh hơn. Ví dụ. 0,29 giây so với 3,91 giây:
04 function calls in 3.816 seconds
4 function calls in 3.816 seconds
124 function calls in 3.816 seconds
4 function calls in 3.816 seconds
3Điều này là do độ phức tạp về thời gian của việc lấy K bản ghi nhỏ nhất từ bộ dữ liệu của N phần tử là ...
- O [n log n] với phân loại và cắt.
- O [n + k log n] với heapq.nsmallest. [Nếu k không đổi, thì đây là o [n].]
Vì vậy, bạn nên sử dụng Sắp xếp hoặc Danh sách.Sort? Còn Min và Max thì sao?
Nếu bạn chỉ cần bản ghi lớn nhất hoặc nhỏ nhất [K = 1], thì hãy sử dụng tối thiểu hoặc tối đa.
Nếu k là "nhỏ" - so với kích thước của bộ dữ liệu - thì hãy sử dụng heapq.nsmallest và heapq.nlargest.
Nếu không, sử dụng sắp xếp hoặc danh sách.sort.
Lý do mà tôi nói "nếu k là 'nhỏ'" là bởi vì - theo lý thuyết, mặc dù độ phức tạp về thời gian của heapq.nsmallest sẽ luôn luôn tốt như điều đó, o [n log n] - trong Thực hành, Timsort của Python có thể nhanh hơn khi k gần n.
Cách tốt nhất để tìm ra nếu bạn nên sử dụng heapq.nsmallest hoặc được sắp xếp là thử cả hai và đo kết quả.
Hợp nhất các bộ dữ liệu được sắp xếp thành một bộ dữ liệu được sắp xếp
Nếu bạn có một số bộ dữ liệu được sắp xếp và bạn muốn hợp nhất chúng thành một bộ dữ liệu được sắp xếp duy nhất, bạn có thể kết hợp chúng và sắp xếp kết quả. .
Ví dụ. Tôi có 7 bộ dữ liệu được sắp xếp - trong các tệp trên đĩa - với 1.000.000 bản ghi:
4 function calls in 3.816 seconds
4Tôi muốn hợp nhất các bộ dữ liệu này thành một bộ dữ liệu được sắp xếp - trên đĩa. Tôi có thể làm điều này bằng cách kết hợp các bộ dữ liệu với itertools.chain.from_iterable và sau đó sử dụng chức năng tích hợp được sắp xếp:
4 function calls in 3.816 seconds
5Hoặc, tôi cũng có thể hợp nhất các bộ dữ liệu này với Heapq.merge:
4 function calls in 3.816 seconds
6Mặc dù Heapq.merge chạy chậm hơn một chút - ví dụ: 10,9 s so với 6,7 giây - nó sử dụng bộ nhớ ít hơn nhiều - ví dụ: 144 kb so với 811.036 kb:slower -- e.g., 10.9 s vs. 6.7 s -- it uses much less memory -- e.g., 144 kB vs. 811,036 kB:
74 function calls in 3.816 seconds
4 function calls in 3.816 seconds
894 function calls in 3.816 seconds
cProfile.run['heapq.nsmallest[10, data]']
01cProfile.run['heapq.nsmallest[10, data]']
cProfile.run['heapq.nsmallest[10, data]']
23cProfile.run['heapq.nsmallest[10, data]']
cProfile.run['heapq.nsmallest[10, data]']
4Sự cảnh báo ở đây là các bộ dữ liệu đầu vào phải được sắp xếp.
Tạo và thao tác các đống
Heapq cung cấp một API để tạo trực tiếp và điều khiển một min-heap nhị phân. Heapq đại diện cho một danh sách tối thiểu nhị phân làm danh sách, vì vậy một đống trống chỉ là một danh sách trống:
5cProfile.run['heapq.nsmallest[10, data]']
cProfile.run['heapq.nsmallest[10, data]']
5Một danh sách có thể được biến thành một đống tại chỗ bằng cách sử dụng Heapq.Heapify:in-place using heapq.heapify:
7cProfile.run['heapq.nsmallest[10, data]']
cProfile.run['heapq.nsmallest[10, data]']
8Phần tử tối thiểu là phần tử đầu tiên của danh sách:
9cProfile.run['heapq.nsmallest[10, data]']
143 function calls in 0.289 seconds
01143 function calls in 0.289 seconds
True
Bạn có thể đẩy các phần tử lên đống bằng Heapq.HeAppush và bạn có thể bật các phần tử ra khỏi đống bằng Heapq.HeAppop:
3143 function calls in 0.289 seconds
cProfile.run['heapq.nsmallest[10, data]']
85143 function calls in 0.289 seconds
143 function calls in 0.289 seconds
07143 function calls in 0.289 seconds
143 function calls in 0.289 seconds
89143 function calls in 0.289 seconds
import random
data = [random.uniform[0, 1] for _ in range[10_000_000]]
sorted[data][:10]
0Heapq cũng cung cấp các phương thức phím tắt Heapq.HeAppushpop và Heapq.Heapreplace:
Heapq.HeAppushpop có tác dụng tương tự như gọi Heapq.HeAppush sau đó Heapq.HeAppop. Nhưng, việc triển khai
3 hiệu quả hơn một chút so với gọi riêng lẻ[0.22276864861022627, 0.34177789369722766, 0.37737542995066387, 0.45103237357931525, 0.47123333399847134, 0.47861174271801377, 0.4913566066457804, 0.49333286645473995, 0.584151135096475, 0.7235123209516772]
0 và[2.4336311188477566e-08, 2.4653716934608383e-07, 2.484063295060679e-07, 3.0462511246831525e-07, 3.9886920166765094e-07, 4.797817356738676e-07, 5.917942023092593e-07, 8.555260716525126e-07, 1.207491232779745e-06, 1.2146108545607603e-06]
1.[2.4336311188477566e-08, 2.4653716934608383e-07, 2.484063295060679e-07, 3.0462511246831525e-07, 3.9886920166765094e-07, 4.797817356738676e-07, 5.917942023092593e-07, 8.555260716525126e-07, 1.207491232779745e-06, 1.2146108545607603e-06]
Heapq.Heapreplace có tác dụng tương tự như gọi Heapq.HeAppop sau đó Heapq.HeAppush. Nhưng,
2 hiệu quả hơn so với gọi riêng lẻ và[2.4336311188477566e-08, 2.4653716934608383e-07, 2.484063295060679e-07, 3.0462511246831525e-07, 3.9886920166765094e-07, 4.797817356738676e-07, 5.917942023092593e-07, 8.555260716525126e-07, 1.207491232779745e-06, 1.2146108545607603e-06]
0. Và[2.4336311188477566e-08, 2.4653716934608383e-07, 2.484063295060679e-07, 3.0462511246831525e-07, 3.9886920166765094e-07, 4.797817356738676e-07, 5.917942023092593e-07, 8.555260716525126e-07, 1.207491232779745e-06, 1.2146108545607603e-06]
2 không thay đổi kích thước của đống.[2.4336311188477566e-08, 2.4653716934608383e-07, 2.484063295060679e-07, 3.0462511246831525e-07, 3.9886920166765094e-07, 4.797817356738676e-07, 5.917942023092593e-07, 8.555260716525126e-07, 1.207491232779745e-06, 1.2146108545607603e-06]
Nếu bạn muốn tạo một đống kích thước cố định:
- Theo dõi kích thước heap bằng Len.
- Khi kích thước heap đạt đến "tối đa", sau đó chuyển từ sử dụng heapq.HeAppush sang sử dụng Heapq.Heapreplace hoặc Heapq.HeAppushPop.
Nếu bạn muốn tạo một heap tối đa [thay vì một min-heap]:
Nếu có thể, hãy lấy lại kịch bản thành các điều khoản của một min-heap. Nếu các bản ghi là số, phủ định các số [ví dụ: 5 trở thành -5]. Nói chung, bọc các bản ghi trong một đối tượng đảo ngược thứ tự. Ví dụ.
1import random data = [random.uniform[0, 1] for _ in range[10_000_000]] sorted[data][:10]
True
3import random data = [random.uniform[0, 1] for _ in range[10_000_000]] sorted[data][:10]
4import random data = [random.uniform[0, 1] for _ in range[10_000_000]] sorted[data][:10]
5import random data = [random.uniform[0, 1] for _ in range[10_000_000]] sorted[data][:10]
6import random data = [random.uniform[0, 1] for _ in range[10_000_000]] sorted[data][:10]
Nếu không, hãy thực hiện tối đa. Xem "Tôi sử dụng gì để triển khai tối đa trong Python?" thông qua stackoverflow.com để biết thêm thông tin.
Tóm lại là...
Trong bài đăng tuần này, bạn đã tìm hiểu về mô -đun HEAPQ của Python. Bạn đã học cách trực tiếp tạo và thao tác các mục tiêu tối thiểu nhị phân, cũng như cách sử dụng các hàm tiện ích cấp cao để có được K nhỏ nhất [hoặc lớn nhất] từ bộ dữ liệu và hợp nhất nhiều bộ dữ liệu được sắp xếp vào một bộ dữ liệu được sắp xếp.
Thử thách của tôi với bạn:
Sử dụng API được cung cấp bởi Heapq, tạo lại các chức năng tiện ích Heapq.nsmallest, Heapq.nlargest và Heapq.merge. Ví dụ. Dưới đây là việc tái tạo HEAPQ.NSMALLEST:
7import random data = [random.uniform[0, 1] for _ in range[10_000_000]] sorted[data][:10]
Nếu bạn thích bài đăng trong tuần này, hãy chia sẻ nó với bạn bè của bạn và theo dõi bài đăng vào tuần tới. Gặp bạn sau!
[Nếu bạn phát hiện ra bất kỳ lỗi hoặc lỗi chính tả nào trên bài đăng này, liên hệ với tôi qua trang liên hệ của tôi.]