Created
April 6, 2015 03:34
-
-
Save dominiccooney/2f1332b49076bf1c8929 to your computer and use it in GitHub Desktop.
Levy Flights and Random Walks
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<style> | |
canvas { | |
width: 1000px; | |
height: 1000px; | |
background: black; | |
} | |
</style> | |
<canvas width="1000" height="1000"></canvas> | |
<script> | |
'use strict'; | |
// Generates a random number uniformly distributed between 0 and n-1. | |
function uniformInt(n) { | |
return Math.floor(Math.random() * n); | |
} | |
// Generates a uniformly distributed angle, in radians. | |
function uniformAngle() { | |
return 2 * Math.PI * Math.random(); | |
} | |
const epsilon = 1e-16; | |
function standardNormal() { | |
standardNormal.generate = !standardNormal.generate; | |
if (standardNormal.generate) { | |
// There's a cached result. | |
return standardNormal.y; | |
} | |
do { | |
var u = Math.random(); | |
var v = uniformAngle(); | |
} while (u < Number.MIN_VALUE); | |
let factor = Math.sqrt(-2 * Math.log(u)); | |
let x = factor * Math.cos(v); | |
standardNormal.y = factor * Math.sin(v); | |
return x; | |
} | |
standardNormal.generate = true; | |
function clip(min, max, value) { | |
return Math.max(min, Math.min(max, value)); | |
} | |
let width = 1000; | |
let height = 1000; | |
function Walk() { | |
this.xs = [uniformInt(width)]; | |
this.ys = [uniformInt(height)]; | |
} | |
Walk.prototype.draw = function(c) { | |
c.beginPath(); | |
c.moveTo(this.xs[0], this.ys[0]); | |
for (let i = 1; i < this.xs.length; i++) { | |
c.lineTo(this.xs[i], this.ys[i]); | |
} | |
c.stroke(); | |
} | |
// A random walk with a uniformly distributed step size. | |
function RandomWalk(stepSize) { | |
Walk.call(this); | |
this.stepSize = stepSize; | |
} | |
RandomWalk.prototype = Object.create(Walk.prototype); | |
RandomWalk.prototype.step = function() { | |
let dir = uniformAngle(); | |
let dist = Math.random() * this.stepSize; | |
this.xs.push( | |
clip(0, width, this.xs[this.xs.length - 1] + Math.cos(dir) * dist)); | |
this.ys.push( | |
clip(0, height, this.ys[this.ys.length - 1] + Math.sin(dir) * dist)); | |
}; | |
// A Levy flight. | |
function LevyFlight(stepSize) { | |
Walk.call(this); | |
this.stepSize = stepSize; | |
// See Yang, X.-S., Nature-Inspired Optimization Algorithms, p. 50. | |
this.beta = 3/2; | |
// From http://stackoverflow.com/questions/15454183/how-to-make-a-function-that-computes-the-factorial-for-numbers-with-decimals | |
function gamma(z) { | |
return Math.sqrt(2 * Math.PI / z) * Math.pow((1 / Math.E) * (z + 1 / (12 * z - 1 / (10 * z))), z); | |
} | |
// I have some doubt that this is correct because the denominator is zero | |
// for beta=1 which should correspond to the Cauchy distribution. | |
this.sigmaU = gamma(1 + this.beta) * Math.sin(Math.PI * this.beta / 2) / | |
(gamma((1 + this.beta) / 2) * this.beta * | |
Math.pow(2, (this.beta - 1) / 2)); | |
this.sigmaU = Math.pow(this.sigmaU, 1 / this.beta); | |
this.sigmaV = 1; | |
} | |
LevyFlight.prototype = Object.create(Walk.prototype); | |
LevyFlight.prototype.step = function() { | |
let dir = uniformAngle(); | |
let u = standardNormal() * this.sigmaU * this.stepSize; | |
let v = standardNormal() * this.sigmaV * this.stepSize; | |
let dist = u / Math.pow(Math.abs(v), 1 / this.beta); | |
this.xs.push( | |
clip(0, width, this.xs[this.xs.length - 1] + Math.cos(dir) * dist)); | |
this.ys.push( | |
clip(0, height, this.ys[this.ys.length - 1] + Math.sin(dir) * dist)); | |
}; | |
var canvas = document.querySelector('canvas'); | |
var c = canvas.getContext('2d'); | |
c.clearRect(0, 0, c.canvas.width, c.canvas.height); | |
// Draw a random walk. | |
var walk = new RandomWalk(20); | |
for (let i = 0; i < 1000; i++) { | |
walk.step(); | |
} | |
c.strokeStyle = 'skyblue'; | |
walk.draw(c); | |
// Draw a Levy flight. | |
var flight = new LevyFlight(20); | |
for (let i = 0; i < 1000; i++) { | |
flight.step(); | |
} | |
c.strokeStyle = 'red'; | |
flight.draw(c); | |
</script> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
nice code! But what I don't understand is why
standardNormal
does not return an object containing x and y, but returns x and caches y?