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, rperm
iki 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ı replicate
permü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.head
1 (ü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.