简体   繁体   English

JavaScript 分形生成算法 - 为什么一个这么快?

[英]JavaScript fractal generation algorithms - why is one so much faster?

I'm trying to write a JavaScript fractal generation algorithm from first principles.我正在尝试从第一原理编写 JavaScript 分形生成算法。 I'm aware that there are many examples out there but I wanted to incorporate additional functionality to support both Mandelbrot and 'spinning' Julia with variants such as 'Burning Ship' and 'Tricorn'.我知道那里有很多示例,但我想加入额外的功能来支持 Mandelbrot 和“旋转”Julia 以及“Burning Ship”和“Tricorn”等变体。 With this in mind I implemented a lightweight Complex maths library ( again, I'm aware there are standard Complex js libraries out there but I wanted to build one from scratch as a learning exercise ).考虑到这一点,我实现了一个轻量级的复杂数学库(同样,我知道那里有标准的复杂 js 库,但我想从头开始构建一个作为学习练习)。

I tested two alternate functions, one fractal using standard maths functions and the other fractalComplex using my Complex library methods.我测试了两个替代函数,一个使用标准数学函数的fractal ,另一个使用我的 Complex 库方法的fractalComplex They both work fine, but I was surprised to find that the standard version is almost twice as fast as the Complex version.它们都工作得很好,但我惊讶地发现标准版本的速度几乎是复杂版本的两倍 I was expecting some additional overhead but not that much!我期待一些额外的开销,但不是那么多!

Can anyone explain why?谁能解释为什么? The Complex library is using the same maths constructs 'under the covers'. Complex 库“在幕后”使用相同的数学结构。 Is the additional overhead purely down to object creation?额外的开销是否完全取决于 object 的创建?

The code is reproduced below (the input parms z and c are objects of the form {re, im} ).代码如下所示(输入参数 z 和 c 是{re, im}形式的对象)。

function fractal(z, c, maxiter) {

    var i, za, re, im, re2, im2;
    c = (settype === JULIA ? c : z);

    // Iterate until abs(z) exceeds escape radius
    for (i = 0; i < maxiter; i += 1) {

        if (setvar === BURNING_SHIP) {
            re = Math.abs(z.re);
            im = -Math.abs(z.im);
        }
        else if (setvar === TRICORN) {
            re = z.re
            im = -z.im; // conjugate z
        }
        else { // Mandelbrot
            re = z.re;
            im = z.im;
        }

        re2 = re * re;
        im2 = im * im;
        z = { // z = z² + c
            re: re2 - im2 + c.re,
            im: 2 * im * re + c.im
        };

        za = re2 + im2 // abs(z)²
        if (za > 4) { // abs(z)² > radius²
            break;
        }
    }
    za = Math.sqrt(za); // abs(z)
    return { i, za };
}

function fractalComplex(z, c, maxiter, n, radius) {

    var i, za;
    c = (settype === JULIA ? c : z);

    // Iterate until abs(z) exceeds escape radius
    for (i = 0; i < maxiter; i += 1) {

        if (setvar === BURNING_SHIP) {
            z = new Complex(Math.abs(z.re), -Math.abs(z.im))
        }
        if (setvar === TRICORN) {
            z = z.conjugate()
        }

        z = z.quad(n, c); // z = zⁿ + c
        za = z.abs();
        if (za > radius) {
            break;
        }
    }
    return { i, za };
}

My "Complex lite" library is as follows:我的“复杂精简版”库如下:

// ------------------------------------------------------------------------
// A basic complex number library which implements the methods used for
// Mandelbrot and Julia Set generation.
// ------------------------------------------------------------------------
'use strict';

// Instantiate complex number object.
function Complex(re, im) {
  this.re = re; // real
  this.im = im; // imaginary
}

Complex.prototype = {

  're': 0,
  'im': 0,

  // Set value.
  'set': function (re, im) {
    this.re = re;
    this.im = im;
  },

  // Get magnitude.
  'abs': function () {
    return Math.sqrt(this.re * this.re + this.im * this.im);
  },

  // Get polar representation (r, θ); angle in radians.
  'polar': function () {
    return { r: this.abs(), θ: Math.atan2(this.im, this.re) };
  },

  // Get square.
  'sqr': function () {
    var re2 = this.re * this.re - this.im * this.im;
    var im2 = 2 * this.im * this.re;
    return new Complex(re2, im2);
  },

  // Get complex number to the real power n.
  'pow': function (n) {
    if (n === 0) { return new Complex(1, 0); }
    if (n === 1) { return this; }
    if (n === 2) { return this.sqr(); }
    var pol = this.polar();
    var rn = Math.pow(pol.r, n);
    var θn = n * pol.θ;
    return cart(rn, θn);
  },

  // Get conjugate.
  'conjugate': function () {
    return new Complex(this.re, -this.im);
  },

  // Get quadratic zⁿ + c.
  'quad': function (n, c) {
    var zn = this.pow(n);
    return new Complex(zn.re + c.re, zn.im + c.im);
  },

  // Rotate by angle in radians.
  'rotate': function (angle) {
    var pol = this.polar();
    angle += pol.θ;
    return new Complex(pol.r * Math.cos(angle), pol.r * Math.sin(angle));
  },

  // String in exponent format to specified significant figures.
  'toString': function (sig = 9) {
    return this.re.toExponential(sig) + " + " + this.im.toExponential(sig) + "i";
  },
}

// Convert polar (r, θ) to cartesian representation (re, im).
function cart(r, θ) {
  var re = r * Math.cos(θ);
  var im = r * Math.sin(θ);
  return new Complex(re, im);
}

Additional edit 20/12/2021 12:15:附加编辑 20/12/2021 12:15:

For what it's worth, this is what I eventually settled on...对于它的价值,这就是我最终决定的......

function fractal(p, c, maxiter) {

        var i, za, zre, zim, cre, cim, tmp;
        var lastre = 0;
        var lastim = 0;
        var per = 0;
        if (setmode === JULIA) {
            cre = c.re;
            cim = c.im;
            zre = p.re;
            zim = p.im;
        }
        else { // Mandelbrot mode
            cre = p.re;
            cim = p.im;
            zre = 0;
            zim = 0;
        }

        // Iterate until abs(z) exceeds escape radius
        for (i = 0; i < maxiter; i += 1) {

            if (setvar === BURNING_SHIP) {
                zre = Math.abs(zre);
                zim = -Math.abs(zim);
            }
            else if (setvar === TRICORN) {
                zim = -zim; // conjugate z
            }

            // z = z² + c
            tmp = zre * zre - zim * zim + cre;
            zim = 2 * zre * zim + cim;
            zre = tmp;

            // Optimisation - periodicity check speeds
            // up processing of points within set
            if (PERIODCHECK) {
                if (zre === lastre && zim === lastim) {
                    i = maxiter;
                    break;
                }
                per += 1;
                if (per > 20) {
                    per = 0;
                    lastre = zre;
                    lastim = zim;
                }
            }
            // ... end of optimisation

            za = zre * zre + zim * zim // abs(z)²
            if (za > radius) { // abs(z)² > radius²
                break;
            }
        }
        return { i, za };
}

The following class might satisfy both performance concerns and proper encapsulation of the Complex object...以下 class 可能同时满足复杂 object 的性能问题和正确封装...

Notables:知名人士:

  • Where applicable, always return the this Complex object (ie, the instantiated object), which facilitates chaining.在适用的情况下,始终返回this Complex object(即实例化对象),这有助于链接。 Eg,例如,

    • x = new Complex( 10, 5 ).sqrThis().powThis( 1 ); x = new Complex(10, 5).sqrThis().powThis(1);
  • For every Complex method that returns a Complex object, configure two (2) methods:对于每个返回 Complex object 的 Complex 方法,配置两 (2) 个方法:

    • A method, dubbed <method>This that operates directly on the this object and contains the function logic.一种称为<method>This的方法,它直接在this object 上运行并包含 function 逻辑。
    • A method, dubbed <method> that clones a new Complex object from the this object, and then calls <method>This to execute the function logic on the clone.一种名为<method>的方法,它从this object 克隆一个新的 Complex object,然后调用<method>This以在克隆上执行 function 逻辑。
    • This provides the developer the choice of methods that either updates the existing object or returns a new object.这为开发人员提供了更新现有 object 或返回新 object 的方法的选择。
  • Internal calls to other Complex methods generally should use the <method>This version, as the initial call establishes whether to use the existing this object in-place, or to clone it.对其他 Complex 方法的内部调用通常应使用<method>This版本,因为初始调用确定是就地使用现有的this object,还是克隆它。 From there, all internal calls to the other Complex methods will either continue to operate on the this object or the clone.从那里,对其他 Complex 方法的所有内部调用将继续在this object 或克隆上运行。

 // ------------------------------------------------------------------------ // A basic complex number library which implements the methods used for // Mandelbrot and Julia Set generation. // ------------------------------------------------------------------------ 'use strict'; class Complex { constructor( reOrComplex, im ) { this.set( reOrComplex, im ); } set( reOrComplex, im ) { if ( reOrComplex instanceof Complex ) { this.re = reOrComplex.re; this.im = reOrComplex.im; } else { this.re = reOrComplex; this.im = im; } return this; } abs() { return Math.sqrt(this.re * this.re + this.im * this.im); } toPolar() { return { r: this.abs(), θ: Math.atan2(this.im, this.re) }; } sqrThis() { return this.set( this.re * this.re - this.im * this.im, 2 * this.im * this.re ); } sqr() { return new Complex( this ).sqrThis(); } powThis( n ) { if ( n === 0 ) { return this.set( 1, 0 ) }; if ( n === 1 ) { return this; } if ( n === 2 ) { return this.sqrThis(); } let polar = this.toPolar(); return this.toCartesianThis( Math.pow(polar.r, n), n * polar.θ ); } pow( n ) { return new Complex( this ).powThis( n ); } conjugateThis() { return this.set( this.re, -this.im); } conjugate() { return new Complex( this ).conjugateThis(); } quadraticThis( n, c ) { let zn = this.powThis( n ); return this.set( zn.re + c.re, zn.im + c.im ); } quadratic( n, c ) { return new Complex( this ).quadraticThis( n, c ); } rotateThis( deltaAngle ) { let polar = this.toPolar(); let angle = polar.θ + deltaAngle; return this.set( polar.r * Math.cos(angle), polar.r * Math.sin(angle) ); } rotate( deltaAngle ) { return new Complex( this ).rotateThis( deltaAngle ); } toString( sig = 9 ) { return this.re.toExponential( sig ) + " + " + this.im.toExponential( sig ) + "i"; } toCartesianThis( r, θ ) { return this.set( r * Math.cos( θ ), r * Math.sin( θ ) ); } } // Convert polar (r, θ) to cartesian representation (re, im). Complex.toCartesian = function ( r, θ ) { return new Complex().toCartesianThis( r, θ ); } let x = new Complex( 10, 5 ).sqrThis(); console.log( 'x = new Complex( 10, 5 ).sqrThis()' ); console.log( 'x is ', x ); let y = x.pow( 3 ); console.log ( 'y = x.pow( 3 )' ); console.log ( 'y is ', y ); x.sqr(); console.log ( 'x.sqr()' ); console.log ( 'x is still', x ); x.sqrThis(); console.log ( 'x.sqrThis()' ); console.log ( 'x is now', x );

In short, structuring a class this way provides two versions of the same method:简而言之,以这种方式构建 class 提供了相同方法的两个版本:

  • One method which embodies the function logic and directly mutates the instantiated this object.一种体现 function 逻辑并直接改变实例this的 object 的方法。
  • And the other method which simply clones the instantiated this object, and then calls the associated method containing the function logic.而另一种方法是简单地克隆实例化的this object,然后调用包含 function 逻辑的关联方法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 为什么Javascript实现Bubble排序比其他排序算法快得多? - Why Javascript implementation of Bubble sort much faster than others sorting algorithms? 为什么打电话比申请快得多? - Why is call so much faster than apply? 为什么JS中的简单因子算法要比Python或R快得多? - Why simple factorial algorithms in JS are much faster than in Python or R? 为什么循环数组比JavaScript的本机`indexOf`快得多? - Why is looping through an Array so much faster than JavaScript's native `indexOf`? 为什么检查void 0比检查undefined快得多? - Why is checking void 0 so much faster than checking undefined? 为什么用while循环填充新数组如此之快? - Why is filling a new Array so much faster with a while loop? 为什么异步代码比同步代码快得多? - Why is async code considered so much faster than synchronous? 为什么使用 JavaScript 对 32 位数字进行排序比对 33 位数字进行排序快得多? - Why does sorting 32-bit numbers using JavaScript so much faster than sorting 33-bit numbers? Javascript:为什么这个基准测试显示array.shift()在读取数组中的值时比array [i]快得多? - Javascript: Why does this benchmark show array.shift() to be so much faster than array[i] at reading off values from an array? 哪一个更快?为什么? javascript - Which one is faster and why ? javascript
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM