Головна Алгоритми та AI Квадрантне дерево

🌲 Квадрантне дерево

Квадрантне дерево рекурсивно поділяє площину на чотири нащадки. Дивіться, як воно адаптується до вашого рою точок і прискорює запити діапазону, найближчого сусіда та колізій — обхід суттєво менший за O(n).

Алгоритми та AI3DСередній60 FPS
quadtree ↗ Відкрити окремо
DRAG · SCROLL · CLICK — керуйте прямо у вікні симуляції.

Про просторовий індекс квадрантного дерева

Квадрантне дерево (quadtree) — це деревоподібна структура даних, у якій кожен внутрішній вузол ділить свій 2D-регіон рівно на чотири однакові квадранти (NW, NE, SW, SE), рекурсивно, доки кожна листкова область не міститиме не більше порогової кількості точок (зазвичай однієї). Винайдене Рафаелем Фінкелем і Дж. Л. Бентлі 1974 року та популяризоване в обчислювальній геометрії протягом 1980-х, квадрантні дерева зменшують час просторових запитів з лінійного сканування O(n) до O(log n + k), де k — кількість повернутих результатів. Вони є стандартною структурою прискорення для широкофазного виявлення колізій у 2D-ігрових рушіях, географічних інформаційних системах (ГІС) і стисненні зображень (зображення, закодовані квадрантним деревом, замінюють однорідні області одним вузлом кольору).

Цей симулятор дозволяє розсіяти випадкові точки або клацати, щоб розміщувати їх поодинці, а потім спостерігати, як дерево ділиться в реальному часі, коли перевищується поріг ємності вузла. Можна перетягнути прямокутник запиту діапазону, щоб побачити, які квадранти обходяться, а які відсікаються, порахувати кількість відвіданих вузлів порівняно з наївним лінійним скануванням, і спостерігати, як скупчений чи рівномірний розподіл точок впливає на глибину дерева та ефективність запитів.

Часті запитання

Яка часова складність запиту діапазону в квадрантному дереві?

Для рівномірно випадкового набору з n точок в одиничному квадраті запит діапазону, що повертає k точок, у середньому відвідує O(√n + k) вузлів — значно краще, ніж лінійне сканування O(n). У найгіршому випадку (сильно вироджені розподіли точок або запит, що перекриває багато частково включених вузлів) межа зростає до O(n), але на практиці це рідкість. Доданок O(√n) походить від кількості комірок квадрантного дерева, що перетинають межу запиту, не будучи повністю в ній.

Як квадрантне дерево прискорює виявлення колізій у іграх?

У 2D-фізичному рушії перевірка всіх пар об'єктів на колізію коштує O(n²) — нездійсненно для сотень об'єктів. Широкофазний підхід на основі квадрантного дерева вставляє обмежувальний прямокутник кожного об'єкта в дерево, а потім для кожного об'єкта запитує лише об'єкти в тій самій або сусідніх листкових комірках. Якщо об'єкти розподілені по простору, середня кількість кандидатів на об'єкт падає до O(log n) або менше, зменшуючи загальну вартість широкофазного етапу до O(n log n). Такі рушії, як Box2D, Unity 2D і LibGDX, використовують просторові дерева (квадрантні дерева або AABB-дерева) саме для цього.

Яка різниця між точковим квадрантним деревом і PR (point-region) квадрантним деревом?

Точкове квадрантне дерево ділиться в координатах вставленої точки — чотири нащадки представляють квадранти з центром у цій точці. PR (point-region) квадрантне дерево ділить простір у своїй геометричній середній точці незалежно від розташування точок, даючи фіксовану ієрархічну сітчасту структуру. PR-дерева передбачуваніші за глибиною (завжди ⌈log₂(D/ε)⌉ для роздільної здатності ε в домені D) і легші для реалізації без балансування. Цей симулятор використовує варіант PR, оскільки поділ за фіксованою серединою робить візуальну анімацію чистішою.

Наскільки глибоко може рости квадрантне дерево?

Максимальна глибина PR-квадрантного дерева обмежена роздільною здатністю системи координат. Для 32-бітних чисел із плаваючою комою в одиничному квадраті мінімальна розрізнювана відстань становить близько 10⁻⁷, тож дерево може досягти щонайбільше ~23 рівнів, перш ніж дві «різні» точки опиняться в одному листку. Для цілочисельних координат у сітці 1024×1024 максимальна глибина дорівнює 10 (оскільки 2¹⁰ = 1024). На практиці дуже глибокі дерева формуються лише тоді, коли багато точок скупчуються на крихітній ділянці; середня глибина для рівномірного набору точок — O(log n).

Чи може квадрантне дерево обробляти динамічну вставку та видалення точок?

