순열을 반복하지 않고 R에서 다시 샘플링하는 방법은 무엇입니까?


12

R에서 set.seed ()를 사용한 다음 샘플 함수를 사용하여 목록을 무작위 화하면 동일한 순열을 생성하지 않을 수 있습니까?

즉 ...

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

이것은 생산

[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

인쇄 된 모든 순열은 고유 순열입니까? 또는 구현 방법에 따라 반복 할 수있는 기회가 있습니까?

반복 없이이 작업을 수행 할 수 있기를 원합니다. 어떻게해야합니까?

(또한 permn ()과 같은 함수를 사용하지 않으려 고합니다. permn ()은 모든 순열을 생성하는 매우 기계적인 방법을 가지고 있습니다. 무작위로 보이지 않습니다.)

또한, sidenote --- 실수하지 않으면이 문제는 O ((n!)!) 인 것 같습니다.


기본적으로 'sample'의 'replace'인수는 FALSE로 설정되어 있습니다.
ocram

감사합니다 ocram, 그러나 그것은 특정 샘플 내에서 작동합니다. 따라서 0,1,2 및 3이 무승부 내에서 반복되지 않도록 보장하므로 0,1,2,2를 그릴 수는 없지만 두 번째 샘플이 같은 순서의 0123을 다시 그릴 수 없습니다. 시드 설정이 반복에 영향을 미치는지 여부는 구현 측면에서 궁금합니다.
Mittenchops

그렇습니다. 이것은 내가 답을 읽음으로써 마침내 이해 한 것입니다. ;-)
ocram

1
경우 limit12을 초과하는 R 시도가 공간을 할당 할 때, 당신은 가능성이 RAM 부족하게됩니다 seq(1,factorial(limit)). (12!는 약 2GB가 필요하므로 13!는 약 25GB, 14!는 약 350GB 등이 필요합니다.)
whuber

2
n을 편안하게 저장할 수 있다면 1 : n 의 모든 순열의 랜덤 시퀀스를 생성 하는 빠르고 작고 우아한 솔루션 이 있습니다! 0 : (n!) 범위의 정수 순열 의 반전 테이블 표시와 숫자의 계승 기본 표시를 결합합니다.
whuber

답변:


9

이 질문에는 많은 유효한 해석이 있습니다. 주석-특히 15 개 이상의 요소의 순열을 나타내는 주석이 필요합니다 (15! = 1307674368000이 커짐)-원하는 것은 대체하지 않고 모든 n 의 비교적 작은 무작위 샘플 이라는 것을 제안합니다 ! = n * (n-1) (n-2) ... * 2 * 1 1 : n의 순열입니다. 이것이 사실이라면, 효율적인 솔루션이 존재합니다.

다음 함수 rperm는 두 개의 인수 n(샘플링 할 순열의 크기)와 m(그리기 위해 크기 n의 순열 수)를 허용합니다. m이 n!에 근접하거나이를 초과하면 함수는 시간이 오래 걸리고 많은 NA 값을 반환합니다. 이는 n이 상대적으로 크고 (8 이상) m이 n보다 훨씬 작을 때 사용하기위한 것입니다. 지금까지 찾은 순열의 문자열 표현을 캐싱 한 다음 새로운 순열이 발견 될 때까지 새로운 순열을 생성합니다 (임의로). R의 연관 목록 인덱싱 기능을 활용하여 이전에 찾은 순열 목록을 빠르게 검색합니다.

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.

속성은 replicate순열을 벡터 로 반환하는 것입니다 . 예를 들어 , 다음은 원래 질문에서 바뀐 예를 재현합니다 .

> 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

타이밍은 작거나 중간 정도의 m, 최대 약 10,000까지 우수하지만 더 큰 문제에서는 저하됩니다. 예를 들어, n = 1000 요소의 m = 10,000 순열의 샘플 (천만 값의 행렬)이 10 초 내에 얻어졌다. 출력 (400,000 개 항목의 행렬)이 훨씬 작더라도 m = 20,000 개의 순열 n = 20 개 요소의 샘플은 11 초가 필요했습니다. 260 초 후에 m = 100,000 순열의 n = 20 요소의 계산 샘플이 중단되었습니다 (완료를 기다리는 인내심이 없었습니다). 이 스케일링 문제는 R의 연관 어드레싱에서의 스케일링 비효율 성과 관련이있는 것으로 보인다. 1000 개 정도의 그룹으로 샘플을 생성 한 다음이 샘플을 큰 샘플로 결합하고 복제본을 제거하여이 문제를 해결할 수 있습니다.

편집하다

캐시를 두 개의 캐시 계층으로 나누면 거의 선형적인 점근 적 성능달성 할 수 있으므로 R은 큰 목록을 검색 할 필요가 없습니다. 개념적으로 (구현되지는 않았지만) 순열 의 첫 번째 요소에 의해 인덱스 된 배열을 만듭니다 . 이 배열의 항목은 첫 요소를 공유하는 모든 순열의 목록입니다 . 순열이 있는지 확인하려면 첫 번째 요소를 사용 하여 캐시에서 해당 항목을 찾은 다음 해당 항목에서 해당 순열을 검색하십시오. 를 선택 하여 모든 목록의 예상 크기의 균형을 맞출 수 있습니다 . 실제 구현은 사용하지 않습니다k k k kkkkkk-fold 배열은 충분한 일반성으로 프로그램하기는 어렵지만 대신 다른 목록을 사용합니다.

