[英]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.