Так. Вставка проходить від кореня до відповідного листка за O(глибина), розділяючи листок, якщо він перевищує ємність. Видалення прибирає точку, і якщо загальна кількість точок у батьківському вузлі опускається нижче порогу злиття, чотири дочірні вузли згортаються назад у батьківський листок. Обидві операції в середньому займають O(log n) для рівномірно розподілених точок. Часті вставки й видалення в скупченому наборі точок можуть вимагати періодичної перебудови, щоб запобігти сильному дисбалансу.

Що таке октодерево і як воно пов'язане з квадрантним деревом?

Октодерево (octree) — це 3D-узагальнення: кожен вузол ділить свій куб на вісім однакових підкубів (октантів). Октодерева широко використовуються в 3D-ігрових рушіях, прискоренні трасування променів та обробці хмар точок LiDAR. Ті самі алгоритмічні принципи застосовуються — запити діапазону, пошук найближчого сусіда та широкофазне виявлення колізій виграють від просторової ієрархії. На практиці 3D-дерева BVH (Bounding Volume Hierarchy) часто перевершують октодерева для динамічних сцен, оскільки адаптуються до розподілу точок, а не використовують фіксовані серединні поділи.

Як квадрантне дерево використовується у стисненні зображень?

Кодування зображень квадрантним деревом рекурсивно ділить зображення на квадранти. Якщо всі пікселі квадранта потрапляють у межу порогу від одного значення кольору, квадрант зберігається як єдиний листковий вузол із цим кольором — без попіксельного зберігання. Інакше квадрант ділиться знову. Це дає схему стиснення без втрат (при порозі 0) або з втратами (при порозі більше 0). Фрактальне стиснення зображень (яке використовувалося в деяких ранніх іграх на CD-ROM) — споріднена техніка. Сучасні кодеки (HEVC, AV1) використовують подібні до квадрантного дерева ієрархії одиниць кодування (CU) для поділу кадрів на блоки змінного розміру для ентропійного кодування.

Який алгоритм запиту найближчого сусіда в квадрантному дереві?

Пошук найближчого сусіда починається в корені й спускається в дочірній квадрант, що містить точку запиту, підтримуючи «поточного найкращого» кандидата. При поверненні вгору по рекурсії кожен сусідній квадрант перевіряється: якщо мінімально можлива відстань від запиту до квадранта (відстань до найближчого його кута) менша за поточного найкращого, квадрант потрібно обшукати — інакше він відсікається. На практиці це відвідує O(log n) вузлів для рівномірно випадкових даних, хоча в найгіршому випадку (при зловмисному розташуванні точок) — O(n).

Як квадрантне дерево пов'язане з k-d деревом?

K-d дерево (k-вимірне дерево, Бентлі, 1975) — це бінарне дерево просторового поділу, яке циклічно перемикається між координатними осями для своїх поділів, обираючи медіанну точку вздовж поточної осі як значення поділу. Для 2D-даних k-d дерево чергує x-поділи та y-поділи. На відміну від PR-квадрантного дерева, k-d дерево завжди ідеально збалансоване (глибина O(log n) для n точок), але має гіршу продуктивність кешу і складніше оновлюється динамічно. Емпірично k-d дерева перевершують квадрантні дерева для статичних наборів точок і нижчих вимірностей; квадрантні дерева переважні для динамічних 2D-даних і широкофазного виявлення колізій.

Що таке крива Z-порядку і як вона пов'язана з квадрантними деревами?

Крива Z-порядку (Мортона) відображає 2D-координати в 1D-індекс шляхом чергування бітів координат x і y: для x = b₁b₂b₃ і y = c₁c₂c₃ код Мортона — b₁c₁b₂c₂b₃c₃. Ця лінеаризація зберігає просторову локальність: точки, близькі на кривій Z-порядку, просторово близькі в 2D. Код Мортона точки точно відповідає шляху від кореня до листка в PR-квадрантному дереві, закодованому як двійковий рядок. Системи баз даних (наприклад, DynamoDB, Google S2) використовують криві Мортона або Гільберта для індексування просторових даних в одновимірних B-деревах, досягаючи продуктивності запитів на рівні квадрантного дерева зі стандартними структурами індексів.

Чи можуть квадрантні дерева представляти непотокові просторові дані, як-от полігони?

Так — регіональне квадрантне дерево зберігає, які комірки (пікселі) перебувають «усередині» полігона, ділячи їх, доки комірки не стануть повністю всередині, повністю зовні або не досягнуть межі роздільної здатності (тоді вони зберігаються як частково покриті). Векторні квадрантні дерева вставляють відрізки ліній або полігони, перевіряючи кожен рівень поділу на перетин. Наївне зберігання великих полігонів спричиняє дублювання в багатьох вузлах; R-дерева (Гуттман, 1984) зазвичай переважні для індексування прямокутників і полігонів у ГІС, оскільки щільніше обмежують об'єкти й уникають надлишкового багатовузлового зберігання.

Про цю симуляцію

