Монте Карло метод + странная ошибка при рассчете Пи
Вот, хотел сделать для детишек демонстацию рассчета Пи по методу Монте Карло, и тут облом :) Не могу понять почему ошибка, при чем постоянно площадь круга получается больше чем нужно. Код вроде простой, ничего такого заумного...
Код AS3:
package tests
{
import flash.display.BitmapData;
import flash.display.BitmapDataChannel;
import flash.display.Graphics;
import flash.display.Sprite;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import flash.events.Event;
import flash.geom.Point;
import flash.text.TextField;
import flash.text.TextFormat;
[SWF(width="255", height="275")]
public class MonteCarloIllustration extends Sprite
{
private static const RADIUS:uint = 0xFF;
private const _monitor:TextField = new TextField();
private const _data:BitmapData = new BitmapData(RADIUS, RADIUS, false, 0);
private const _noise:BitmapData = new BitmapData(RADIUS, RADIUS, false, 0);
private const _zeroPoint:Point = new Point();
private var _lastCalculated:Number = 0;
private var _knownMax:Number = 0;
private var _knownMin:Number = Number.MAX_VALUE;
public function MonteCarloIllustration()
{
super();
if (super.stage) this.init();
else super.addEventListener(Event.ADDED_TO_STAGE, this.init);
}
private function init(event:Event = null):void
{
super.removeEventListener(Event.ADDED_TO_STAGE, this.init);
super.stage.scaleMode = StageScaleMode.NO_SCALE;
super.stage.align = StageAlign.TOP_LEFT;
this.prepare();
super.addEventListener(Event.ENTER_FRAME, this.enterFrameHandler);
}
private function enterFrameHandler(event:Event):void
{
var pi:Number = this.calculate();
this._monitor.text = "Pi: " + pi;
this.addNoise();
this._lastCalculated = pi;
}
private function prepare():void
{
this._monitor.width = this._monitor.y = RADIUS;
this._monitor.height = 20;
this._monitor.defaultTextFormat = new TextFormat("_sans");
this.paint();
super.addChild(this._monitor);
}
private function paint():void
{
var canvas:Graphics = super.graphics;
canvas.beginBitmapFill(this._data);
canvas.drawRect(0, 0, RADIUS, RADIUS);
canvas.endFill();
}
private function addNoise():void
{
this._noise.noise(Math.random() * int.MAX_VALUE,
0, 0xFF, BitmapDataChannel.BLUE);
this._data.merge(this._noise,
this._data.rect, this._zeroPoint, 0, 0, 0xFF, 0);
}
private function calculate():Number
{
var pixels:Vector.<uint> = this._data.getVector(this._data.rect);
var squareAvg:uint;
var circleAvg:uint;
var pixel:uint;
var blue:uint;
var result:Number;
for (var total:uint = pixels.length - 1; total > 0; total--)
{
pixel = pixels[total];
// Blue is the channel we use to store the distribution data
blue = 0xFF & pixel;
if (isInCircle(total % RADIUS, total / RADIUS))
{
// Let's paint those within the circle red.
circleAvg += blue;
// In order to visually control that the poins are indeed within
// the circle, let's paint them red.
pixels[total] = 0xFF0000 | blue;
}
squareAvg += blue;
}
this._data.setVector(this._data.rect, pixels);
result = 4 * circleAvg / squareAvg;
if (this._knownMax)
{
// This is a very naive way to calculate the average :)
// You may want to improve here
this._knownMax = Math.max(this._knownMax, result);
this._knownMin = Math.min(this._knownMin, result);
result = (this._knownMax + this._knownMin + this._lastCalculated) / 3;
}
else this._knownMax = result;
return result;
}
private function isInCircle(xPos:uint, yPos:uint):Boolean
{
// Simple Pithagor theorem: the sum of powers of two of
// catheti of the rectangular triangle equals to the power of two
// of it's hypotenuse. Hence, if the sum of the square of
// the catheti is smaller or equal to the radius, the point is within
// the circle.
return (xPos * xPos + yPos * yPos) <= RADIUS * RADIUS;
}
}
}
EDIT: А, блин, сам спросил и тут же дошло. Рассчитываю-то правильно, только количество пикселов в битмапдате ограничено... И соотношение площади реально нарисованого в ней круга с общей площадью будет не таким, как у "идеального" круга и прямоугольника вокруг него. Хех, прийдется делать приближение по пикселу за итерацию, тогда хоть примерно эффект будет понятен :)
|