как быстро судить два двойных набора пересекаются или нет? - PullRequest
1 голос
/ 28 марта 2020

Я хочу быстро судить, пересекаются ли два двойных набора или нет.

проблема

Элемент в наборе может иметь весь диапазон. Элемент в наборе не упорядочен. Каждый набор имеет более 100 000 элементов.

Если существует двойной a из набора A, двойной b из набора B, a и b очень близки, например abs(a-b)<1e-6, мы говорим, набор A и B пересекаются.

Мой путь

  1. вычисляет диапазон (нижняя граница и верхняя граница) set_A и set_B

    O (n), n - это размер набора

  2. вычисление пересечения диапазона rang_intersect из range_A и range_B

    O (1)

  3. если rang_intersect пустые два набора не пересекаются.

    O (1)

  4. если range_intersect не пустой, найдите sub_set_A из set_A, который в range_intersect, найдите sub_set_B из set_B, который в range_intersect

    O (n)

  5. сортировка sub_set_A и sub_set_B

    O (млогм): m sub_set_A s

  6. transvers sub_set_A_sorted и sub_set_B_sorted по двум указателям. найти, если существует элемент близко, если существует два набора пересекаются, если нет, два набора не пересекаются.

    O (м)

Мой путь может работать, но мне интересно если я смогу сделать быстрее.

Приложение

Почему я хочу это:

На самом деле я столкнулся с проблемой, чтобы судить о столкновении двух точек набора A & B или не. Каждая точка p в наборе точек имеет двойную координату x,y,z. Если точка a существует из набора точек A, точка b из набора точек B, a и координата b очень близки, мы говорим, набор точек A и B столкновение.

В трехмерном случае мы можем определить порядок точек, сначала сравнив x, затем сравнив y, последним z.

Мы можем определить close что, если координата всех измерений близка, две точки закрываются.

Эта проблема может преобразовать в проблему выше.

1 Ответ

2 голосов
/ 29 марта 2020

Некоторая идея, заключив в сетку пространство:

Давайте возьмем точку (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

В примере выше в 2D плане, 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. Мы check.

Основная идея состоит в том, чтобы связать с каждой точкой ее 8 кубов.

Мы можем связать uniq ha sh с каждым кубом: координата верхнего уровня, например, "{x}-{y}-{z}"

Чтобы проверить, пересекается ли A B:

  • , мы строим для каждой точки A ее 8 хешей и сохраняем их в хэш-карте: ha sh -> точка
  • для каждой точки B, мы строим хэши, и если один из них существует в хэш-карте, мы проверяем, находятся ли соответствующие точки в отношении

Теперь рассмотрим

    y
    ^
    |
  3 +---+---+
    | a2|   |
 2.5+-------+
    | a1|   |
  2 +---+---+
    1  1.5  2

a2 и хэши a1 будут перекрываться на квадратах (1,2) и (1,2.5). Таким образом, хэш-карта на самом деле имеет ha sh -> точек. Это означает, что наихудший случай может быть O(n^2), если все точки попадают в одни и те же кубы. Надеемся, что в действительности они не будут?

Ниже кода с нерелевантными данными: (поставить 10 ** 4, чтобы избежать зависания пользовательского интерфейса)

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')
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...