Pertanyaannya memiliki banyak interpretasi yang valid. Komentar - terutama yang mengindikasikan permutasi 15 elemen atau lebih diperlukan (15! = 1307674368000 semakin besar) - menunjukkan bahwa yang diinginkan adalah sampel acak yang relatif kecil , tanpa penggantian, dari semua n! = n * (n-1) (n-2) ... * 2 * 1 permutasi 1: n. Jika ini benar, ada (agak) solusi yang efisien.
Fungsi berikut rperm
,, menerima dua argumen n
(ukuran permutasi untuk sampel) dan m
(jumlah permutasi ukuran n untuk menggambar). Jika m mendekati atau melebihi n !, fungsi akan membutuhkan waktu yang lama dan mengembalikan banyak nilai NA: ini dimaksudkan untuk digunakan ketika n relatif besar (katakanlah, 8 atau lebih) dan m jauh lebih kecil dari n !. Ia bekerja dengan caching representasi string dari permutasi yang ditemukan sejauh ini dan kemudian menghasilkan permutasi baru (secara acak) sampai yang baru ditemukan. Ini mengeksploitasi kemampuan pengindeksan daftar asosiatif R untuk mencari daftar permutasi yang ditemukan sebelumnya dengan cepat.
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.
Sifat dari replicate
adalah mengembalikan permutasi sebagai vektor kolom ; misalnya , berikut mereproduksi contoh dalam pertanyaan asli, ditransformasikan :
> 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
Pengaturan waktu sangat baik untuk nilai m kecil hingga sedang, hingga sekitar 10.000, tetapi menurunkan untuk masalah yang lebih besar. Sebagai contoh, sampel dari m = 10.000 permutasi dari n = 1000 elemen (sebuah matriks dengan nilai 10 juta) diperoleh dalam 10 detik; sampel m = 20.000 permutasi n = 20 elemen yang diperlukan 11 detik, meskipun output (matriks 400.000 entri) jauh lebih kecil; dan menghitung sampel m = 100.000 permutasi n = 20 elemen dibatalkan setelah 260 detik (saya tidak memiliki kesabaran untuk menunggu penyelesaian). Masalah penskalaan ini tampaknya terkait dengan penskalaan inefisiensi dalam pengalamatan asosiatif R. Seseorang dapat mengatasinya dengan menghasilkan sampel dalam kelompok, katakanlah, sekitar 1000 atau lebih, kemudian menggabungkan sampel tersebut ke dalam sampel besar dan menghapus duplikat.
Edit
Kita dapat mencapai kinerja asimptotik linier dekat dengan memecah cache menjadi hierarki dua cache, sehingga R tidak perlu mencari melalui daftar besar. Secara konseptual (meskipun tidak seperti yang diterapkan), buat array yang diindeks oleh elemen pertama dari permutasi. Entri dalam larik ini adalah daftar semua permutasi yang membagikan elemen pertama . Untuk memeriksa apakah permutasi telah terlihat, gunakan elemen pertamanya untuk menemukan entri dalam cache dan kemudian cari permutasi tersebut di dalam entri itu. Kita dapat memilih untuk menyeimbangkan ukuran yang diharapkan dari semua daftar. Implementasi aktual tidak menggunakank k k kkkkkk-lipat array, yang akan sulit diprogram secara umum, tetapi menggunakan daftar lain.
Berikut adalah beberapa waktu yang berlalu dalam detik untuk berbagai ukuran permutasi dan jumlah permutasi berbeda yang diminta:
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
(Speedup yang kelihatannya anomali dari ukuran = 10 ke ukuran = 15 adalah karena level pertama dari cache lebih besar untuk ukuran = 15, mengurangi jumlah rata-rata entri dalam daftar tingkat kedua, sehingga mempercepat pencarian asosiatif R.) biaya dalam RAM, eksekusi dapat dibuat lebih cepat dengan meningkatkan ukuran cache tingkat atas. Hanya meningkatkan k.head
dengan 1 (yang mengalikan ukuran level atas dengan 10) mempercepat rperm(100000, size=10)
dari 11,77 detik menjadi 8,72 detik, misalnya. cache 10 kali lebih besar namun tidak mencapai perolehan yang berarti, clocking pada 8,51 detik.)
Kecuali untuk kasus 1.000.000 permutasi unik dari 10 elemen (sebagian besar dari semua 10! = Sekitar 3,63 juta permutasi semacam itu), praktis tidak ada tabrakan yang pernah terdeteksi. Dalam kasus luar biasa ini, ada 169.301 tabrakan, tetapi tidak ada kegagalan total (satu juta permutasi unik sebenarnya diperoleh).
Perhatikan bahwa dengan ukuran permutasi yang besar (lebih dari 20 atau lebih), peluang untuk mendapatkan dua permutasi yang identik bahkan dalam sampel sebesar 1.000.000.000 semakin kecil. Dengan demikian, solusi ini berlaku terutama dalam situasi di mana (a) sejumlah besar permutasi unik (b) antara dan atau lebih elemen yang dihasilkan tetapi meskipun demikian, (c) secara substansial lebih sedikit daripada semuapermutasi diperlukan.n = 15 n !n=5n=15n!
Kode kerja berikut.
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.