配列の値を被らないようにランダムに取り出す(ループしても被らない)

被らないように配列から値を取り出す

とある業務の日誌をコンピュータで記録させているのだが、その時に音楽を流してほしいと要望があった。
数曲の中からランダムに選曲と再生するのではあるが、全曲が流れたあとリセットし直すとまれに最後に流れた曲が最初に流れる可能性がある。
ようは「あれ?この曲、さっき流れなかった?」と言われたくないのである。
Fisher–Yates shuffle というアルゴリズムを応用するも、、、。
es6 なんだから yield を使えよ!>俺

<!DOCTYPE html>

<body>

<script>

{//ランダムシャッフル
  const
    random = n => Math.floor (Math.random () * n),
    int = n => Math.floor (n),
    
    fisher_yates =
      function (a) {
        let
          i = a.length,
          r = random;
        
        while (1 < i) {
          let n = r (i--);
          [a[n], a[i]] = [a[i], a[n]];
        }
        return a;        
      };
  
  
  //____________________________
  
  class RandomShaffle {
    
    constructor (ary = [ ], limit = -1) {
      this.list = ary;
      this.index = this.list.length;
      
      let limit_max = int ((ary.length) / 3);
      this.limit = (limit < 0 || limit_max < limit) ? limit_max: limit;

      this.reset ();
    }
    
    

    next () {
      if (! this.index) {
        let
          r = random,
          a = this.list,
          i = a.length, // = 10
          lmt = this.limit, // = 4
          rng = i - lmt,
          mid = rng;

        this.index = i;
      
        while (mid < i--) {
          let n = lmt + r (rng--);
          [a[n], a[i]] = [a[i], a[n]];
        }

        while (1 < i) {
          let n = r (i--);
          [a[n], a[i]] = [a[i], a[n]];
        }
      }
      
      return this.list[--this.index];      
    }
    
    
        
    reset () {
      return fisher_yates (this.list);
    }
  }

  //____________________________

  this.RandomShaffle = RandomShaffle;
}



let ary = [0,1,2,3,4,5,6,7,8,9];
let rnd = new RandomShaffle (ary, 2);
for (let i = 1; i <= ary.length * 10; i++) {
  document.write (rnd.next () + ', ');
  if (! (i % ary.length))
    document.write ('<br>');
}
  
</script>
使い方
let
  list = [], //曲目リスト
  n = 4, //次に同じ曲が現れるまでの最低パス数
  title = new RandomShaffle (list),
  t;
while (t = title.next ())
  musicPlay (t);