Форум Flasher.ru

Форум Flasher.ru (http://www.flasher.ru/forum/index.php)
-   ActionScript 3.0 (http://www.flasher.ru/forum/forumdisplay.php?f=83)
-   -   Монте Карло метод + странная ошибка при рассчете Пи (http://www.flasher.ru/forum/showthread.php?t=157425)

wvxvw 05.06.2011 18:18

Монте Карло метод + странная ошибка при рассчете Пи
 
Вот, хотел сделать для детишек демонстацию рассчета Пи по методу Монте Карло, и тут облом :) Не могу понять почему ошибка, при чем постоянно площадь круга получается больше чем нужно. Код вроде простой, ничего такого заумного...
Код 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: А, блин, сам спросил и тут же дошло. Рассчитываю-то правильно, только количество пикселов в битмапдате ограничено... И соотношение площади реально нарисованого в ней круга с общей площадью будет не таким, как у "идеального" круга и прямоугольника вокруг него. Хех, прийдется делать приближение по пикселу за итерацию, тогда хоть примерно эффект будет понятен :)

alatar 05.06.2011 18:48

Можно просто радиус увеличить, хотя бы в два раза. Сразу станет ближе к истине.

wvxvw 05.06.2011 19:54

Ну фишка-то в том, что нужно было продемонстритовать, как с помощью этого метода затрачивая меньше усилий можно прощитать сложную формулу, а получилось практически наоборот :) Вобщем, детерминизм рулит, по-прежнему :D

i.o. 05.06.2011 20:25

wvxvw, а зачем ты через целочисленные значения пытаешься вычислить Пи?
Почему не напрямую использовать Math.random() вместо шума [0..255]?

Добавлено через 1 час 18 минут
Код AS3:

package 
{
        import flash.display.Sprite;
        import flash.events.Event;
        import flash.text.TextField;
 
        public class Main extends Sprite
        {
 
                public function Main():void
                {
                        this.computePI( 1E+06, 1E+07 );
                }
 
 
                ///
                private var _txtLog : TextField;
 
 
                ///
                public function computePI( radius:Number, iterations:Number=10 ) : void
                {
                        var radiusPow2:Number = radius * radius;
                        var hits:Number = 0;
                        var i:Number = -1;
                        while (++i < iterations)
                        {
                                var rndX:Number = 2.0 * radius * (0.5 - Math.random());
                                var rndY:Number = 2.0 * radius * (0.5 - Math.random());
 
                                if (rndX * rndX + rndY * rndY <= radiusPow2)
                                        hits += 1.0;
                        }
 
                        var rectS:Number = 4.0 * radiusPow2;
                        var circS:Number = rectS * hits / iterations;
                        var PI:Number = circS / radiusPow2;
 
                        this.addLog("PI: " + PI);
                        this.addLog("rectS: " + rectS);
                        this.addLog("circS: " + circS);
                        this.addLog("iterations: " + iterations);
                        this.addLog("hits: " + hits);
                }
 
                ///
                public function addLog( msg:String ) : void
                {
                        if (!this._txtLog)
                        {
                                this._txtLog = new TextField();
                                this._txtLog.x = 10.0;
                                this._txtLog.y = 10.0;
                                this._txtLog.width = 400.0;
                                this._txtLog.height = 300.0;
                                this.addChild( this._txtLog );       
                        }
 
                        this._txtLog.appendText( msg + "\n" );
                }
 
        }
}

Цитата:

PI: 3.1416376
rectS: 4000000000000
circS: 3141637600000
iterations: 10000000
hits: 7854094
как вариант

wvxvw 05.06.2011 22:33

Потому что эффект тот же, точности хватит ровно на столько, на сколько ее может хватить используя конкретный численный тип и радиус. А целочисленный потому что в битмапдате точнее не будет, а хотелось чтобы с картинкой. Собственно, ради картинки и делалось. Т.е. от того, что используя число с плавающей запятой мы получим на 2-3 знака после запятой больше, с точки зрения вероятно бесконечного количества знаков после запятой разница мизерная :)
Но за пример спасибо. Я кстати перед этим на лиспе сваял, и там можно было с 128-bit float, но, опять же :)


Часовой пояс GMT +4, время: 11:57.

Copyright © 1999-2008 Flasher.ru. All rights reserved.
Работает на vBulletin®. Copyright ©2000 - 2026, Jelsoft Enterprises Ltd. Перевод: zCarot
Администрация сайта не несёт ответственности за любую предоставленную посетителями информацию. Подробнее см. Правила.