다음은 다양한 순열 크기 및 요청 된 고유 순열 수에 대한 몇 초의 경과 시간입니다.

 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

(크기 = 10에서 크기 = 15 로의 비정상적인 속도 향상은 size = 15에 대해 캐시의 첫 번째 수준이 더 커서 두 번째 수준 목록의 평균 항목 수를 줄여서 R의 연관 검색 속도를 높이기 때문입니다. RAM 비용이 증가하면 상위 캐시 크기를 늘림으로써 실행 속도를 높일 수 있습니다. 예 k.head를 들어, 1을 늘리면 (예 : 상위 크기에 10을 곱한) rperm(100000, size=10)11.77 초에서 8.72 초로 증가했습니다. 캐시는 10 배 더 크지 만 8.51 초의 클럭으로 눈에 띄는 이득을 얻지 못했습니다.)

10 개 요소의 1,000,000 개의 고유 순열 (10!의 실질적인 부분 = 약 3,630 만 순열)을 제외하고는 실제로 충돌이 감지되지 않았습니다. 이 예외적 인 경우에는 169,301 건의 충돌이 있었지만 완전한 실패는 없었습니다 (실제로 백만개의 순열이 얻어졌습니다).

큰 순열 크기 (20 이상)에서는 1,000,000,000의 표본에서도 두 개의 동일한 순열을 얻을 가능성이 거의 없습니다. 따라서이 솔루션은 주로 (a) ( ) 에서 사이의 많은 고유 순열이 발생하는 상황에서 적용 할 수 있지만 (c) 모든 보다 실질적으로 적습니다순열이 필요합니다.n = 15 n !n=5n=15n!

작업 코드는 다음과 같습니다.

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.

이것은 가깝지 만 1, 2 및 4와 같은 오류가 발생한다는 것을 알았습니다. 그러나 나는 당신이 의미하는 바를보고 그것을 사용할 수 있어야한다고 생각합니다. 감사! > 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
Mittenchops

4

unique올바른 방법으로 사용 하면 트릭을 수행해야합니다.

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

코드를 올바르게 설명하지 않아서 죄송합니다. 나는 지금 약간 서두르고 있지만 나중에 질문에 답변 드리겠습니다. 또한, 나는 위 코드의 속도에 대해 전혀 모른다.
MånsT

1
나는 당신이 나에게 이런 식으로 주었다 :`myperm <-function (limit) {myindex <-seq (0, limit) endDim <-factorial (limit) permutations <-sample (myindex) while (is.null (dim (unique) dim (unique (permutations)) [1]! = endDim) {순열 <-rbind (permutations, sample (myindex))} return (unique (permutations))} '작동하지만 I 동안 limit = 6, limit = 7을 수행하면 컴퓨터가 과열됩니다. = PI 아직 하위 표본
추출

@Mittenchops, 왜 우리는 순열을 반복하지 않고 R에서 리샘플링을 위해 고유성을 사용해야한다고 말합니까? 감사합니다.
Frank

2

나는 "m은 첫 번째 질문 측면 단계로 조금가는, 그리고 상대적으로 짧은 벡터를 처리하는 경우, 당신은 단순히 모든 순열을 사용하여 생성 할 수 있음을 시사 permn하고 무작위로 주문 하는 사용하여 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

나는 이것을 많이 좋아하고 그것이 옳은 생각이라고 확신합니다. 그러나 내 문제는 10까지 올라가는 시퀀스를 사용하고 있습니다. Permn ()은 factorial (7)과 factorial (8) 사이에서 상당히 느리기 때문에 9와 10이 엄청나게 클 것이라고 생각합니다.
Mittenchops

@Mittenchops True이지만 실제로 한 번만 계산하면 될 수 있습니까? 파일에 저장 한 다음 필요할 때로드하고 미리 정의 된 목록에서 "샘플링"하십시오. 따라서 느린 계산 permn(10)또는 한 번만 수행 할 수 있습니다.
joran

맞습니다. 그러나 만약 내가 모든 순열을 어딘가에 저장한다면, 이것조차도 factorial (15) --- 저장하기에 너무 많은 공간으로 분류됩니다. 그래서 시드를 설정하면 순열을 집합 적으로 샘플링 할 수 있는지, 그렇지 않은 경우 알고리즘을 수행 할 수 있는지 궁금합니다.
Mittenchops 5

@Mittenchops 시드 설정은 성능에 영향을 미치지 않으며 PRNG를 호출 할 때마다 동일한 시작을 보장합니다.
Roman Luštrik

1
@Mitten 다음 도움말을 참조하십시오. set.seedRNG 상태를 저장하고 나중에 복원하는 방법에 대해 설명합니다.
whuber
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.