Ця симуляція вирощує point-region (PR) квадрантне дерево наживо, коли ви розсіюєте, перетягуєте або малюєте точки на 2D-полотні. Щоразу, коли листкова комірка утримує більше точок, ніж поточна ємність, вона ділиться на чотири рівні квадранти — NW, NE, SW і SE — навколо власної середини, рекурсуючи, доки кожна комірка не опиниться під ємністю або не досягне фіксованого обмеження глибини. Перемикання режиму дозволяє перетягнути прямокутник запиту діапазону, шукати найближчого сусіда або запустити рух усіх точок, щоб спостерігати, як те саме дерево керує широкофазним виявленням колізій, з живими лічильниками, що порівнюють відвідані вузли з наївним лінійним скануванням.

🔬 Що це показує

Лінії поділу малюються під час розщеплення комірок, точки всередині активного прямокутника запиту діапазону стають блакитними, а поточний збіг найближчого сусіда підсвічується жовтим із колом радіуса пошуку. У режимі Рух/колізії сусідство кожної точки перевіряється лише щодо близьких комірок, а не всіх інших точок, і статистика порівнює кандидатні пари, знайдені деревом, із кількістю пар, які знадобилися б наївній перевірці всіх пар.

🎮 Як користуватися

Повзунок Ємність на листок (1–16, за замовчуванням 4) задає, скільки точок утримує комірка перед поділом; Швидкість анімації (0,1×–3×) масштабує рух у режимі Рух/колізії. Шість перемикачів режиму перемикають між додаванням/перетягуванням, малюванням рою, стиранням, запитом діапазону, найближчим сусідом і рухом/колізіями; Розсіяти 200, Очистити та Скинути заповнюють або спорожняють полотно, а прапорці Показати поділ / Показати точки / Показати вікно запиту перемикають, що малюється.

💡 А чи знали ви?

PR-квадрантні дерева були представлені Рафаелем Фінкелем і Дж. Л. Бентлі 1974 року. Оскільки цей варіант завжди ділиться в геометричній середині комірки, а не в координатах точки, його максимальна глибина фіксується лише роздільною здатністю — тут обмежена 10 рівнями в коді, тож жодна комірка не може ділитися вічно, навіть якщо багато точок скупчуються на крихітній ділянці.

Поширені запитання

Як це квадрантне дерево вирішує, коли ділити комірку?

Кожна комірка починається як єдиний листок, що утримує всі свої точки. Щойно кількість точок листка перевищує значення повзунка Ємність на листок, він ділиться на чотири рівні дочірні квадранти у власній середині, перерозподіляє свої точки в той дочірній квадрант, до якого вони тепер належать, і стає внутрішнім вузлом. Поділ припиняється, коли глибина комірки перевищує 10, навіть якщо вона все ще утримує більше точок, ніж її ємність.

Що змінює підвищення чи зниження повзунка ємності на листок?

Низька ємність змушує комірки ділитися набагато швидше, тож дерево росте глибшим із більшою кількістю листків і внутрішніх вузлів для того самого набору точок — це видно в статистиці Листки, Внутрішні вузли та Макс. глибина. Висока ємність дозволяє кожному листку утримувати більше точок перед поділом, створюючи мілкіше дерево, яке відвідує менше вузлів на запит, але сканує більше точок усередині кожного листка.

Як режим запиту діапазону порівнюється з лінійним скануванням?

Перетягування прямокутника в режимі Запит діапазону обходить дерево від кореня, спускаючись лише в ті дочірні квадранти, що перетинають прямокутник, і повністю пропускаючи решту. Статистика Відвідано вузлів точно підраховує, скільки комірок було перевірено таким чином, тоді як Лінійне сканування завжди показує загальну кількість точок — різниця між цими двома числами є заощадженням, яке дерево дає порівняно з перевіркою кожної точки окремо.

Як пошук найближчого сусіда уникає перевірки кожної точки?

Вибір цілі в режимі Найближчий сусід спочатку шукає в дочірньому квадранті, що містить точку, щоб отримати початкову поточну найкращу відстань, а потім перевіряє сусідні квадранти лише тоді, коли найближчий можливий кут ближчий за цю найкращу відстань — усе інше відсікається. Жовте коло, намальоване навколо точки запиту, позначає поточну найкращу відстань, а Відвідано вузлів повідомляє, скільки комірок було фактично оглянуто.

Що відбувається в режимі Рух/колізії?

Кожній точці надається швидкість, і вона відскакує від країв полотна, а на кожному кадрі квадрантне дерево перебудовується й використовується для запуску невеликого запиту діапазону навколо кожної точки, щоб знайти близьких кандидатів на колізію, замість порівняння з кожною іншою точкою. Статистика Результат запиту та Відвідано вузлів показує унікальні кандидатні пари, знайдені деревом, тоді як Лінійне сканування показує загальну кількість пар, яку вимагала б наївна перевірка всіх пар.

Схожі симуляції