I have a project that generates strings of random numbers that are used to select items from a test bank. I noticed that some of the items were being selected at disproportionately high rates so I decided to check the 'randomness' of Math.Random. The following code produces randomly ordered lists of the numbers 0 through n-1. It then counts the number of times the first item is a 0, 1, 2,... ,n-1 You can change the number of samples that are generated with the horizontal slider. What I have produced appears to indicate that the random numbers are not at all random For example, if I select 100 samples of a six digit string, I get the following distribution suggesting that 0, and 5 are greatly under represented: 11,23,15,18,24,9 This pattern holds as I re-run the simulation. I've checked my code but would greatly appreciate the insight of others concerning the accuracy of this. I've heard that AS3 does not produce true random numbers, but can they really be this bad?
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" minWidth="955" minHeight="600" >
<mx:Script>
<![CDATA[
import mx.events.ListEvent;
private var startingArray:Array = [];
private var questionsArray:Array;
private var countArray:Array;
private var randomNumbers:int = 3;
private function calculate():void{
countArray = [0,0,0,0,0,0,0,0,0,0];
for( var i:int = 0; i < slider.value; i++){
questionsArray = [];//Reset the list of questions
createRandomListOfQuestions(randomNumbers);
}
result.text = String(countArray);
}
public function createRandomListOfQuestions(_numQuestions:int):void{
//Create an array containing the sequence of test questions
var numQuestions:int=_numQuestions;
//Reset the array
startingArray=[];//Contains a randomized question order
for (var i:int=0;i<numQuestions; i++){//Create an array of question numbers starting at 1
var count:int = 0
startingArray.push(i);
}
//splice() removes one or more elements from an array and returns the deleted elements,
//here only one (as specified in the second argument)
while (startingArray.length > 0) {//Create a randomized list (questionsArray) from startingArray
var val:int =startingArray.splice(Math.round(Math.random() * (startingArray.length - 1)), 1)
questionsArray.push(val);
if(count == 0){
countArray[val] += 1
count++
}
}
questionsArrayText.text += String(questionsArray) + "\r";
}
private function changeEvt(event:Event):void {
randomNumbers = event.currentTarget.selectedItem.data
}
]]>
</mx:Script>
<mx:VBox horizontalCenter="0" verticalCenter="0">
<mx:Text x="487" y="261" text="{}" width="500" id="result"/>
<mx:ComboBox change="changeEvt(event)" >
<mx:ArrayCollection>
<mx:Object label="Three" data="3"/>
<mx:Object label="Four" data="4"/>
<mx:Object label="Five" data="5"/>
<mx:Object label="Six" data="6"/>
<mx:Object label="Seven" data="7"/>
<mx:Object label="Eight" data="8"/>
<mx:Object label="Nine" data="9"/>
<mx:Object label="Ten" data="10"/>
</mx:ArrayCollection>
</mx:ComboBox>
<mx:Button label="New list" click="calculate()"/>
<mx:HSlider id="slider" value="5" minimum="5" maximum="100" snapInterval="1" />
<mx:Label text="Random Numbers: {Math.round(slider.value) }"/>
</mx:VBox>
<mx:Text id="questionsArrayText" horizontalCenter="0" verticalCenter="0"/>
</mx:Application>
There are numerous implementations of custom random number generation available; however, ActionScript's Math.random()
produces an expected distribution given a fair number of samples.
Below are distributions via Math.random()
generating a number between 0 and 100. Vertical axis shows the number of times that value was generated.
Code to generate and visualize samples:
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
creationComplete="creationCompleteHandler(event)">
<fx:Script>
<![CDATA[
import mx.collections.ArrayList;
import mx.events.FlexEvent;
[Bindable]
protected var data:ArrayList = new ArrayList();
protected function creationCompleteHandler(event:FlexEvent):void
{
var i:uint = 0;
var set:Dictionary = new Dictionary();
const ITERATIONS:uint = 100;
for (i = 0; i < 100; i++)
{
set[i] = {value: i, count: 0};
}
for (i = 0; i < ITERATIONS; i++)
{
var n:uint = Math.random() * 100;
set[n].count++;
}
for (i = 0; i < 100; i++)
{
data.addItem({value: set[i].value, count: set[i].count});
}
}
]]>
</fx:Script>
<fx:Declarations>
<mx:SolidColor id="areaFill"
color="0x123456"
alpha=".3" />
<mx:SolidColorStroke id="areaStroke"
color="0x123456" />
</fx:Declarations>
<mx:AreaChart height="100%"
width="100%"
paddingLeft="5"
paddingRight="5"
showDataTips="true"
dataProvider="{data}">
<mx:verticalAxis>
<mx:LinearAxis title="count" />
</mx:verticalAxis>
<mx:horizontalAxis>
<mx:CategoryAxis categoryField="value"
title="value" />
</mx:horizontalAxis>
<mx:series>
<mx:AreaSeries yField="count"
areaStroke="{areaStroke}"
areaFill="{areaFill}">
</mx:AreaSeries>
</mx:series>
</mx:AreaChart>
</s:Application>