Skip to content

Instantly share code, notes, and snippets.

@dominiccooney
Created April 6, 2015 03:34
Show Gist options
  • Save dominiccooney/2f1332b49076bf1c8929 to your computer and use it in GitHub Desktop.
Save dominiccooney/2f1332b49076bf1c8929 to your computer and use it in GitHub Desktop.
Levy Flights and Random Walks
<!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>
@cangSDARM
Copy link

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?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment