Câu hỏi có nhiều cách giải thích hợp lệ. Các ý kiến - đặc biệt là một hoán vị chỉ ra từ 15 yếu tố trở lên là cần thiết (15! = 1307674368000 đang trở nên lớn) - đề xuất rằng những gì đang muốn là một mẫu ngẫu nhiên tương đối nhỏ , không thay thế, trong tất cả n! = n * (n-1) (n-2) ... * 2 * 1 hoán vị của 1: n. Nếu điều này là đúng, có tồn tại (phần nào) giải pháp hiệu quả.
Hàm sau đây rperm
, chấp nhận hai đối số n
(kích thước của hoán vị để lấy mẫu) và m
(số lượng hoán vị của kích thước n để vẽ). Nếu m tiếp cận hoặc vượt quá n!, Thì hàm sẽ mất nhiều thời gian và trả về nhiều giá trị NA: nó được sử dụng khi n tương đối lớn (giả sử, 8 hoặc nhiều hơn) và m nhỏ hơn nhiều so với n!. Nó hoạt động bằng cách lưu trữ một chuỗi đại diện cho các hoán vị được tìm thấy cho đến nay và sau đó tạo ra các hoán vị mới (ngẫu nhiên) cho đến khi tìm thấy một hoán vị mới. Nó khai thác khả năng lập chỉ mục danh sách liên kết của R để tìm kiếm danh sách các hoán vị được tìm thấy trước đó một cách nhanh chóng.
rperm <- function(m, size=2) { # Obtain m unique permutations of 1:size
# Function to obtain a new permutation.
newperm <- function() {
count <- 0 # Protects against infinite loops
repeat {
# Generate a permutation and check against previous ones.
p <- sample(1:size)
hash.p <- paste(p, collapse="")
if (is.null(cache[[hash.p]])) break
# Prepare to try again.
count <- count+1
if (count > 1000) { # 1000 is arbitrary; adjust to taste
p <- NA # NA indicates a new permutation wasn't found
hash.p <- ""
break
}
}
cache[[hash.p]] <<- TRUE # Update the list of permutations found
p # Return this (new) permutation
}
# Obtain m unique permutations.
cache <- list()
replicate(m, newperm())
} # Returns a `size` by `m` matrix; each column is a permutation of 1:size.
Bản chất của replicate
là trả về các hoán vị dưới dạng vectơ cột ; ví dụ: phần sau đây tái tạo một ví dụ trong câu hỏi ban đầu, được hoán vị :
> set.seed(17)
> rperm(6, size=4)
[,1] [,2] [,3] [,4] [,5] [,6]
[1,] 1 2 4 4 3 4
[2,] 3 4 1 3 1 2
[3,] 4 1 3 2 2 3
[4,] 2 3 2 1 4 1
Thời gian là tuyệt vời cho các giá trị nhỏ đến trung bình của m, lên đến khoảng 10.000, nhưng suy giảm cho các vấn đề lớn hơn. Ví dụ: một mẫu có m = 10.000 hoán vị n = 1000 phần tử (ma trận 10 triệu giá trị) đã thu được trong 10 giây; một mẫu m = 20.000 hoán vị n = 20 phần tử cần 11 giây, mặc dù đầu ra (ma trận 400.000 mục) nhỏ hơn nhiều; và mẫu tính toán m = 100.000 hoán vị của n = 20 phần tử đã bị hủy bỏ sau 260 giây (Tôi không đủ kiên nhẫn để chờ hoàn thành). Vấn đề mở rộng này dường như có liên quan đến sự thiếu hiệu quả trong việc đánh địa chỉ liên kết của R. Người ta có thể làm việc xung quanh nó bằng cách tạo các mẫu trong các nhóm, ví dụ 1000 hoặc hơn, sau đó kết hợp các mẫu đó thành một mẫu lớn và loại bỏ các bản sao.
Biên tập
Chúng ta có thể đạt được hiệu suất tiệm cận tuyến tính gần bằng cách chia bộ đệm thành một hệ thống phân cấp gồm hai bộ đệm, để R không bao giờ phải tìm kiếm trong một danh sách lớn. Về mặt khái niệm (mặc dù không được thực hiện), tạo một mảng được lập chỉ mục bởi các phần tử đầu tiên của một hoán vị. Các mục trong mảng này là danh sách tất cả các hoán vị chia sẻ các phần tử đầu tiên đó . Để kiểm tra xem có hoán vị hay không, hãy sử dụng các phần tử đầu tiên của nó để tìm mục nhập của nó trong bộ đệm và sau đó tìm kiếm hoán vị đó trong mục đó. Chúng ta có thể chọn để cân bằng các kích thước dự kiến của tất cả các danh sách. Việc thực hiện không sử dụngk k k k kkkkkkmảng gấp đôi, sẽ khó lập trình đủ tổng quát, nhưng thay vào đó sử dụng một danh sách khác.
Dưới đây là một số thời gian trôi qua tính bằng giây cho một loạt các kích thước hoán vị và số lượng hoán vị riêng biệt được yêu cầu:
Number Size=10 Size=15 Size=1000 size=10000 size=100000
10 0.00 0.00 0.02 0.08 1.03
100 0.01 0.01 0.07 0.64 8.36
1000 0.08 0.09 0.68 6.38
10000 0.83 0.87 7.04 65.74
100000 11.77 10.51 69.33
1000000 195.5 125.5
(Việc tăng tốc bất thường rõ ràng từ kích thước = 10 đến kích thước = 15 là do cấp độ bộ đệm đầu tiên lớn hơn đối với kích thước = 15, giảm số lượng mục nhập trung bình trong danh sách cấp hai, do đó tăng tốc tìm kiếm kết hợp của R. Chi phí trong RAM, việc thực thi có thể được thực hiện nhanh hơn bằng cách tăng kích thước bộ đệm cấp trên. Chỉ cần tăng k.head
thêm 1 (nhân kích thước cấp trên lên 10), tăng rperm(100000, size=10)
từ 11,77 giây lên 8,72 giây, ví dụ: Tạo cấp trên bộ nhớ cache lớn hơn 10 lần nhưng không đạt được mức tăng đáng kể, thời gian ở mức 8,51 giây.)
Ngoại trừ trường hợp 1.000.000 hoán vị duy nhất của 10 yếu tố (một phần đáng kể của tất cả 10! = Khoảng 3,63 triệu hoán vị như vậy), thực tế không có sự va chạm nào được phát hiện. Trong trường hợp đặc biệt này, đã có 169.301 vụ va chạm, nhưng không có thất bại hoàn toàn (thực tế đã thu được một triệu hoán vị duy nhất).
Lưu ý rằng với kích thước hoán vị lớn (lớn hơn 20 hoặc hơn), cơ hội có được hai hoán vị giống hệt nhau ngay cả trong một mẫu lớn tới 1.000.000.000 là rất nhỏ. Do đó, giải pháp này được áp dụng chủ yếu trong các tình huống trong đó (a) số lượng lớn các hoán vị duy nhất của (b) giữa và hoặc hơn các phần tử sẽ được tạo nhưng ngay cả như vậy, (c) ít hơn tất cảhoán vị là cần thiết.n = 15 n !n = 5n = 15n !
Mã làm việc sau đây.
rperm <- function(m, size=2) { # Obtain m unique permutations of 1:size
max.failures <- 10
# Function to index into the upper-level cache.
prefix <- function(p, k) { # p is a permutation, k is the prefix size
sum((p[1:k] - 1) * (size ^ ((1:k)-1))) + 1
} # Returns a value from 1 through size^k
# Function to obtain a new permutation.
newperm <- function() {
# References cache, k.head, and failures in parent context.
# Modifies cache and failures.
count <- 0 # Protects against infinite loops
repeat {
# Generate a permutation and check against previous ones.
p <- sample(1:size)
k <- prefix(p, k.head)
ip <- cache[[k]]
hash.p <- paste(tail(p,-k.head), collapse="")
if (is.null(ip[[hash.p]])) break
# Prepare to try again.
n.failures <<- n.failures + 1
count <- count+1
if (count > max.failures) {
p <- NA # NA indicates a new permutation wasn't found
hash.p <- ""
break
}
}
if (count <= max.failures) {
ip[[hash.p]] <- TRUE # Update the list of permutations found
cache[[k]] <<- ip
}
p # Return this (new) permutation
}
# Initialize the cache.
k.head <- min(size-1, max(1, floor(log(m / log(m)) / log(size))))
cache <- as.list(1:(size^k.head))
for (i in 1:(size^k.head)) cache[[i]] <- list()
# Count failures (for benchmarking and error checking).
n.failures <- 0
# Obtain (up to) m unique permutations.
s <- replicate(m, newperm())
s[is.na(s)] <- NULL
list(failures=n.failures, sample=matrix(unlist(s), ncol=size))
} # Returns an m by size matrix; each row is a permutation of 1:size.