题目:
给定一个由非重叠的轴对齐矩形的数组 rects ,其中 rects[i] = [ai, bi, xi, yi] 表示 (ai, bi) 是第 i 个矩形的左下角点,(xi, yi) 是第 i 个矩形的右上角点。设计一个算法来随机挑选一个被某一矩形覆盖的整数点。矩形周长上的点也算做是被矩形覆盖。所有满足要求的点必须等概率被返回。
在给定的矩形覆盖的空间内的任何整数点都有可能被返回。
请注意 ,整数点是具有整数坐标的点。
实现 Solution 类:
Solution(int[][] rects)用给定的矩形数组rects初始化对象。int[] pick()返回一个随机的整数点[u, v]在给定的矩形所覆盖的空间内。
算法:
方法一:水塘抽样
分两步走:
- 按矩形面积占总面积的比例选择一个矩形。
遍历矩形,设cur为当前矩形的点数(注意2*2的矩形有9个点),sum为已遍历的所有矩形的面积,rand(sum) < cur则意味着选择当前矩形,否则维持选择的矩形不变。 - 从矩形中随机选择一个点返回
随机的点为[a + rand(x - a), b + rand(y - b)]注意点的总数为边长+1之积。 时间复杂度O(n),空间复杂度O(1)
import "math/rand"
type Solution struct {
Rects [][]int
}
func Constructor(rects [][]int) Solution {
return Solution{Rects:rects}
}
func (this *Solution) Pick() []int {
cur := 0
sum := 0
index := 0
for i, rec := range this.Rects {
cur = (rec[2] - rec[0] + 1)*(rec[3] - rec[1] + 1)
sum = sum + cur
if rand.Intn(sum) < cur {
index = i
}
}
// rand.Intn(n)的取值范围为[0,n),所以最终结果为[a + rand(x - a + 1), b + rand(y - b + 1)]
return []int{this.Rects[index][0] + rand.Intn(this.Rects[index][2] - this.Rects[index][0] + 1), this.Rects[index][1] + rand.Intn(this.Rects[index][3] - this.Rects[index][1] + 1)}
}
方法二:前缀和+二分查找
方法一每次pick都需要遍历所有矩形,我们不如在初始化时就将所有矩形的点数(权重)计算好,然后根据前置前缀和取一个随机数,判断随机数位于哪个矩阵,在从矩阵中随机去一个点即可。
点数前缀和:
[0, 9, 21, 58],如果点数[0-9]则选择第一个矩阵
import "math/rand"
type Solution struct {
Rects [][]int
PointSum []int
}
func Constructor(rects [][]int) Solution {
s := Solution{
Rects: rects,
PointSum: make([]int, len(rects) + 1),
}
for i, rec := range rects {
s.PointSum[i + 1] = s.PointSum[i] + (rec[2] - rec[0] + 1)*(rec[3] - rec[1] + 1)
}
return s
}
func (this *Solution) Pick() []int {
// 注意rand.Ints的范围
num := rand.Intn(this.PointSum[len(this.PointSum) - 1] + 1)
mid := 0
// 找到num所在PointSum的下界
left, right := 0, len(this.Rects) - 1
for left < right {
mid = (left + right + 1) >> 1
if this.PointSum[mid] < num {
left = mid
} else {
right = mid - 1
}
}
index := left
return []int{this.Rects[index][0] + rand.Intn(this.Rects[index][2] - this.Rects[index][0] + 1), this.Rects[index][1] + rand.Intn(this.Rects[index][3] - this.Rects[index][1] + 1)}
}