仮想計算機構

IT業界と無縁な派遣社員のブログ

レスラー方程式

レスラー方程式は以下で定義される連続力学系です。


\dot x = -y-z\\
\dot y = x+ay\\
\dot z = bx-z(x-c)

ここでドット記号は時間 t での微分を表すこととします。

連続時間ではシミュレーションできないので時間を0.02ずつ増やしてシミュレーションしました。また、パラメータは


a=0.2\\
b=0.2\\
c=5.7

としました。

f:id:riverta1992:20201128140810g:plain

以下シミュレーションに用いたコードになります。

<html>
    <body>
        <script type="text/javascript" src="rossler.js"></script>
    </body>
</html>

const A = 0.2;
const B = 0.2;
const C = 5.7;
const D = 0.02;

// x軸まわりの回転
function rotate_x(x,y,z,theta){
  return [x,y*Math.cos(theta)-z*Math.sin(theta),y*Math.sin(theta)+z*Math.cos(theta)];
}

// y軸まわりの回転
function rotate_y(x,y,z,theta){
  return [x*Math.cos(theta)+z*Math.sin(theta),y,-x*Math.sin(theta)+z*Math.cos(theta)];
}

// z軸まわりの回転
function rotate_z(x,y,z,theta){
  return [x*Math.cos(theta)-y*Math.sin(theta),x*Math.sin(theta)+y*Math.cos(theta),z];
}

const WIDTH = 400;
const HEIGHT = 400;

const E = 20;
const X_MAX = E;
const X_MIN = -E;
const Y_MAX = E;
const Y_MIN = -E;
const Z_MAX = E;
const Z_MIN = -E;

const canvas = document.createElement('canvas');
canvas.width = WIDTH;
canvas.height = HEIGHT;

const context = canvas.getContext('2d');

document.body.appendChild(canvas);

class Point{
    constructor(x, y, z, color){
      this.x = x;
      this.y = y;
      this.z = z;
      this.color = color;
    }
    update(){
        this.prev_x = this.x;
        this.prev_y = this.y;
        this.prev_z = this.z;
        let dx = -this.y-this.z;
        let dy = this.x + A*this.y;
        let dz = B + this.z*(this.x-C);
        this.x += D * dx;
        this.y += D * dy;
        this.z += D * dz;
    }
    render(context){
      context.beginPath();
      context.strokeStyle = this.color;

      let [x0,y0,z0] = rotate_z(this.prev_x, this.prev_y, this.prev_z,Math.PI);
      [x0,y0,z0] = rotate_x(x0,y0,z0,Math.PI/4);

      let [x1,y1,z1] = rotate_z(this.x, this.y, this.z,Math.PI); 
      [x1,y1,z1] = rotate_x(x1,y1,z1,Math.PI/4);

      context.moveTo(WIDTH*(x0-X_MIN)/(X_MAX-X_MIN), HEIGHT*(y0-Y_MIN)/(Y_MAX-Y_MIN));
      context.lineTo(WIDTH*(x1-X_MIN)/(X_MAX-X_MIN), HEIGHT*(y1-Y_MIN)/(Y_MAX-Y_MIN));
      context.stroke(); 
    }
}

const objects = [];
let count = 0;

let colors = ["blue","green","red","purple","yellow"];
for(let i=0;i<3;i++){
  objects.push(new Point(1+0.01*i,1+0.01*i,1+0.01*i,colors[i]));
}

function loop(timestamp) {
  objects.forEach((obj) => obj.update());
  objects.forEach((obj) => obj.render(context));
  window.requestAnimationFrame((ts) => loop(ts));
  count++;
  context.fillStyle = "white";
  context.fillRect(0,0,60,40);
  context.fillStyle = "black";
  context.fillText("t : " + (D*count).toFixed(2), 10, 10);

}

window.requestAnimationFrame((ts) => loop(ts));