シャッフルしたいです

レポートが忙しく感じて手がつかないので、プログラムを書いてなかったのでちょっと書いてみる。

すでに古いけども、Radium SoftwareThe Danger of Naïvetéが紹介されていた。配列の要素をランダムに並び替えたいけど、素朴に書くと偏りが出るんじゃない? っていう問題らしい。


素朴な方法っていうと、n個の要素から1つを引き抜いて新しい要素の上に順番に載せていくのが楽そうだ。

(defun shuffled-list-sub (n l r)
  (if (= n 0)
	  l
	(progn (setq x (random n))
	  (shuffled-list-sub (1- n) (append l (list (elt r x))) (delete (elt r x) r :count 1 :start x)))))
(defun shuffled-list (&rest r)
  (make-random-state)
  (let (x)
	(shuffled-list-sub (length r) nil r)))

こんな感じだろうか。リストから特定部位を消す方法が良く分からなかったので、その部分は回りくどいけど、イメージ通り。subは中に書いた方が良いかな?
 ともかく、これを60000回ほどテストしてみる。テスト結果のリストを作れば良いんだろうが、メモリを食いそうなので配列でも使ってみる。

(defun testcase ()
  (let ((ar (make-array 6 :initial-element 0)))
  (dotimes (x 600000)
	(let* ((test (shuffled-list 1 2 3))
		   (g (position test '((1 2 3) (1 3 2) (2 1 3) (2 3 1) (3 1 2) (3 2 1)) :test 'equal)))
	  (when g (setf (aref ar g) (1+ (aref ar g))))))
  ar))

これでそんなに偏るように思えないけどなあ・・・。

(compile 'testcase)
(testcase)
>> #(99899 99875 99997 100659 99660 99910)

うん、1%も偏らないね! で、何が問題だったのかな?