Reputation: 1265
I want to fast judge two double set intersect or not.
The element in set can be all range. The element in set are not ordered. Each set have 100,000+ element.
If exist a double a
from set A
, a double b
from set B
, a
and b
is very close,for example abs(a-b)<1e-6
, we say set A
and B
intersect.
calculate the range(lower bound and upper bound) of set_A
and set_B
O(n), n is set's size
calculate range intersection rang_intersect
of range_A
and range_B
O(1)
if rang_intersect
empty two set not intersect.
O(1)
if range_intersect
not empty, find sub_set_A
from set_A
which in the range_intersect
, find sub_set_B
from set_B
which in the range_intersect
O(n)
sort sub_set_A
and sub_set_B
O(mlogm) m is sub_set_A
's size
tranvers sub_set_A_sorted
and sub_set_B_sorted
by two pointer. find if exist element close, if exist two set intersect, if not, two set not intersect.
O(m)
My way can works, but I wonder if I can do faster.
Why I want this:
Actually I am face a problem to judge two point set A
& B
collision or not. Each point p
in point set have a double coordinate x,y,z
. If exist a point a
from point set A
, a point b
from point set B
, a
and b
's coordinate very close, we say point set A
and B
collision.
In 3d case, we can define the order of point by first compare x
then compare y
, last compare z
.
We can define the close
that if all dimension's coordinate is close , the two point close.
This problem can convert to the problem above.
Upvotes: 1
Views: 75
Reputation: 5703
Some idea by gridding the space:
Let's take the point (1.2, 2.4, 3.6)
with minimial distance required 1
.
We may say that this point "touches" 8
unit cubes of 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)
]
If two points are close to each other, their will be connected by some of their cube.
y
^
|
3 +---+---+
| | |
2.5+-------+---+---+
| a | | c | b |
2 +---+---+---+---+--->x
1 1.5 2
In example above in 2D plan, a
is (1.2, 2.4)
.
Say b
is (2.5, 2.4)
. b
will touch the square (2,2)
, but a
does not.
So they are not connected (indeed the min distance possible is (2.5-1.5===1
).
Say c
is (2.45, 2.4)
. c
touches the square (1.5, 2)
. So is a
. We check.
The main idea is to associate to each point its 8
cubes.
We can associate a uniq hash to each cube: the top level coordinate. e.g "{x}-{y}-{z}"
To check if A
intersects B
:
A
its 8 hashes and store them in a hashmap: hash->pointB
, we build the hashes, and if one of those exist in the hashmap we check if the corresponding points are in relationNow consider
y
^
|
3 +---+---+
| a2| |
2.5+-------+
| a1| |
2 +---+---+
1 1.5 2
a2
and a1
's hashes will overlap on squares (1,2)
and (1,2.5)
. So the hashmap is actually hash->points.
This implies that worst case could be O(n^2)
if all the points land into the same cubes. Hopefully in reality they won't?
Below a code with irrelevant data: (put 10**4 to avoid freezing the 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')
Upvotes: 2