Permütasyonları tekrar etmeden R'de nasıl yeniden örneklenir?


13

R'de, bir set.seed () ayarlayıp sonra bir listeyi rasgele seçmek için örnek işlevini kullanırsam, aynı permütasyonu üretmeyeceğimi garanti edebilir miyim?

yani ...

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

Bu üretir

[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

yazdırılan tüm permütasyonlar benzersiz permütasyonlar olacak mı? Yoksa bunun uygulanış şekline bağlı olarak, bazı tekrarlar alma şansım var mı?

Bunu tekrar etmeden yapmak istiyorum, garantili. Bunu nasıl yaparım?

(Ayrıca, tüm permütasyonları oluşturmak için çok mekanik bir yöntemi olan permn () gibi bir işlevi kullanmak zorunda kalmaktan kaçınmak istiyorum - rastgele görünmüyor.)

Ayrıca, sidenote --- yanılmıyorsam bu sorun O ((n!)!) Gibi görünüyor.


Varsayılan olarak, 'sample' öğesinin 'replace' argümanı FALSE olarak ayarlanmıştır.
ocram

Teşekkürler ocram, ama bu belirli bir örnek içinde çalışıyor. Böylece 0,1,2 ve 3'ün bir çekilişte tekrarlanmamasını sağlar (yani, 0,1,2,2 çizemem), ancak bunun ikinci örneğin, Aynı 0123 dizisini tekrar çizemiyorum. Tohum ayarlamanın bu tekrar üzerinde herhangi bir etkisi olup olmadığını uygulama açısından merak ediyorum.
Mittenchops

Evet, nihayet cevapları okuyarak anladım ;-)
ocram

1
Eğer limit12 aşarsa R girişimleri alanı ayırmaya zaman, büyük olasılıkla RAM tükenecek seq(1,factorial(limit)). (12! Yaklaşık 2 GB gerektirir, bu yüzden 13! Yaklaşık 25 GB, 14! Yaklaşık 350 GB vb.)
Gerekir

2
N'yi rahatça saklayabilmeniz şartıyla, 1: n'nin tüm permütasyonlarının rastgele dizilerini oluşturmak için hızlı, kompakt ve zarif bir çözüm var ! 0 aralığındaki tamsayılar: (n!). Bir permütasyonun inversiyon tablosu gösterimini sayıların faktöriyel temsili ile birleştirir .
whuber

Yanıtlar:


9

Sorunun birçok geçerli yorumu var. Yorumlar - özellikle 15 veya daha fazla elemanın permütasyonlarını gösterene ihtiyaç duyulmaktadır (15! = 1307674368000 büyüyor) - istenen şeyin tüm n yerine değiştirilmeden nispeten küçük rastgele bir örnek olduğunu önerin ! = n * (n-1) (n-2) ... * 2 * 1 1: n permütasyonları. Bu doğruysa, (bir şekilde) etkili çözümler vardır.

Aşağıdaki işlev, rpermiki bağımsız değişkeni n(örneğe geçirilecek permütasyonların boyutu) ve m(çizilecek n boyutundaki permütasyonların sayısını) kabul eder. M, n! 'Ye yaklaşır veya aşarsa, işlev uzun zaman alacak ve birçok NA değeri döndürecektir: n, nispeten büyük (örneğin, 8 veya daha fazla) ve m, n!' Den çok daha küçük olduğunda kullanılmak üzere tasarlanmıştır. Şimdiye kadar bulunan permütasyonların dize halinde temsilini önbelleğe alarak ve ardından yeni bir permütasyon bulunana kadar (rasgele) yeni permütasyonlar üreterek çalışır. R'nin önceden bulunan permütasyonların listesini hızlı bir şekilde aramak için ilişkisel liste indeksleme yeteneğinden yararlanı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.

Doğası replicatepermütasyonları kolon vektörleri olarak döndürmektir ; örneğin , asıl soruya aktarılan bir örnek aşağıda verilmiştir :

> 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

Zamanlamalar yaklaşık 10.000'e kadar küçük ila orta m değerleri için mükemmeldir, ancak daha büyük sorunlar için bozunur. Örneğin, n = 1000 elementin (10 milyon değerinde bir matris) m = 10.000 permütasyon örneği 10 saniyede elde edildi; Çıktı (400.000 girişlik bir matris) çok daha küçük olmasına rağmen, m = 20.000 n = 20 element permütasyon örneği 11 saniye gerektirdi; ve n = 20 elementin m = 100.000 permütasyonunun hesaplanması örneği 260 saniye sonra iptal edildi (tamamlanmayı beklemek için sabrım yoktu). Bu ölçeklendirme problemi, R'nin ilişkisel adreslemesindeki ölçeklendirme verimsizlikleri ile ilişkili görünmektedir. Kişi, örneğin 1000 kişilik gruplar halinde numuneler üreterek, daha sonra bu örnekleri büyük bir numunede birleştirerek ve kopyaları çıkararak bunun etrafında çalışabilir.

Düzenle

Önbelleği iki önbellek hiyerarşisine bölerek neredeyse lineer asimptotik performansa ulaşabiliriz , böylece R asla büyük bir listede arama yapmak zorunda kalmaz. Kavramsal olarak (uygulanmadığı halde), bir permütasyonun ilk elemanları tarafından endekslenen bir dizi oluşturun . Bu dizideki girdiler, bu ilk öğelerini paylaşan tüm permütasyonların listeleridir . Bir permütasyonun görülüp görülmediğini kontrol etmek için , önbellekteki girişini bulmak için ilk öğelerini kullanın ve ardından bu giriş içindeki o permütasyonu arayın. Tüm listelerin beklenen boyutlarını dengelemek için seçebiliriz . Gerçek uygulama kullanmazk k k kkkkkk-klasman dizisi, yeterli genellikte programlanması zor olur, ancak bunun yerine başka bir liste kullanır.

