ジンジャーブレッドマン写像は下記の式で定義される離散力学系です。
二次元の点(x, y)を50x50=2500個用意して、各点がどのような軌跡を描くのかやってみました。点は座標に応じて色づけしています。具体的にはHLS色空間の色相Hを以下のように計算しています。
結果は下記の通りです。
Wikipediaに載っている図と同じようなパターンを再現できました。見た目は単純な写像なのに、なかなか面白い結果です。ジンジャーブレッドマンに似ていることから命名されているようなのですが、自分はデュエルマスターズの「浄化の精霊ウルス」にしかみえませんでした。
以下コードです。
<html> <body> <script type="text/javascript" src="ginger.js"></script> </body> </html>
const WIDTH = 600; const HEIGHT = 600; const canvas = document.createElement('canvas'); canvas.width = WIDTH; canvas.height = HEIGHT; const context = canvas.getContext('2d'); document.body.appendChild(canvas); class Point{ constructor(id, x, y, color, th){ this.id = id; this.x = x; this.y = y; this.color = color; this.th = th; } update(){ let new_x = 1-this.y+Math.abs(this.x); let new_y = this.x; this.x = new_x; this.y = new_y; } render(context,xmin,xmax,ymin,ymax){ context.beginPath(); context.fillStyle = this.color; context.fillRect(WIDTH*(xmax-this.x)/(xmax-xmin), HEIGHT*(ymax-this.y)/(ymax-ymin), 1, 1); context.fill(); } } context.beginPath(); context.fillStyle = "white" context.fillRect(0,0,WIDTH, HEIGHT); context.fill(); N = 50; mv = 10; const objects = []; for(let i=0;i<N;i++){ for(let j=0;j<N;j++){ let tx = 2*mv*i/N-mv; let ty = 2*mv*j/N-mv; let th = 0; if(tx > 0){ th = (180*Math.atan(ty/tx)/Math.PI+360)%360; }else if(tx < 0){ th = (180*Math.atan(ty/tx)/Math.PI+180)%360; } objects.push(new Point(i+j*N,tx ,ty ,'hsl('+ th +','+70+'%,'+50+'%)', th)); } } for(let i=0;i<N;i++){ for(let j=0;j<N;j++){ if(objects[i+j*N].x == 0){ tmp=0;let count=0; if(j>0){ tmp += objects[i+(j-1)*N].th; count += 1; } if(j+1<N){ tmp += objects[i+(j+1)*N].th; count += 1; } objects[i+j*N].color = 'hsl('+ tmp/count +','+70+'%,'+50+'%)'; } } } let count = 0; function loop(timestamp) { context.clearRect(0, 0, 100, 50); objects.forEach((obj) => obj.update()); objects.forEach((obj) => obj.render(context,-mv,mv,-mv,mv)); window.requestAnimationFrame((ts) => loop(ts)); count++; context.fillStyle = "black"; context.font = "20px sanserif"; context.fillText("n : " + count, 10, 20); } window.requestAnimationFrame((ts) => loop(ts));