'Đặt hàng' các bộ trong python


14

Khi tôi chuyển đổi danh sách Python 3.8.0 thành một tập hợp, thứ tự tập kết quả * có cấu trúc cao theo cách không tầm thường. Làm thế nào cấu trúc này được trích xuất từ ​​danh sách giả ngẫu nhiên?


Là một phần của thử nghiệm tôi đang chạy, tôi đang tạo một bộ ngẫu nhiên. Tôi đã rất ngạc nhiên khi thấy rằng việc vẽ đồ thị đột nhiên cho thấy cấu trúc tuyến tính bất ngờ trong tập hợp. Vì vậy, có hai điều khiến tôi bối rối - tại sao việc chuyển đổi thành kết quả đã đặt lại có thứ tự * kết thúc làm nổi bật cấu trúc này; và, ở mức độ thấp hơn tại sao tập giả ngẫu nhiên lại có cấu trúc "ẩn" này?

Mật mã:

X = [randrange(250) for i in range(30)]
print(X)
print(set(X))

đầu ra nào, ví dụ

[238, 202, 245, 94, 111, 106, 148, 164, 154, 113, 128, 10, 196, 141, 69, 38, 106, 8, 40, 53, 160, 87, 85, 13, 38, 147, 204, 50, 162, 91]

{128, 8, 10, 141, 13, 147, 148, 154, 160, 162, 164, 38, 40, 50, 53, 196, 69, 202, 204, 85, 87, 91, 94, 106, 238, 111, 113, 245}

Một âm mưu ** của danh sách trên trông khá ngẫu nhiên, như mong đợi:

Biểu đồ WolframAlpha của danh sách được tạo ngẫu nhiên

trong khi vẽ sơ đồ tập hợp (vì nó được sắp xếp ở đầu ra) thể hiện cấu trúc có trong tập hợp:

Đồ thị của WolframAlpha từ danh sách ngẫu nhiên

Hành vi này phù hợp 100% trên máy của tôi (nhiều ví dụ bên dưới) với các giá trị 250 và 30 được sử dụng trong đoạn mã trên (ví dụ tôi sử dụng không phải là cherry được chọn - nó chỉ là cái cuối cùng tôi chạy). Điều chỉnh các giá trị này đôi khi dẫn đến cấu trúc hơi khác nhau (ví dụ: tập hợp con của ba tiến trình số học *** thay vì hai).

Đây có phải là tái sản xuất trên máy của người khác? Tất nhiên, cấu trúc như vậy tồn tại dường như chỉ ra một thế hệ số giả ngẫu nhiên không quá lớn, nhưng điều này không giải thích cách chuyển đổi thành một tập hợp theo nghĩa nào đó sẽ "giải nén" cấu trúc này. Theo như tôi biết, không có gì đảm bảo chính thức rằng thứ tự của một tập hợp (khi được chuyển đổi từ danh sách) là xác định (và ngay cả khi nó là, không có thứ tự phức tạp nào được thực hiện trong nền). Vậy chuyện này xảy ra thế nào?!


(*): Tôi biết, các tập hợp là các bộ sưu tập không có thứ tự, nhưng ý tôi là "được đặt hàng" theo nghĩa là, khi gọi printcâu lệnh, tập hợp là đầu ra theo một số thứ nhất quán làm nổi bật cấu trúc bộ bên dưới.

(**): Những mảnh đất này là của Wolfram Alpha. Hai ví dụ nữa dưới đây:

nhập mô tả hình ảnh ở đây

(***): Hai ô khi thay đổi phạm vi của các số ngẫu nhiên từ 250 đến 500:

nhập mô tả hình ảnh ở đây

Câu trả lời:


14

Về cơ bản, điều này là do hai điều:

  • Một tập hợp trong Python được triển khai bằng cách sử dụng hàm băm ,
  • Băm của một số nguyên là chính số nguyên.

Do đó, chỉ số mà một số nguyên xuất hiện trong mảng bên dưới sẽ được xác định bởi giá trị của số nguyên, điều chỉnh độ dài của mảng bên dưới. Vì vậy, các số nguyên sẽ có xu hướng theo thứ tự tăng dần khi bạn đặt một phạm vi liền kề của chúng vào một tập hợp:

>>> list(set(range(10000))) == list(range(10000))
True # this can't be an accident!

Nếu bạn không có tất cả các số từ một phạm vi liền kề, thì phần "modulo chiều dài của mảng bên dưới" sẽ xuất hiện:

>>> r = range(0, 50, 4)
>>> set(r)
{0, 32, 4, 36, 8, 40, 12, 44, 16, 48, 20, 24, 28}
>>> sorted(r, key=lambda x: x % 32)
[0, 32, 4, 36, 8, 40, 12, 44, 16, 48, 20, 24, 28]

Trình tự có thể dự đoán được nếu bạn biết độ dài của mảng bên dưới và thuật toán (xác định) để thêm các phần tử. Trong trường hợp này, độ dài của mảng là 32, vì ban đầu là 8 và được tăng gấp bốn lần trong khi các phần tử được thêm vào.

Ngoại trừ một đốm sáng gần cuối (vì các số 52 và 56 không có trong tập hợp), phạm vi được chia thành hai chuỗi 0, 4, 8, ...32, 36, 40, ...xen kẽ vì các giá trị băm, chính là các giá trị của số, được lấy modulo 32 để chọn các chỉ số trong mảng. Có va chạm; ví dụ: 4 và 36 là modulo 32 bằng nhau, nhưng 4 được thêm vào tập đầu tiên để 36 kết thúc ở một chỉ mục khác nhau.

Đây là một biểu đồ cho chuỗi này. Cấu trúc trong biểu đồ của bạn chỉ là một phiên bản ồn ào hơn, bởi vì bạn đã tạo các số của mình một cách ngẫu nhiên thay vì từ một phạm vi với một bước.

nhập mô tả hình ảnh ở đây

Số lượng các chuỗi xen kẽ sẽ phụ thuộc vào kích thước của tập hợp tỷ lệ với độ dài của phạm vi mà các số được lấy mẫu từ đó, vì điều đó xác định số lần độ dài của phạm vi "bao quanh" modulo độ dài của mảng bên dưới của hàm băm. Dưới đây là một ví dụ với ba trình tự xen kẽ 0, 6, 12, ..., 66, 72, 78, ...36, 42, 48, ...:

>>> set(range(0, 90, 6))
{0, 66, 36, 6, 72, 42, 12, 78, 48, 18, 84, 54, 24, 60, 30}

Ah! Điều đó giải thích nó (và giải thích tốt đẹp quá)!
John Don

Và tất nhiên, mẫu này trong các ô không liên quan gì đến cấu trúc cơ bản trong tập hợp (chúng tôi hy vọng mẫu này sẽ phát sinh trong các ô với các danh sách ngẫu nhiên như trong ví dụ của tôi) ... Tôi chỉ bị quyến rũ bởi các mẫu bất ngờ trong cốt truyện!
John Don

Làm thế nào để bạn thấy rằng 30 là chiều dài của mảng bên dưới?
Mark Snyder

@MarkSnyder Hóa ra là 32, có nghĩa là có va chạm, nhưng thứ tự giống như khi nó là modulo 30.
kaya3

2
@MarkSnyder Mảng sẽ được thay đổi kích thước nếu nó đầy hơn 2/3 , vì hiệu suất của hashtable giảm đáng kể nếu bạn để mảng đầy hoặc gần đầy.
kaya3
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.