Làm thế nào để lấy mẫu lại trong R mà không lặp lại hoán vị?


12

Trong R, nếu tôi set.seed (), sau đó sử dụng hàm mẫu để chọn ngẫu nhiên một danh sách, tôi có thể đảm bảo tôi sẽ không tạo ra hoán vị tương tự không?

I E...

set.seed(25)
limit <- 3
myindex <- seq(0,limit)
for (x in seq(1,factorial(limit))) {
    permutations <- sample(myindex)
    print(permutations)
}

Điều này tạo ra

[1] 1 2 0 3
[1] 0 2 1 3
[1] 0 3 2 1
[1] 3 1 2 0
[1] 2 3 0 1
[1] 0 1 3 2

tất cả các hoán vị được in là hoán vị duy nhất? Hoặc có một số cơ hội, dựa trên cách thực hiện điều này, rằng tôi có thể nhận được một số lần lặp lại?

Tôi muốn có thể làm điều này mà không cần lặp lại, đảm bảo. Làm thế nào tôi có thể làm điều đó?

(Tôi cũng muốn tránh phải sử dụng một hàm như permn (), có một phương thức rất cơ học để tạo ra tất cả các hoán vị --- nó không có vẻ ngẫu nhiên.)

Ngoài ra, sidenote --- có vẻ như vấn đề này là O ((n!)!), Nếu tôi không nhầm.


Theo mặc định, đối số 'thay thế' của 'mẫu' được đặt thành FALSE.
ocram

Cảm ơn ocram, nhưng đó là hoạt động trong một mẫu cụ thể. Vì vậy, điều đó đảm bảo rằng 0,1,2 và 3 sẽ không lặp lại trong một lần rút (vì vậy, tôi không thể rút 0,1,2,2), nhưng tôi không biết liệu điều đó có đảm bảo rằng mẫu thứ hai không, Tôi không thể vẽ lại cùng một chuỗi 0123. Đó là điều tôi đang băn khoăn khi thực hiện, liệu việc đặt hạt giống có ảnh hưởng gì đến sự lặp lại đó không.
Găng tay

Vâng, đây là những gì cuối cùng tôi đã hiểu bằng cách đọc các câu trả lời ;-)
ocram

1
Nếu limitvượt quá 12, bạn có thể sẽ hết RAM khi R cố gắng phân bổ dung lượng cho seq(1,factorial(limit)). (12!
Yêu

2
Có một giải pháp nhanh, gọn và thanh lịch để tạo các chuỗi ngẫu nhiên của tất cả các hoán vị 1: n, miễn là bạn có thể thoải mái lưu trữ n! số nguyên trong phạm vi 0: (n!). Nó kết hợp biểu diễn bảng đảo ngược của một hoán vị với biểu diễn cơ sở giai thừa của các số.
whuber

Câu trả lời:


9

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 replicatelà 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.headthê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.

Điều này rất gần, nhưng tôi nhận thấy tôi gặp một số lỗi, như 1, 2 và 4, nhưng tôi nghĩ tôi thấy ý của bạn là gì và có thể làm việc với nó. Cảm ơn! > rperm(6,3) $failures [1] 9 $sample [,1] [,2] [,3] [1,] 3 1 3 [2,] 2 2 1 [3,] 1 3 2 [4,] 1 2 2 [5,] 3 3 1 [6,] 2 1 3
Găng tay

3

Sử dụng uniqueđúng cách nên thực hiện thủ thuật:

set.seed(2)
limit <- 3
myindex <- seq(0,limit)

endDim<-factorial(limit)
permutations<-sample(myindex)

while(is.null(dim(unique(permutations))) || dim(unique(permutations))[1]!=endDim) {
    permutations <- rbind(permutations,sample(myindex))
}
# Resulting permutations:
unique(permutations)

# Compare to
set.seed(2)
permutations<-sample(myindex)
for(i in 1:endDim)
{
permutations<-rbind(permutations,sample(myindex))
}
permutations
# which contains the same permutation twice

Xin lỗi vì đã không giải thích mã đúng. Bây giờ tôi đang hơi vội vàng, nhưng tôi rất vui được trả lời bất kỳ câu hỏi nào bạn có sau này. Ngoài ra, tôi không biết gì về tốc độ của đoạn mã trên ...
MånsT

1
Tôi đã chức năng hóa những gì bạn đã cho tôi theo cách này: `myperm <- function (giới hạn) {myindex <- seq (0, giới hạn) endDim <-factorial (giới hạn) hoán vị <-sample (myindex) while (is.null (dim (unique) (hoán vị))) | | dim (unique (hoán vị)) [1]! = endDim) {hoán vị <- rbind (hoán vị, mẫu (myindex))} return (unique (hoán vị))} 'Nó hoạt động, nhưng trong khi tôi có thể làm giới hạn = 6, giới hạn = 7 làm cho máy tính của tôi quá nóng. = PI nghĩ rằng vẫn phải có một cách để lấy mẫu này ...
Găng tay vào

@Mittenchops, Tại sao bạn nói chúng ta cần sử dụng duy nhất để lấy mẫu lại trong R mà không lặp lại hoán vị? Cảm ơn bạn.
Frank

2

Tôi sẽ chuyển sang bên cạnh câu hỏi đầu tiên của bạn một chút và đề nghị rằng nếu bạn đang xử lý các vectơ tương đối ngắn, bạn có thể chỉ cần tạo tất cả các hoán vị bằng cách sử dụng permnvà chúng ra lệnh ngẫu nhiên cho những người sử dụng sample:

x <- combinat:::permn(1:3)
> x[sample(factorial(3),factorial(3),replace = FALSE)]
[[1]]
[1] 1 2 3

[[2]]
[1] 3 2 1

[[3]]
[1] 3 1 2

[[4]]
[1] 2 1 3

[[5]]
[1] 2 3 1

[[6]]
[1] 1 3 2

Tôi thích điều này rất nhiều, và tôi chắc chắn đó là suy nghĩ đúng đắn. Nhưng vấn đề của tôi khiến tôi sử dụng một chuỗi lên tới 10. Permn () chậm hơn đáng kể giữa giai thừa (7) và giai thừa (8), vì vậy tôi nghĩ rằng 9 và 10 sẽ rất lớn.
Găng tay vào

@Mittenchops Đúng, nhưng vẫn có thể bạn thực sự chỉ cần tính toán chúng một lần, phải không? Lưu chúng vào một tệp, sau đó tải chúng khi bạn cần chúng và "mẫu" từ danh sách được xác định trước. Vì vậy, bạn có thể làm phép tính chậm permn(10)hoặc bất cứ điều gì chỉ một lần.
Joran

Phải, nhưng nếu tôi đang lưu trữ tất cả các hoán vị ở đâu đó, thậm chí điều này bị phá vỡ bởi xung quanh giai thừa (15) --- đơn giản là quá nhiều chỗ để lưu trữ. Đó là lý do tại sao tôi tự hỏi liệu việc thiết lập hạt giống có cho phép tôi lấy mẫu hoán vị tập thể --- và nếu không, nếu có một thuật toán để làm điều đó.
Găng tay

@Mittenchops Đặt hạt giống sẽ không ảnh hưởng đến hiệu suất, nó chỉ đảm bảo bắt đầu giống nhau mỗi khi bạn thực hiện cuộc gọi đến PRNG.
Roman Luštrik

1
@Mitten Xem trợ giúp cho set.seed: nó mô tả cách lưu trạng thái của RNG và khôi phục nó sau.
whuber
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.