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;i document.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