Pitondaki setlerin 'tuhaf' sıralaması


14

Bir Python 3.8.0 listesini bir kümeye dönüştürdüğümde, ortaya çıkan küme sırası * önemsiz olmayan bir şekilde son derece yapılandırılmıştır. Bu yapı yalancı rasgele listeden nasıl çıkarılıyor?


Çalıştığım bir deneyin parçası olarak rastgele bir set oluşturuyorum. Seti çizmenin birdenbire sette beklenmedik doğrusal yapı gösterdiğini görünce şaşırdım. Bu yüzden beni şaşırtan iki şey var - neden belirli bir sonuca dönüştürmenin bu yapıyı vurgulayan bir sıralaması * var; ve daha az bir ölçüde sahte rastgele kümenin neden bu "gizli" yapıya sahip olduğu?

Kod:

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

hangi çıktılar, örneğin

[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}

Yukarıdaki listenin bir grafiği ** beklendiği gibi oldukça rastgele görünüyor:

Rastgele oluşturulmuş listenin WolframAlpha çizimi

buna karşılık kümenin çizilmesi (çıktıda sıralandığı gibi) kümede bulunan yapıyı gösterir:

Rasgele listeden kümenin Alfa grafiği

Bu davranış, yukarıdaki kodda kullanılan 250 ve 30 değerleri ile makinemde% 100 tutarlı (daha fazla örnek) (kullandığım örnek kiraz toplanmış değil - sadece son koştuğum). Bu değerlerin ayarlanması bazen biraz farklı bir yapıya neden olabilir (örneğin, iki yerine üç aritmetik ilerlemenin alt kümesi ***).

Bu diğer insanların makinelerinde tekrarlanabilir mi? Tabii ki, bu tür bir yapı var, o kadar da büyük olmayan bir sahte rastgele sayı üretiminin göstergesi gibi görünüyor, ancak bu, bir kümeye dönüştürmenin bir anlamda bu yapıyı nasıl 'ayıklayacağını' açıklamıyor. Bildiğim kadarıyla, bir setin (bir listeden dönüştürüldüğünde) siparişinin belirleyici olduğuna dair resmi bir garanti yoktur (ve öyle olsa bile, arka planda karmaşık bir sipariş yapılmaz). Peki bu nasıl oluyor ?!


(*): Biliyorum, setleri sırasız koleksiyonları vardır, ama demek çağrılırken, anlamında "sipariş" printdeyimi, seti de çıkışı bazı tutarlı yatan set yapısını vurgular sırayla.

(**): Bu araziler Wolfram Alpha'dan. İki örnek daha aşağıdadır:

resim açıklamasını buraya girin

(***): Rastgele sayıların aralığını 250'den 500'e değiştirirken iki çizim:

resim açıklamasını buraya girin

Yanıtlar:


14

Temel olarak, bunun iki nedeni vardır:

  • Python'da bir küme bir karma tablo kullanılarak uygulanır ,
  • Bir tamsayının karması tamsayının kendisidir.

Bu nedenle, temel dizide bir tamsayı görünen dizin, tamsayı değeri, modulo temel dizinin uzunluğu ile belirlenir. Yani, tamsayılar bir kümeye bitişik bir aralık koyduğunuzda, tamsayılar artan düzende kalma eğilimindedir:

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

Bitişik bir aralıktaki tüm sayılara sahip değilseniz, "temel dizinin uzunluğu modulo" kısmı devreye girer:

>>> 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]

Temel dizinin uzunluğunu ve öğe eklemek için (deterministik) algoritmayı biliyorsanız dizi tahmin edilebilir. Bu durumda, dizinin uzunluğu 32'dir, çünkü başlangıçta 8'dir ve öğeler eklenirken dört katına çıkar .

Sonuna yakın bir bip haricinde (52 ve 56 sayıları kümede olmadığından), aralık iki diziye ayrılır 0, 4, 8, ...ve 32, 36, 40, ...bu, dönüşümlü olarak, sayıların değerleri olan hashlerin kendileri için modulo 32 alınır. dizideki dizinler. Çarpışmalar var; örneğin, 4 ve 36 eşit modulo 32'dir, ancak ilk önce sete 4 eklenir, böylece 36 farklı bir indekste sonuçlanır.

İşte bu dizi için bir grafik. Grafiklerinizdeki yapı sadece daha gürültülü bir sürümdür, çünkü sayılarınızı bir adımla bir aralıktan ziyade rastgele oluşturdunuz.

resim açıklamasını buraya girin

Serpiştirilmiş dizilerin sayısı, kümenin sayıların örneklendiği aralığın uzunluğuna orantılı olarak büyüklüğüne bağlı olacaktır, çünkü bu, aralığın uzunluğunun, hashtable'ın temel dizisinin uzunluğunu "kaç kez" sardığını "belirler. Burada üç içe geçmiş dizileri ile bir örnek 0, 6, 12, ..., 66, 72, 78, ...ve 36, 42, 48, ...:

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

Ah! Bu açıklıyor (ve güzel açıklama da)!
John Don

Ve elbette, parsellerdeki bu paternin setin altında yatan yapı ile bir ilgisi yok (bu paternin örneğimde olduğu gibi rastgele listelere sahip parsellerde ortaya çıkmasını bekleriz) ... Ben sadece araziler!
John Don

30'un altında yatan dizinin uzunluğu olduğunu nasıl buluyorsunuz?
Mark Snyder

@MarkSnyder 32 olduğu ortaya çıktı, bu da çarpışmalar olduğu anlamına geliyor, ancak sipariş modulo 30 ile aynı.
kaya3

2
@MarkSnyder Dizi, 2/3'ten fazla dolu olduğunda yeniden boyutlandırılır , çünkü dizinin dolu veya neredeyse dolu olmasına izin verirseniz, bir hashtable'ın performansı çok önemli ölçüde düşer.
kaya3
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.