Algorithm 布尔网格的随机标记

Algorithm 布尔网格的随机标记,algorithm,random,Algorithm,Random,假设我有一个大小为N的正方形boolean网格(2D数组)。一些值为true,一些值为false(/比率未指定)。我想随机选择一个标记(x,y),这样网格[x][y]就是真的。如果我想要一个省时的解决方案,我会这样做(Python): 但是这是O(N^2),对于tic-tac-toe游戏的实现来说,这已经足够了,但是我猜对于大型N,它会消耗更多的内存 如果我想要不消耗内存的东西,我会: x, y = 0, 0 t = N - 1 while True: x = random.randin

假设我有一个大小为
N
的正方形
boolean
网格
(2D数组)。一些值为
true
,一些值为
false
/
比率未指定)。我想随机选择一个标记
(x,y)
,这样
网格[x][y]
就是
真的
。如果我想要一个省时的解决方案,我会这样做(Python):

但是这是
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
值,那么“猜测”哪个指标是我感兴趣的可能要花很长时间。我应该如何使这个算法最优化?

您可以使用一个实现为对数深度的二叉树的字典。这将占用
O(N^2)
空间,并允许您在
O(log(N^2))=O(logN)
时间中搜索/删除。例如,您可以使用

查找随机值的算法可能是:

t = tree.root
if (t == null)
    throw Exception("No more values");
// logarithmic serach
while t.left != null or t.right != null
     pick a random value k from range(0, 1, 2)
     if (k == 0)
         break;
     if (k == 1)
         if (t.left == null)
             break
         t = t.left
     if (k == 2)
         if (t.right == null)
             break
         t = t.right

result = t.value
// logarithmic delete
tree.delete(t)
return result
当然,您可以将
(i,j)
索引表示为
i*N+j


没有额外的内存,您无法跟踪单元格状态的更改。在我看来,没有比O(N^2)(遍历数组)更好的了。

你可以使用一个实现为对数深度的二叉树的字典。这将占用
O(N^2)
空间,并允许您在
O(log(N^2))=O(logN)
时间中搜索/删除。例如,您可以使用

查找随机值的算法可能是:

t = tree.root
if (t == null)
    throw Exception("No more values");
// logarithmic serach
while t.left != null or t.right != null
     pick a random value k from range(0, 1, 2)
     if (k == 0)
         break;
     if (k == 1)
         if (t.left == null)
             break
         t = t.left
     if (k == 2)
         if (t.right == null)
             break
         t = t.right

result = t.value
// logarithmic delete
tree.delete(t)
return result
当然,您可以将
(i,j)
索引表示为
i*N+j


没有额外的内存,您无法跟踪单元格状态的更改。在我看来,没有比迭代数组更好的了。

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

每行网格
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
总数=8
非零行:[0、2、3、5]
要选择一个随机索引,请选择一个随机值r,最大为真值总数,使用每个非零行的真值数迭代数组,将它们相加,直到知道第r个真值所在的行,然后迭代该行以查找第r个真值的位置

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

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

网格大小预处理查找
10000 x 10000 5000 2.2
1000 x 1000 50 0.22
100 x 100 0.50.022
如您所见,对于大型网格,查找比预处理快2000多倍,因此,如果您需要在同一(或稍微更改)网格上随机选择多个位置,预处理非常有意义

函数随机2D(网格){
this.grid=grid;
this.num=this.grid.map(函数(elem){//每行的真值数
返回元素还原(函数(和,值){
返回和+(val?1:0);
}, 0);
});
this.total=this.num.reduce(函数(sum,val){//真值总数
返回和+val;
}, 0);
this.update=函数(行、列、值){//更改网格中的值
var prev=该网格[行][col];
此.grid[row][col]=val;
如果(上一个^val){
this.num[row]+=val?1:-1;
此值为1.total+=val?1:-1;
}
}
this.select=函数(){//选择随机索引
变量行=0,列=0;
var rnd=Math.floor(Math.random()*this.total)+1;
而(rnd>this.num[row]){//find row
rnd-=this.num[row++];
}
while(rnd){//find列
如果(此网格[行][列])--rnd;
如果(rnd)+列;
}
返回{x:col,y:row};
}
}
var grid=[],size=1000,prob=0.01;//生成测试数据
对于(变量i=0;idocument.write(JSON.stringify(rnd.select());//获取随机索引
如果网格是静态的或变化不大,或者您有时间做一些预处理,那么您可以存储一个数组,其中包含每行的真值数、真值总数和非零行列表(如果网格发生变化,您可以随时更新所有这些内容):

每行网格
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
总数=8
非零行:[0、2、3、5]
要选择一个随机索引,请选择一个随机值r,最大为真值总数,使用每个非零行的真值数迭代数组,将它们相加,直到知道第r个真值所在的行,然后迭代该行以查找第r个真值的位置

(您只需选择一个非空的行f