Có gì sai với thuật toán xáo trộn tinh nghịch này của người Viking?


23

Đây là phần tiếp theo của câu hỏi Stackoverflow về việc xáo trộn một mảng ngẫu nhiên .

Có các thuật toán đã được thiết lập (chẳng hạn như Shuffle Knuth-Fisher-Yates ) mà người ta nên sử dụng để xáo trộn một mảng, thay vì dựa vào các triển khai ad-hoc "ngây thơ".

Bây giờ tôi quan tâm đến việc chứng minh (hoặc từ chối) rằng thuật toán ngây thơ của tôi bị hỏng (như trong: không tạo ra tất cả các hoán vị có thể có xác suất bằng nhau).

Đây là thuật toán:

Lặp lại một vài lần (độ dài của mảng nên làm) và trong mỗi lần lặp, lấy hai chỉ mục mảng ngẫu nhiên và hoán đổi hai phần tử ở đó.

Rõ ràng, điều này cần nhiều số ngẫu nhiên hơn KFY (gấp đôi so với), nhưng ngoài việc nó có hoạt động đúng không? Và số lần lặp thích hợp là bao nhiêu ("độ dài của mảng" là đủ)?


4
Tôi chỉ không thể hiểu tại sao mọi người nghĩ rằng việc hoán đổi này là 'đơn giản' hoặc 'ngây thơ' hơn FY ... Khi tôi giải quyết vấn đề này lần đầu tiên tôi mới thực hiện FY (không biết nó thậm chí còn có tên) , chỉ vì nó có vẻ là cách đơn giản nhất để làm điều đó cho tôi.

1
@mbq: Cá nhân tôi thấy chúng dễ như nhau, mặc dù tôi đồng ý rằng FY có vẻ "tự nhiên" hơn đối với tôi.
nico

3
Khi tôi nghiên cứu các thuật toán xáo trộn sau khi tự viết (một thực tế tôi đã từ bỏ), tôi hoàn toàn là "tào lao thần thánh, nó đã được thực hiện và nó có tên !!"
JM không phải là một thống kê

Câu trả lời:


12

Nó bị hỏng, mặc dù nếu bạn thực hiện đủ xáo trộn thì đó có thể là một xấp xỉ tuyệt vời (như các câu trả lời trước đã chỉ ra).

Chỉ cần để có được một xử lý về những gì đang xảy ra, xem xét mức độ thường xuyên thuật toán của bạn sẽ tạo shuffles của một mảng yếu tố, trong đó yếu tố đầu tiên là cố định, k 2 . Khi hoán vị được tạo ra với xác suất bằng nhau, điều này sẽ xảy ra 1 / k thời gian. Đặt p n là tần số tương đối của sự xuất hiện này sau khi n xáo trộn với thuật toán của bạn. Chúng ta cũng hãy hào phóng và giả sử bạn thực sự chọn các cặp chỉ số riêng biệt một cách ngẫu nhiên cho các xáo trộn của mình, sao cho mỗi cặp được chọn với xác suất =kk21/kpnn 2/(k(k-1))1/(k2)2/(k(k1)). (Điều này có nghĩa là không có sự xáo trộn "tầm thường" bị lãng phí. Mặt khác, nó hoàn toàn phá vỡ thuật toán của bạn cho một mảng hai phần tử, bởi vì bạn xen kẽ giữa việc sửa hai phần tử và hoán đổi chúng, vì vậy nếu bạn dừng lại sau một số lượng được xác định trước các bước, không có sự ngẫu nhiên cho kết quả nào!)

