algorithm - bool 网格的随机索引

标签 algorithm random

假设我有一个正方形 boolean grid (二维数组)大小为 N .一些值是 true有些是false (<true values> / <false values> 比率未指定)。我想随机选择一个指数 (x, y)这样grid[x][y]true .如果我想要一个省时的解决方案,我会做这样的事情(Python):

x, y = random.choice([(x, y) for x in range(N) for y in range(N) if grid[x][y]])

但这是 O(N^2) ,这对于 tic-tac-toe 游戏的实现来说已经绰绰有余了,但我猜想对于大 N 来说它会消耗更多的内存。 .

如果我想要一些不占用内存的东西,我会这样做:

x, y = 0, 0
t = N - 1
while True:
    x = random.randint(0, t)
    y = random.randint(0, t)
    if grid[x][y]:
        break

但问题是,如果我有一个大小为 10^4 的网格而且只有一两个true中的值,可能需要永远“猜测”哪个指数是我感兴趣的指数。我应该如何使这个算法最优?

最佳答案

如果网格是静态的或变化不大,或者您有时间进行一些预处理,您可以存储一个数组,其中包含每行真值的数量、真值的总数以及一个列表非零行(如果网格发生变化,您可以随时更新所有这些行):

grid        per row

0 1 0 0 1 0    2
0 0 0 0 0 0    0
0 0 1 0 0 0    1
0 0 0 0 1 0    1
0 0 0 0 0 0    0
1 0 1 1 1 0    4
       total = 8

non-zero rows: [0, 2, 3, 5]

要选择一个随机索引,选择一个随机值 r 直到真值的总数,用每个非零行的真值数遍历数组,将它们相加直到你知道 r-第 r 个真值在中,然后遍历该行以找到第 r 个真值的位置。

(您可以简单地先选择一个非空行,然后从该行中选择一个真值,但这会产生不均匀的概率。)

对于 N×N 大小的网格,预处理将花费 N×N 时间和 2×N 空间,但最坏情况下的查找时间为 N。在实践中,使用下面的 JavaScript 代码示例,预处理和查找时间(以毫秒为单位)的顺序为:

  grid size      pre-processing    look-up  
10000 x 10000        5000            2.2  
 1000 x  1000          50            0.22  
  100 x   100           0.5          0.022  

如您所见,对于大型网格,查找比预处理快 2000 多倍,因此如果您需要在同一(或略微改变的)网格上随机选择多个位置,预处理会很有道理。

function random2D(grid) {
    this.grid = grid;
    this.num = this.grid.map(function(elem) {         // number of true values per row
        return elem.reduce(function(sum, val) {
            return sum + (val ? 1 : 0);
        }, 0);
    });
    this.total = this.num.reduce(function(sum, val) { // total number of true values
        return sum + val;
    }, 0);

    this.update = function(row, col, val) {           // change value in grid
        var prev = this.grid[row][col];
        this.grid[row][col] = val;
        if (prev ^ val) {
            this.num[row] += val ? 1 : -1;
            this.total += val ? 1 : -1;
        }
    }

    this.select = function() {                        // select random index
        var row = 0, col = 0;
        var rnd = Math.floor(Math.random() * this.total) + 1;
        while (rnd > this.num[row]) {                 // find row
            rnd -= this.num[row++];
        }
        while (rnd) {                                 // find column
            if (this.grid[row][col]) --rnd;
            if (rnd) ++col;
        }
        return {x: col, y: row};
    }
}

var grid = [], size = 1000, prob = 0.01;              // generate test data
for (var i = 0; i < size; i++) {
    grid[i] = [];
    for (var j = 0; j < size; j++) {
        grid[i][j] = Math.random() < prob;
    }
}
var rnd = new random2D(grid);                         // pre-process grid
document.write(JSON.stringify(rnd.select()));         // get random index

保留包含至少一个真值的行的列表只对非常稀疏填充的网格有意义,其中许多行不包含真值,所以我没有在代码示例中实现它。如果您实现它,非常稀疏数组的查找时间将减少到不到 1µs。

关于algorithm - bool 网格的随机索引,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39178919/

相关文章:

algorithm - 回答大网格矩形查询中的元素总和

C++ : Find the closest values in an array

random - 在GPU上高效获取范围内的随机数

python - 在Python中输入参数

c - 如何设计金盒游戏的策略和算法

algorithm - 网格划分的快速算法是什么?

java - 如何在 Java 中为 2D 游戏构建 Tiled map ?

random - Mlib RandomForest (Spark 2.0) 预测单个向量

java - 在Java中随机化一个字符串

c++ - MPI 中每个进程的随机数