必ず元の場所に戻るランダムウォークのサンプル


概要

一定量の移動幅を持つランダムウォークをし終わったときに、必ず元の場所に戻ってくるコードのサンプルです。

原理

プラス方向に移動した分だけマイナス方向にも動く(どのタイミングで逆の動きをするかはランダム)ことにより、移動量の合計が0に戻る仕組みです。

デモ

こんな風に横移動した後に最初の位置に戻ります。

デモページ:
http://codepen.io/butchi/pen/XMqevj

サンプルコード

シャッフルにはFisher–Yatesアルゴリズムを用いましたが、
Underscoreの_.shuffleとかで代用できるので読み飛ばせばOK。

const LENGTH = 25; // 25×2の50回ランダムウォークする
const MAGNITUDE = 10; // 振動ピクセル幅

// 入力した正の数を正負両方含む配列(倍のサイズ)として返す
function double(arr) {
  let ret = [];

  arr.forEach((num) => {
    ret.push(num);
    ret.push(-num);
  });

  return ret;
}

// Fisher–Yatesアルゴリズムによる配列シャッフル
function shuffle(arr) {
  let ret = [].concat(arr);

  for(let i = ret.length - 1; i > 0; i--){
    let r = Math.floor(Math.random() * (i + 1));
    let tmp = ret[i];
    ret[i] = ret[r];
    ret[r] = tmp;
  }

  return ret;
}

// ランダムウォークの対象要素
let walkerElm = document.querySelector('.walker')

// ランダムウォークを始めるボタン
let startBtn = document.querySelector('.btn-start');

startBtn.addEventListener('click', () => {
  // ランダム数値配列(mapを有効にするため一旦0で初期化)
  let deltaXArr = (new Array(LENGTH)).fill(0).map(() => {
    return Math.random() * MAGNITUDE;
  });

  let deltaXArr2 = double(deltaXArr);
  let shuffleXArr = shuffle(deltaXArr2);

  // 累積変動量
  let deltaX = 0;

  for(let i = 0; i < LENGTH * 2; i++) {
    setTimeout(() => {
      deltaX += shuffleXArr[i];
      walkerElm.style.transform = `translateX(${deltaX}px)`;
    }, i * 50);
  }
});

今回は簡単のため横軸のみ移動にしましたが、
X軸とY軸だったり絶対値(abs)と偏角(arg)の2つの配列で処理すれば平面上のランダムウォークも実装できます。