配列をシャッフルする関数を作ってみた
ヌジョレーボーボーが楽しそうだったので私もコメント欄にCFML版を書いてみたんですが、CFには配列をシャッフルする関数がなかったので長くなってしまいました。
無いなら作ってしまえ、というわけで関数を作ってみたので今後のために書き残しておきます。
<cffunction name="arrayShuffle" returntype="array">
<cfargument name="a" type="array" required="true">
<cfset createObject("java","java.util.Collections").shuffle(arguments.a)>
<cfreturn arguments.a>
</cffunction>
Javaに頼らないなら、
<cffunction name="arrayShuffle2" returntype="array">
<cfargument name="a" type="array" required="true">
<cfset local.b = []>
<cfloop condition="arrayLen(arguments.a)">
<cfset i = randRange(1, arrayLen(arguments.a))>
<cfset arrayAppend(local.b, arguments.a[i])>
<cfset arrayDeleteAt(arguments.a, i)>
</cfloop>
<cfreturn local.b>
</cffunction>
この関数があれば、ボジョレーのやつも1行で書けますね!
<cfoutput>#arrayToList(arrayMap(arrayShuffle(['ボ','ジョ','レ','ヌ','ボ']), function(x,i){return i gt 2 ? x&'ー' : x}),'')#</cfoutput>
badShuffle
arraySort()を用いたシャッフルも思いついたんですが、
<cffunction name="arrayShuffle3" returntype="array">
<cfargument name="a" type="array" required="true">
<cfset arraySort(arguments.a, function(){return randRange(-1, 1)})>
<cfreturn arguments.a>
</cffunction>
Array#sort実装のshuffleは偏るという記事にあるように、だめみたいです。
均等に分布しているかを調べる
<cfscript>
array = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J'];
times = 1000;
trial = function (shuffle) {
var arr = [];
var average = {};
var i = 0;
var j = 0;
var len = 0;
i = 1;
len = arrayLen(array);
while (i <= len) {
average[array[i]] = 0;
i++;
}
i = times;
while (i--) {
arr = shuffle(array);
j = 1;
len = arrayLen(arr);
while (j <= len) {
average[arr[j]] += j - 1;
j++;
}
}
for (elem in average) {
average[elem] /= times;
}
return average;
};
writeDump(trial(arrayShuffle));
writeDump(trial(arrayShuffle2));
writeDump(trial(arrayShuffle3));
</cfscript>