Tần số này thỏa mãn một sự tái phát đơn giản, bởi vì phần tử đầu tiên được tìm thấy ở vị trí ban đầu của nó sau khi xáo trộn theo hai cách khác nhau. Một là nó đã được sửa sau xáo trộn và lần xáo trộn tiếp theo không di chuyển phần tử đầu tiên. Khác là nó đã được di chuyển sau shuffles nhưng shuffle di chuyển nó trở lại. Cơ hội không di chuyển phần tử đầu tiên bằng = , trong khi đó cơ hội di chuyển phần tử đầu tiên trở lại bằng = . Từ đâun n n + 1 s t ( k - 1n+1nnn+1st (k-2)/k1/ ( k(k12)/(k2)(k2)/k 2/(k(k-1))1/(k2)2/(k(k1))

p0=1
vì phần tử đầu tiên bắt đầu ở đúng vị trí của nó;

pn+1=k2kpn+2k(k1)(1pn).

Giải pháp là

pn=1/k+(k3k1)nk1k.

Trừ đi , chúng ta thấy rằng tần số bị sai bởi . Đối với và , một xấp xỉ tốt là . Điều này cho thấy rằng lỗi trong tần số cụ thể này sẽ giảm theo cấp số nhân với số lần hoán đổi so với kích thước của mảng ( ), cho thấy sẽ rất khó phát hiện với các mảng lớn nếu bạn đã thực hiện một số lượng hoán đổi tương đối lớn - nhưng lỗi luôn ở đó.( k - 31/k knk-1(k3k1)nk1kknn/kk1kexp(2nk1)n/k

Thật khó để cung cấp một phân tích toàn diện về các lỗi trong tất cả các tần số. Tuy nhiên, có khả năng họ sẽ hành xử như thế này, tuy nhiên, điều đó cho thấy rằng ở mức tối thiểu bạn sẽ cần (số lần hoán đổi) đủ lớn để khiến lỗi nhỏ đến mức chấp nhận được. Một giải pháp gần đúng làn

n>12(1(k1)log(ϵ))

trong đó phải rất nhỏ so với . Điều này ngụ ý nên nhiều lần cho các xấp xỉ thô ( nghĩa là , trong đó theo thứ tự lần hoặc hơn.)1 / k n k ϵ 0,01 1 / kϵ1/knkϵ0.011/k

Tất cả điều này đặt ra câu hỏi: tại sao bạn lại chọn sử dụng một thuật toán không hoàn toàn (nhưng chỉ xấp xỉ) chính xác, sử dụng các kỹ thuật chính xác giống như một thuật toán khác có thể chứng minh được, nhưng đòi hỏi phải tính toán nhiều hơn?

Chỉnh sửa

Nhận xét của Thilo là thích hợp (và tôi đã hy vọng không ai chỉ ra điều này, vì vậy tôi có thể được tha thứ cho công việc làm thêm này!). Hãy để tôi giải thích logic.

  • Nếu bạn chắc chắn tạo ra các giao dịch hoán đổi thực tế mỗi lần, bạn hoàn toàn bị lừa. Vấn đề tôi chỉ ra cho trường hợp kéo dài đến tất cả các mảng. Chỉ một nửa của tất cả các hoán vị có thể có thể có được bằng cách áp dụng một số lượng hoán đổi chẵn; nửa còn lại có được bằng cách áp dụng một số lượng lớn các giao dịch hoán đổi. Do đó, trong tình huống này, bạn không bao giờ có thể tạo ra bất kỳ nơi nào gần một phân phối hoán vị thống nhất (nhưng có rất nhiều khả năng mà một nghiên cứu mô phỏng cho bất kỳ khá lớn nào sẽ không thể phát hiện ra vấn đề). Nó rất là tệ.kk=2k

  • Do đó, thật khôn ngoan khi tạo ra các giao dịch hoán đổi ngẫu nhiên bằng cách tạo ra hai vị trí một cách độc lập một cách ngẫu nhiên. Điều này có nghĩa là có cơ hội mỗi lần hoán đổi một phần tử với chính nó; đó là, không làm gì cả Quá trình này có hiệu quả làm chậm thuật toán một chút: sau bước, chúng tôi chỉ mong đợi về hoán đổi thực sự đã xảy ra.n k - 11/knk1kN<N

  • Lưu ý rằng kích thước của lỗi giảm đơn điệu với số lần hoán đổi riêng biệt. Do đó, trung bình thực hiện ít giao dịch hoán đổi cũng làm tăng lỗi, trung bình. Nhưng đây là một mức giá bạn nên sẵn sàng trả để khắc phục vấn đề được mô tả trong viên đạn đầu tiên. Do đó, ước tính lỗi của tôi thấp một cách bảo thủ, xấp xỉ theo hệ số .(k1)/k

Tôi cũng muốn chỉ ra một ngoại lệ rõ ràng thú vị: xem xét kỹ công thức lỗi cho thấy rằng không có lỗi trong trường hợp . Đây không phải là một sai lầm: nó là chính xác. Tuy nhiên, ở đây tôi chỉ kiểm tra một thống kê liên quan đến sự phân bố đồng đều các hoán vị. Thực tế là thuật toán có thể tái tạo một thống kê này khi (cụ thể là lấy đúng tần số hoán vị cố định bất kỳ vị trí nào) không đảm bảo các hoán vị thực sự được phân phối đồng đều. Thật vậy, sau giao dịch hoán đổi thực tế, các hoán vị khả dĩ duy nhất có thể được tạo ra là ,k=3k=32n(123)(321)và bản sắc Chỉ có cái sau sửa bất kỳ vị trí nào, vì vậy thực sự chính xác một phần ba hoán vị cố định vị trí. Nhưng một nửa hoán vị bị thiếu! Trong trường hợp khác, sau giao dịch hoán đổi thực tế, các hoán vị duy nhất có thể là , và . Một lần nữa, chính xác một trong số này sẽ sửa bất kỳ vị trí nào, vì vậy một lần nữa chúng ta có được tần số hoán vị chính xác cố định vị trí đó, nhưng một lần nữa chúng ta chỉ có được một nửa số hoán vị có thể.2n+1(12)(23)(13)

Ví dụ nhỏ này giúp tiết lộ các vấn đề chính của cuộc tranh luận: bằng cách "hào phóng", chúng tôi đánh giá thấp tỷ lệ lỗi cho một thống kê cụ thể. Vì tỷ lệ lỗi đó là khác không cho tất cả , chúng tôi thấy rằng thuật toán bị hỏng. Hơn nữa, bằng cách phân tích sự phân rã về tỷ lệ lỗi cho thống kê này, chúng tôi thiết lập giới hạn thấp hơn về số lần lặp của thuật toán cần có bất kỳ hy vọng nào về xấp xỉ phân phối hoán vị thống nhất.k4


1
"Chúng ta cũng hãy hào phóng, và giả sử bạn thực sự đang chọn các cặp chỉ số riêng biệt thống nhất một cách ngẫu nhiên cho các xáo trộn của bạn". Tôi không hiểu tại sao giả định đó có thể được đưa ra, và nó hào phóng như thế nào. Nó dường như loại bỏ các hoán vị có thể, dẫn đến một phân phối thậm chí ít ngẫu nhiên hơn.
Thilo

1
@Thilo: Cảm ơn bạn. Nhận xét của bạn xứng đáng có một câu trả lời mở rộng, vì vậy tôi đã đặt nó trong phản hồi. Hãy để tôi chỉ ra ở đây rằng "hào phóng" không thực sự loại bỏ bất kỳ hoán vị nào: nó chỉ loại bỏ các bước trong thuật toán mà nếu không thì chẳng làm được gì.
whuber

2
Vấn đề này có thể được phân tích đầy đủ dưới dạng chuỗi Markov trên biểu đồ Cayley của nhóm hoán vị. Các phép tính số cho k = 1 đến 7 (ma trận 5040 x 5040!) Xác nhận rằng các giá trị riêng lớn nhất về kích thước (sau 1 và -1) là chính xác . Điều này ngụ ý rằng một khi bạn đã đối phó với vấn đề xen kẽ dấu hiệu hoán vị (tương ứng với giá trị riêng của -1), các lỗi trong tất cả các xác suất sẽ phân rã theo tỷ lệ hoặc nhanh hơn Tôi nghi ngờ điều này tiếp tục giữ cho tất cả lớn hơn . (k3)/(k1)=12/(k1)(12/(k1))nk
whuber

1
Bạn có thể làm tốt hơn vì xác suất là bất biến trên các lớp liên hợp và chỉ có phân vùng là để bạn có thể phân tích ma trận . 5040×504015715×15
Douglas Zare

8

Tôi nghĩ rằng thuật toán đơn giản của bạn sẽ xáo trộn các thẻ một cách chính xác vì số lần xáo trộn có xu hướng vô cùng.

Giả sử bạn có ba thẻ: {A, B, C}. Giả sử rằng thẻ của bạn bắt đầu theo thứ tự sau: A, B, C. Sau một lần xáo trộn, bạn có các kết hợp sau:

{A,B,C}, {A,B,C}, {A,B,C} #You get this if choose the same RN twice.
{A,C,B}, {A,C,B}
{C,B,A}, {C,B,A}
{B,A,C}, {B,A,C}

Do đó, xác suất thẻ A ở vị trí {1,2,3} là {5/9, 2/9, 2/9}.

Nếu chúng ta xáo trộn các thẻ lần thứ hai, thì:

Pr(A in position 1 after 2 shuffles) = 5/9*Pr(A in position 1 after 1 shuffle) 
                                     + 2/9*Pr(A in position 2 after 1 shuffle) 
                                     + 2/9*Pr(A in position 3 after 1 shuffle) 

Điều này mang lại cho 0.407.

Sử dụng cùng một ý tưởng, chúng ta có thể hình thành mối quan hệ lặp lại, nghĩa là:

Pr(A in position 1 after n shuffles) = 5/9*Pr(A in position 1 after (n-1) shuffles) 
                                     + 2/9*Pr(A in position 2 after (n-1) shuffles) 
                                     + 2/9*Pr(A in position 3 after (n-1) shuffles).

Mã hóa mã này trong R (xem mã bên dưới), đưa ra xác suất thẻ A ở vị trí {1,2,3} là {0,3334, 0,3333, 0,3333} sau mười lần xáo trộn.

Mã R

## m is the probability matrix of card position
## Row is position
## Col is card A, B, C
m = matrix(0, nrow=3, ncol=3)
m[1,1] = 1; m[2,2] = 1; m[3,3] = 1

## Transition matrix
m_trans = matrix(2/9, nrow=3, ncol=3)
m_trans[1,1] = 5/9; m_trans[2,2] = 5/9; m_trans[3,3] = 5/9

for(i in 1:10){
  old_m = m
  m[1,1] = sum(m_trans[,1]*old_m[,1])
  m[2,1] = sum(m_trans[,2]*old_m[,1])
  m[3,1] = sum(m_trans[,3]*old_m[,1])

  m[1,2] = sum(m_trans[,1]*old_m[,2])
  m[2,2] = sum(m_trans[,2]*old_m[,2])
  m[3,2] = sum(m_trans[,3]*old_m[,2])

  m[1,3] = sum(m_trans[,1]*old_m[,3])
  m[2,3] = sum(m_trans[,2]*old_m[,3])
  m[3,3] = sum(m_trans[,3]*old_m[,3])
}  
m

1
+1. Điều đó chứng tỏ rằng xác suất để một thẻ nhất định kết thúc ở một vị trí nhất định xấp xỉ tỷ lệ dự kiến ​​khi số lần xáo trộn tăng lên. Tuy nhiên, điều tương tự cũng đúng với thuật toán chỉ xoay mảng một lần một lượng ngẫu nhiên: Tất cả các thẻ có xác suất bằng nhau để kết thúc ở tất cả các vị trí, nhưng vẫn không có sự ngẫu nhiên nào cả (mảng vẫn được sắp xếp).
Thilo

@Thilo: Xin lỗi tôi không theo dõi bình luận của bạn. Một "thuật toán xoay theo một lượng ngẫu nhiên" nhưng vẫn "không có ngẫu nhiên"? Bạn có thể giải thích thêm?
csgillespie

Nếu bạn "xáo trộn" một mảng phần tử N bằng cách xoay nó giữa các vị trí 0 và N-1 (ngẫu nhiên), thì mọi thẻ đều có xác suất chính xác giống nhau để kết thúc ở bất kỳ vị trí N nào, nhưng 2 vẫn luôn nằm giữa 1 và 3.
Thilo

1
@Thio: Ah, tôi hiểu ý của bạn. Vâng, bạn có thể tìm ra xác suất (sử dụng chính xác cùng một ý tưởng như trên), cho Pr (A ở vị trí 2) và Pr (A ở vị trí 3) - dito cho thẻ B và C. Bạn sẽ thấy rằng tất cả các xác suất có xu hướng 1/3. Lưu ý: câu trả lời của tôi chỉ đưa ra một trường hợp cụ thể, trong khi câu trả lời hay của @whuber đưa ra trường hợp chung.
csgillespie

4

Một cách để thấy rằng bạn sẽ không nhận được phân phối thống nhất hoàn hảo là chia hết. Trong phân phối đồng đều, xác suất của mỗi hoán vị là . Khi bạn tạo ra một chuỗi các t transpositions ngẫu nhiên, và sau đó trình tự thu thập bởi sản phẩm của họ, xác suất bạn nhận được có dạng A / n 2 t đối với một số nguyên A . Nếu 1 / n ! = A / n 2 t , thì n 2 t / n ! = A1/n!tA/n2tA1/n!=A/n2tn2t/n!=A. By Định đề Bertrand (một định lý), cho có số nguyên tố mà xảy ra ở mẫu số và mà không chia n , do đó n 2 t / n ! không phải là số nguyên và không có cách nào để chia đều các chuyển vị thành n ! hoán vị. Ví dụ, nếu n = 52 , sau đó mẫu số của 1 / 52 ! chia hết cho 3 , 5 , 7 , . . . , 47 trong khi mẫu số của 1 /n3nn2t/n!n!n=521/52!3,5,7,...,47 không phải là, vì vậy A / 52 2 t không thể giảm tới 1 / 52 ! .1/522tMột/522t1/52!

Có bao nhiêu bạn cần xấp xỉ một hoán vị ngẫu nhiên tốt? Tạo ra một hoán vị ngẫu nhiên bằng các chuyển vị ngẫu nhiên được phân tích bởi Diaconis và Shahshahani bằng lý thuyết biểu diễn của nhóm đối xứng trong

Diaconis, P., Shahshahani, M. (1981): "Tạo ra một hoán vị ngẫu nhiên với các chuyển vị ngẫu nhiên." Z. Wahrsch. Verw. Geb. 57, 159 Từ179.

Một kết luận là phải mất hoán vị theo nghĩa là sau(1-ϵ)112nđăng nhậpnhoán vị đang ở xa ngẫu nhiên, nhưng sau khi(1+ε)1(1-ε)12nđăng nhậpnkết quả gần với ngẫu nhiên, cả về ý nghĩa của tổng biến thiên vàkhoảng cáchL2. Loại hiện tượng cắt này là phổ biến trong các lần đi ngẫu nhiên trên các nhóm và có liên quan đến kết quả nổi tiếng rằng bạn cần7lần xáo trộn trước khi một bộ bài trở nên gần với ngẫu nhiên.(1+ε)12nđăng nhậpnL27


2

Hãy nhớ rằng tôi không phải là một nhà thống kê, nhưng tôi sẽ đặt 2 phần trăm của mình.

Tôi đã thực hiện một thử nghiệm nhỏ trong R (cẩn thận, tốc độ rất cao numTrials, mã có thể được tối ưu hóa):

numElements <- 1000
numTrials <- 5000

swapVec <- function()
    {
    vec.swp <- vec

    for (i in 1:numElements)
        {
        i <- sample(1:numElements)
        j <- sample(1:numElements)

        tmp <- vec.swp[i]
        vec.swp[i] <- vec.swp[j]
        vec.swp[j] <- tmp
        }

    return (vec.swp)
    }

# Create a normally distributed array of numElements length
vec <- rnorm(numElements)

# Do several "swapping trials" so we can make some stats on them
swaps <- vec
prog <- txtProgressBar(0, numTrials, style=3)

for (t in 1:numTrials)
    {
    swaps <- rbind(swaps, swapVec())
    setTxtProgressBar(prog, t)
    }

Điều này sẽ tạo ra một ma trận swapsvới numTrials+1các hàng (một cho mỗi thử nghiệm + bản gốc) và numElementscác cột (một cho mỗi phần tử vectơ). Nếu phương thức đúng, phân phối của từng cột (nghĩa là các giá trị cho từng phần tử trong các thử nghiệm) sẽ không khác với phân phối dữ liệu gốc.

Bởi vì dữ liệu ban đầu của chúng tôi thường được phân phối, chúng tôi hy vọng tất cả các cột không bị lệch khỏi đó.

Nếu chúng ta chạy

par(mfrow= c(2,2))
# Our original data
hist(swaps[1,], 100, col="black", freq=FALSE, main="Original")
# Three "randomly" chosen columns
hist(swaps[,1], 100, col="black", freq=FALSE, main="Trial # 1") 
hist(swaps[,257], 100, col="black", freq=FALSE, main="Trial # 257")
hist(swaps[,844], 100, col="black", freq=FALSE, main="Trial # 844")

Chúng tôi nhận được:

Biểu đồ của các thử nghiệm ngẫu nhiên

có vẻ rất hứa hẹn Bây giờ, nếu chúng tôi muốn xác nhận thống kê các bản phân phối không sai lệch so với bản gốc, tôi nghĩ rằng chúng tôi có thể sử dụng thử nghiệm Kolmogorov-Smirnov (xin vui lòng một số nhà thống kê có thể xác nhận điều này là đúng không?)

ks.test(swaps[1, ], swaps[, 234])

Cung cấp cho chúng tôi p = 0,9926

Nếu chúng tôi kiểm tra tất cả các cột:

ks.results <- apply(swaps, 2, function(col){ks.test(swaps[1,], col)})
p.values <- unlist(lapply(ks.results, function(x){x$p.value})

Và chúng tôi chạy

hist(p.values, 100, col="black")

chúng tôi nhận được:

Biểu đồ giá trị p kiểm tra Kolmogorov-Smirnov

Vì vậy, đối với phần lớn các yếu tố của mảng, phương thức hoán đổi của bạn đã cho một kết quả tốt, vì bạn cũng có thể thấy việc nhìn vào các phần tư.

1> quantile(p.values)
       0%       25%       50%       75%      100% 
0.6819832 0.9963731 0.9999188 0.9999996 1.0000000

Lưu ý rằng, rõ ràng, với số lượng thử nghiệm ít hơn, tình huống không tốt bằng:

50 thử nghiệm

1> quantile(p.values)
          0%          25%          50%          75%         100% 
0.0003399635 0.2920976389 0.5583204486 0.8103852744 0.9999165730

100 thử nghiệm

          0%         25%         50%         75%        100% 
 0.001434198 0.327553996 0.596603804 0.828037097 0.999999591 

500 thử nghiệm

         0%         25%         50%         75%        100% 
0.007834701 0.504698404 0.764231550 0.934223503 0.999995887 

0

Đây là cách tôi diễn giải thuật toán của bạn, bằng mã giả:

void shuffle(array, length, num_passes)
  for (pass = 0; pass < num_passes; ++pass) 
    for (n = 0; n < length; ++)
      i = random_in(0, length-1)
      j = random_in(0, lenght-1)
      swap(array[i], array[j]

2×tôiength×nbạnm_pmộtSSeS[0,tôiength-1]tôiength

tôiength2×tôiength×nbạnm_pmộtSSeS

tôiength!tôiength!<tôiength2×tôiength×nbạnm_pmộtSSeS

tôiength!|tôiength2×tôiength×nbạnm_pmộtSSeS

pp<tôiengthptôiengthtôiength>2p|tôiength!tôiength2×tôiength×nbạnm_pmộtSSeSlength!length2×length×num_passeslength>2

lengthp<lengthlength1length1length

lengthlength1length!length!|length!. Không khó để chỉ ra rằng mỗi dấu vết dẫn đến một hoán vị khác nhau, và từ đó dễ dàng thấy rằng Fisher-Yates tạo ra mỗi hoán vị với xác suất bằng nhau.

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.