algorithm - 如何快速判断两个双组相交与否?
问题描述
我想快速判断两个双组相交与否。
问题
set 中的元素可以是所有范围。set 中的元素是无序的。每组有 100,000 多个元素。
如果存在一个a
来自 set 的 double ,一个来自 set的A
double ,并且非常接近,例如,我们说 set and intersect 。b
B
a
b
abs(a-b)<1e-6
A
B
我的方式
set_A
计算和的范围(下限和上限)set_B
O(n), n 是集合的大小
计算和
rang_intersect
的范围交集range_A
range_B
O(1)
如果
rang_intersect
空两个集合不相交。O(1)
如果
range_intersect
不为空,sub_set_A
则从set_A
中的哪个中查找,从中的哪个中range_intersect
查找sub_set_B
set_B
range_intersect
上)
排序
sub_set_A
和sub_set_B
O(mlogm) m 是
sub_set_A
的大小tranvers
sub_set_A_sorted
和sub_set_B_sorted
两个指针。查找是否存在元素关闭,如果存在两个集合相交,如果不存在,则两个集合不相交。O(米)
我的方法可行,但我想知道我是否可以做得更快。
附录
为什么我想要这个:
实际上我面临一个判断两点集A
和B
碰撞与否的问题。点集中的每个点p
都有一个双坐标x,y,z
。如果存在a
点集的一个点,点集的A
一个点,和的坐标非常接近,我们说点集和碰撞。b
B
a
b
A
B
在 3d 情况下,我们可以通过 first comparex
然后 compare y
, last compare来定义 point 的顺序z
。
我们可以定义close
如果所有维度的坐标都接近,则两点接近。
这个问题可以转化为上面的问题。
解决方案
通过网格化空间的一些想法:
让我们以(1.2, 2.4, 3.6)
所需的最小距离为例1
。
我们可以说这个点“触及8
”了R^3
[
(1.0, 2.0, 3.5)
(1.0, 2.0, 4.0)
(1.0, 2.5, 3.5) // 1 < 1.2 < 1.5
(1.0, 2.5, 4.0) // 2 < 2.4 < 2.5
(1.5, 2.0, 3.5) // 3.5 < 3.6 < 4
(1.5, 2.0, 4.0)
(1.5, 2.5, 3.5)
(1.5, 2.5, 4.0)
]
如果两点彼此靠近,它们将通过它们的一些立方体连接。
y
^
|
3 +---+---+
| | |
2.5+-------+---+---+
| a | | c | b |
2 +---+---+---+---+--->x
1 1.5 2
在上面的二维平面示例中,a
是(1.2, 2.4)
.
说b
是(2.5, 2.4)
。b
会碰到正方形(2,2)
,但a
不会。所以它们没有连接(实际上可能的最小距离是(2.5-1.5===1
)。
说c
是(2.45, 2.4)
。c
触及广场(1.5, 2)
。也是如此a
。我们检查。
主要思想是将其8
立方体与每个点相关联。
我们可以为每个立方体关联一个 uniq 散列:顶层坐标。例如"{x}-{y}-{z}"
检查是否A
相交B
:
- 我们为它的 8 个哈希中的每个点构建
A
并将它们存储在一个 hashmap 中:hash->point - 对于 的每个点
B
,我们构建哈希,如果其中一个存在于哈希图中,我们检查对应的点是否相关
现在考虑
y
^
|
3 +---+---+
| a2| |
2.5+-------+
| a1| |
2 +---+---+
1 1.5 2
a2
和a1
的哈希将在正方形(1,2)
和上重叠(1,2.5)
。所以hashmap其实就是hash->points。这意味着最坏的情况可能是O(n^2)
所有点都落在同一个立方体中。希望在现实中他们不会?
下面是一个不相关数据的代码:(放 10**4 避免冻结 ui)
function roundEps (c, nth) {
const eps = 10**-nth
const r = (c % eps)
const v = (r >= eps / 2) ? [c-r+eps/2, c-r+eps] : [c-r, c-r+eps/2]
return v.map(x => x.toFixed(nth + 1))
}
function buildHashes (p, nth) {
return p.reduce((hashes, c) => {
const out = []
hashes.forEach(hash => {
const [l, u] = roundEps(c, nth)
out.push(`${hash},${l}`, `${hash},${u}`)
})
return out
},[''])
}
function buildMap (A, nth) {
const hashToPoints = new Map()
A.forEach(p => {
const hashes = buildHashes(p, nth)
hashes.forEach(hash => {
const v = hashToPoints.get(hash) || []
v.push(p)
hashToPoints.set(hash, v)
})
})
return hashToPoints
}
function intersects (m, b, nth, R) {
let processed = new Set()
return buildHashes(b, nth).some(hash => {
if (!m.has(hash)) return
const pts = m.get(hash)
if (processed.has(pts)) return
processed.add(pts)
return pts.some(p => R(p, b))
})
}
function d (a, b) {
return a.reduce((dist, x, i) => {
return Math.max(dist, Math.abs(x-b[i]))
}, 0)
}
function checkIntersection (A, B, nth=2) {
const m = buildMap(A, nth)
return B.some(b => intersects(m, b, nth, (a,b) => d(a, b) < 10**(-nth)))
}
// ephemeral testing :)
/*
function test () {
const assert = require('assert')
function testRound () {
assert.deepEqual(roundEps(127.857, 2), ['127.855', '127.860'])
assert.deepEqual(roundEps(127.853, 2), ['127.850', '127.855'])
assert.deepEqual(roundEps(127.855, 2), ['127.855', '127.860'])
}
function testD () {
assert.strictEqual(d([1,2,3],[5,1,2]), 4)
assert.strictEqual(d([1,2,3],[0,1,2]), 1)
}
function testCheckIntersection () {
{
const A = [[1.213,2.178,1.254],[0.002,1.231,2.695]]
const B = [[1.213,2.178,1.254],[0.002,1.231,2.695]]
assert(checkIntersection(A, B))
}
{
const A = [[1.213,2.178,1.254],[0.002,1.231,2.695]]
const B = [[10,20,30]]
assert(!checkIntersection(A, B))
}
{
const A = [[0,0,0]]
const B = [[0,0,0.06]]
assert(!checkIntersection(A, B, 2))
}
{
const A = [[0,0,0.013]]
const B = [[0,0,0.006]]
assert(checkIntersection(A, B, 2))
}
}
testRound()
testD()
testCheckIntersection()
}*/
const A = []
const B = []
for (let i = 0; i < 10**4; ++i) {
A.push([Math.random(), Math.random(), Math.random()])
B.push([Math.random(), Math.random(), Math.random()])
}
console.time('start')
console.log('intersect? ', checkIntersection(A, B, 6))
console.timeEnd('start')
推荐阅读
- javascript - 悬停时突出显示所有数字
- java - 在 SpringBoot 中调用外部 API 时如何实现分页
- c++ - 更改布尔值或比较布尔值是否提供更好的性能?
- java - 尝试从 Eclipse 运行 Minecraft 时发生非法反射访问操作
- r - 库中的错误(RWordPress):没有名为“RWordPress”的包
- python - 使用 SelectKBest 在 Python 中的特征重要性
- java - 根据其值删除 Firebase 子项
- cordova - 如何在 Capacitor 应用程序中设置 Cordova 插件变量?
- html - laravel 和 jquery 中的自动完成搜索框问题
- python - Python:蛮力数独递归