[英]Why Javascript implementation of Bubble sort much faster than others sorting algorithms?
[英]JavaScript fractal generation algorithms - why is one so much faster?
我正在尝试从第一原理编写 JavaScript 分形生成算法。 我知道那里有很多示例,但我想加入额外的功能来支持 Mandelbrot 和“旋转”Julia 以及“Burning Ship”和“Tricorn”等变体。 考虑到这一点,我实现了一个轻量级的复杂数学库(同样,我知道那里有标准的复杂 js 库,但我想从头开始构建一个作为学习练习)。
我测试了两个替代函数,一个使用标准数学函数的fractal
,另一个使用我的 Complex 库方法的fractalComplex
。 它们都工作得很好,但我惊讶地发现标准版本的速度几乎是复杂版本的两倍。 我期待一些额外的开销,但不是那么多!
谁能解释为什么? Complex 库“在幕后”使用相同的数学结构。 额外的开销是否完全取决于 object 的创建?
代码如下所示(输入参数 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 };
}
我的“复杂精简版”库如下:
// ------------------------------------------------------------------------
// 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);
}
附加编辑 20/12/2021 12:15:
对于它的价值,这就是我最终决定的......
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 };
}
以下 class 可能同时满足复杂 object 的性能问题和正确封装...
知名人士:
在适用的情况下,始终返回this
Complex object(即实例化对象),这有助于链接。 例如,
对于每个返回 Complex object 的 Complex 方法,配置两 (2) 个方法:
<method>This
的方法,它直接在this
object 上运行并包含 function 逻辑。<method>
的方法,它从this
object 克隆一个新的 Complex object,然后调用<method>This
以在克隆上执行 function 逻辑。 对其他 Complex 方法的内部调用通常应使用<method>This
版本,因为初始调用确定是就地使用现有的this
object,还是克隆它。 从那里,对其他 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 );
简而言之,以这种方式构建 class 提供了相同方法的两个版本:
this
的 object 的方法。this
object,然后调用包含 function 逻辑的关联方法。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.