Aşağıda, bir dizi permütasyon boyutu ve istenen farklı permütasyon sayısı için birkaç saniye geçmiştir:

 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

(Görünüşe göre boyut = 10'dan boyut = 15'e anormal hızlanma, önbelleğin ilk seviyesinin boyut = 15 için daha büyük olması, ikinci düzey listelerdeki ortalama giriş sayısını azaltması ve böylece R'nin ilişkisel aramasını hızlandırmasıdır. RAM'de maliyet, üst düzey önbellek boyutunu artırarak yürütme daha hızlı yapılabilir.Yalnızca k.head1 (üst seviye boyutunu 10 ile çarpar) artırarak rperm(100000, size=10)11.77 saniyeden 8.72 saniyeye çıktı. 10 kat daha büyük önbellek, 8.51 saniyede saat kayda değer bir kazanç elde etmedi.)

10 elementten oluşan 1.000.000 benzersiz permütasyon haricinde (10! = Yaklaşık 3.63 milyon bu permütasyonun önemli bir kısmı), pratikte hiçbir çarpışma tespit edilmedi. Bu istisnai durumda, 169.301 çarpışma oldu, ancak tam bir başarısızlık yoktu (aslında bir milyon benzersiz permütasyon elde edildi).

Büyük permütasyon boyutları (20 ya da daha fazla) ile, 1.000.000.000 kadar büyük bir örnekte bile iki özdeş permütasyon elde etme şansının kaybolan küçük olduğuna dikkat edin. Bu nedenle, bu çözüm esas olarak (a) ve ya da öylesine elemanlar arasında (b) çok sayıda benzersiz permütasyonun üretileceği, ancak buna rağmen, (c) tüm önemli ölçüde daha az olduğu durumlarda uygulanabilirpermütasyon gereklidir.n = 15 n !n=5n=15n!

Çalışma kodu aşağıdadır.

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.

Bu yakın, ama 1, 2 ve 4 gibi bazı hatalar aldığımı fark ettim, ama ne demek istediğini anlıyorum ve onunla çalışabilmeliyim. Teşekkürler! > 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 07

4

uniqueDoğru şekilde kullanmak hile yapmalıdır:

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

Kodu doğru bir şekilde açıklayamadığım için üzgünüm. Şimdi biraz acelem var, ama daha sonra sorularınızı cevaplamaktan mutluluk duyuyorum. Ayrıca, yukarıdaki kodun hızı hakkında hiçbir fikrim yok ...
MånsT

1
Bana bu şekilde verdiklerinizi işlevselleştirdim: `myperm <- function (limit) {myindex <- seq (0, limit) endDim <-factorial (limit) permütasyonları <-sample (myindex) while (is.null (dim (unique (benzersiz) (permütasyonlar))) || dim (benzersiz (permütasyonlar)) [1]! = endDim) {permütasyonlar <- rbind (permütasyonlar, örnek (myindex))} return (unique (permütasyonlar))} 'Çalışıyor, ama ben limit = 6 yapabilir, limit = 7 bilgisayarımı aşırı ısınır. = PI hala alt örneklemek için bir yol olması gerektiğini düşünüyorum ...
Mittenchops 15:12

@Mittenchops, Neden permütasyonları tekrar etmeden R'de yeniden örnekleme için benzersiz kullanmamız gerektiğini söylüyorsunuz? Teşekkür ederim.
Frank

2

Ben ilk soru biraz yan adım gidiyorum ve nispeten kısa vektörlerle uğraşıyorsanız, sadece kullanarak tüm permütasyonlar oluşturmak permnve rastgele kullanarak bu sipariş öneririz 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

Bunu çok seviyorum ve doğru düşünce olduğuna eminim. Ama benim sorunum 10'a kadar bir dizi kullanmamı sağlıyor. Permn () faktöriyel (7) ve faktöriyel (8) arasında önemli ölçüde yavaştı, bu yüzden 9 ve 10'un çok büyük olacağını düşünüyorum.
Mittenchops

@Mittenchops Doğru, ama onları sadece bir kez hesaplamanız hala mümkün, değil mi? Bunları bir dosyaya kaydedin ve ihtiyacınız olduğunda yükleyin ve önceden tanımlanmış listeden "örnek alın". Böylece yavaş hesaplamayı permn(10)ya da sadece bir kez yapabilirsiniz.
joran

Doğru, ama eğer bir yerde tüm permütasyonları saklıyorsam, bu bile faktöriyel (15) etrafında bozuluyor --- saklamak için çok fazla yer var. Bu yüzden tohum ayarlamanın permütasyonları toplu olarak örneklememe izin verip vermeyeceğini merak ediyorum --- eğer değilse, bunu yapmak için bir algoritma varsa.
Mittenchops

@Mittenchops Bir tohum ayarlamak performansı etkilemez, PRNG'ye her çağrı yaptığınızda aynı başlatmayı garanti eder.
Roman Luštrik

1
@Mitten Yardıma bakın set.seed: RNG'nin durumunun nasıl kaydedileceğini ve daha sonra nasıl geri yükleneceğini açıklar.
whuber
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.