【问题标题】:JavaScript: Faster roulette selectionJavaScript:更快的轮盘选择
【发布时间】:2018-08-02 16:20:47
【问题描述】:

我正在实现一种选择算法,该算法根据与其score 值成比例的概率来选择对象。这使得得分较高的对象更有可能被选中。

我的实现如下:

var pool = [];
for (var i = 0; i < 100; i++)
    pool.push({ Id: i, Score: Math.random() * 100 << 0 });

const NUM_RUNS = 100000;
var start = new Date();
for( var i = 0; i < NUM_RUNS; i++ )
  rouletteSelection(pool);
var end = new Date();

var runningTime = (end.getTime() - start.getTime()) / 1000;
var avgExecutionTime = ( runningTime / NUM_RUNS ) * Math.pow(10,9);
console.log('Running Time: ' + runningTime + ' seconds');
console.log('Avg. Execution Time: ' + avgExecutionTime + ' nanoseconds');


function rouletteSelection(pool) {
        // Sum all scores and normalize by shifting range to minimum of 0
        var sumScore = 0, lowestScore = 0;
        pool.forEach((obj) => {
            sumScore += obj.Score;
            if (obj.Score < lowestScore)
                lowestScore = obj.Score;
        })
        sumScore += Math.abs(lowestScore * pool.length);
    
        var rouletteSum = 0;
        var random = Math.random() * sumScore << 0;
        for (var i in pool) {
            rouletteSum += pool[i].Score + lowestScore;
            if (random < rouletteSum)
                return pool[i];
        }
    
        //Failsafe
        console.warn('Could not choose roulette winner, selecting random');
        return pool[Math.random() * pool.length];
};

运行时,性能还不错:在我的机器上,每次调用rouleteSelection() 大约需要 2500-3200 纳秒。但是,在将其用于生产之前,我想对其进行优化并尽可能减少开销,因为在极端情况下,此函数可能会被调用数千万次。

一个明显的优化是以某种方式将所有内容合并到一个循环中,以便对象数组只迭代一次。问题是,为了标准化分数(负分数被移到 0),我需要知道最低分数开始。

有人知道如何解决这个问题吗?

【问题讨论】:

  • 什么是.Fitness,你是说.Score吗?
  • "通过将范围移动到最小值 0 来进行标准化" - 老实说,我只会对负值抛出异常。他们没有意义。并且改变所有值 a) 确实很麻烦 b) 扭曲了其他对象的比例
  • 旁注:您的故障保护不起作用:您需要舍入/截断随机索引。

标签: javascript optimization v8 micro-optimization


【解决方案1】:

冒着说明显而易见的风险:只是不要在每次调用rouletteSelection 时进行标准化。在您构建 pool 之后执行一次。

【讨论】:

  • 对于我的应用程序,负分值是必要的。然而,令我惊讶的是,使用基于范围的 for 循环将执行时间缩短了 5 倍。除此之外,我还巧妙地使用了平均和估计,将执行时间缩短了几纳秒
  • 你的分数只有整数吗?如果是这样,您可以适当地优化为恒定时间算法。即使没有,如果你有一个合理的池大小,在池上累积然后对包含random的区间进行二分搜索可能是有意义的。
  • 我正在测试整数,但在生产中它也需要能够处理浮点数。启动时初始化后,池大小将保持不变。不知道如何进行二进制搜索。轮盘赌选择给池中的每个对象obj.Score/sum_of_scores 的概率。您的意思是创建一个以运行总和为键的 BST?
  • @w0f 是的,就是这样 - 累积分数作为搜索键。使用interpolation search,这应该非常快 - 是否比线性方法更快但简单的方法取决于池大小。
猜你喜欢
  • 2018-07-04
  • 2014-08-27
  • 2012-06-01
  • 2021-06-02
  • 2010-09-22
  • 2020-08-06
  • 2012-05-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多