Яка різниця між зворотним відстеженням та глибиною першого пошуку?


105

Яка різниця між зворотним відстеженням та глибиною першого пошуку?

Відповіді:


98

Зворотний трек - алгоритм більш загального призначення.

Поглиблений пошук - це специфічна форма зворотного відстеження, пов'язана з пошуком деревних структур. З Вікіпедії:

Починається з кореня (вибираючи який-небудь вузол як корінь у випадку графіка) та досліджує, наскільки це можливо, уздовж кожної гілки перед зворотним відстеженням.

Він використовує зворотний трек як частину своїх засобів роботи з деревом, але обмежується структурою дерева.

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


13
Відповідаючи на давній пост тут. Хороша відповідь, але ... чи не могла проблема шахової дошки бути представлена ​​як дерево? :-) З будь-якої позиції на шаховій дошці для даного твору, чи не існує дерево можливих рухів, що поширюються в майбутнє? Частина мене відчуває, що будь-який випадок, коли можна використовувати зворотний трек, можна також моделювати як дерево, але я не впевнений, чи я правильний у цій інтуїції.
The111

4
@ The111: Дійсно, будь-яка проблема пошуку може бути представлена ​​у вигляді дерева - у вас є вузол для кожного можливого часткового рішення та край від кожного вузла до одного або декількох можливих альтернативних варіантів, які можна зробити в цьому стані. Я думаю, що відповідь lcn про те, що зворотний трек зазвичай означає, що DFS на (зазвичай неявному) дереві пошуку, сформованому під час рекурсії, наближається до правди.
j_random_hacker

5
@j_random_hacker Отже, DFS - це спосіб дослідити дерево (або загалом графік), тоді як зворотний трекінг - це спосіб вирішити проблему (в якій використовується DFS разом із обрізкою). :-)
The111

Блукаюче Вікіпедія описує зворотний трек як алгоритм пошуку перших глибин , очевидно, що поєднує ці два поняття.
Андерсон Грін

29

Я думаю, що ця відповідь на інше пов'язане питання пропонує більше розуміння.

Для мене різниця між зворотним відстеженням і DFS полягає в тому, що зворотний трекінг обробляє неявне дерево, а DFS має справу з явним. Це здається банальним, але це означає багато. Коли простір пошуку проблеми відвідано за допомогою зворотного відстеження, неявне дерево перетинається і обрізається посередині нього. Однак для DFS дерево / графік, яким він займається, явно побудовано, і неприйнятні випадки вже були викинуті, тобто обрізані, перед будь-яким пошуком.

Отже, зворотний трекінг - це DFS для неявного дерева, тоді як DFS здійснює зворотний трек без обрізки.


Я думаю, що заплутати думати про зворотний трек як про неявне дерево. Коли я вперше прочитав це, я погодився, але копаючи глибше, я зрозумів, що "неявне дерево" - це справді .. дерево рекурсії. Візьмемо будь-який приклад, який використовує зворотний трекінг, як, наприклад, перестановка рядків символів, немає логічного дерева (немає неявного дерева) що стосується проблеми, але у нас є дерево рекурсії, яке моделює процес нарощування поступових рядків. Що стосується обрізки, то це обрізка робиться на дереві рекурсії, де виконується загальна груба сила ... (буде продовжено)
Gang Fang

(продовження) Наприклад, надрукуйте всю перестановку рядка "відповідь", і скажімо, що 3-й знак має бути символом "а". Перший 2 рівень дерева рекурсії підпорядковується O (n!), Але на 3-му рівні всі гілки, окрім тих, що додають "a", обрізають (відхиляють).
Gang Fang

6

Зворотний трек зазвичай реалізується як DFS плюс обрізка пошуку. Ви обходите глибину пошукового дерева в глибині спочатку, будуючи часткові рішення на цьому шляху. Брутська сила DFS може побудувати всі результати пошуку, навіть ті, які практично не мають сенсу. Це також може бути дуже неефективним для побудови всіх рішень (n! Або 2 ^ n). Тож насправді, як ви робите DFS, вам також потрібно обрізати часткові рішення, які не мають сенсу в контексті реальної задачі, і зосередитись на часткових рішеннях, які можуть призвести до правильних оптимальних рішень. Це власне техніка зворотного відстеження - ви відкидаєте часткові рішення якомога раніше, робите крок назад і знову намагаєтеся знайти локальний оптимум.

Ніщо не зупиняється, щоб перейти до дерева простору пошуку за допомогою BFS та виконувати стратегію зворотного відстеження на цьому шляху, але це не має сенсу на практиці, оскільки вам потрібно буде зберігати рівень пошуку за шаром у черзі, а ширина дерева зростає експоненціально до висоти, тож ми б дуже швидко витратили багато місця. Ось чому дерева зазвичай обробляють за допомогою DFS. У цьому випадку стан пошуку зберігається у стеку (стек викликів або явна структура) і не може перевищувати висоту дерева.


5

Зазвичай пошук по глибині - це спосіб ітерації через фактичну структуру графіка / дерева, яка шукає значення, тоді як зворотний трек - це ітерація через проблемний простір, який шукає рішення. Зворотний трек - більш загальний алгоритм, який не обов'язково стосується навіть дерев.


5

Я б сказав, DFS - це особлива форма зворотного відстеження; зворотний трек - загальна форма DFS.

Якщо ми поширимо DFS на загальні проблеми, ми можемо назвати це зворотним відстеженням. Якщо ми використовуємо зворотний трек для проблем, пов’язаних з деревом / графіком, ми можемо назвати його DFS.

Вони несуть ту саму ідею в алгоритмічному аспекті.


Співвідношення між DFS та Backtracking насправді є просто зворотним. Перевірте мою відповідь, де це детально.
KGhatak

5

За словами Дональда Кнута, це те саме. Ось посилання на його статті про алгоритм Dancing Links, який використовується для вирішення таких «недеревних» проблем, як N-queens і Sudoku solver.

Зворотний трек, який також називають першим глибинним пошуком


Згадано на сторінці 1 пов'язаного PDF.
Стів Чавес

5

ІМХО, більшість відповідей є в основному неточними та / або без будь-яких посилань на підтвердження. Тож дозвольте мені поділитися дуже чітким поясненням із посиланням .

По-перше, DFS - це загальний алгоритм обходу графіків (і пошуку). Тож його можна застосувати до будь-якого графіка (або навіть лісу). Дерево - це особливий вид Графіка, тому DFS працює і для дерева. По суті, давайте перестанемо говорити, що це працює лише для дерева, або подобається.

На основі [1], Backtracking - це особливий вид DFS, який використовується головним чином для економії місця (пам'яті). Відмінність, яку я збираюся зазначити, може здатися заплутаною, оскільки в таких алгоритмах Graph ми звикли мати представлення списку суміжності та використовувати ітеративний шаблон для відвідування всіх безпосередніх сусідів ( для дерева це безпосередні діти ) вузла , ми часто ігноруємо, що неправильна реалізація get_all_immediate_neighbors може спричинити різницю у використанні пам'яті базового алгоритму.

Крім того, якщо вузол графіка має коефіцієнт розгалуження b і діаметр h ( для дерева це висота дерева ), якщо ми зберігаємо всіх безпосередніх сусідів на кожному кроці відвідування вузла, вимоги до пам'яті будуть великими-O (bh) . Однак якщо взяти за один раз лише одного (негайного) сусіда і розширити його, тоді складність пам'яті зменшується до великої-O (h) . Хоча перший вид реалізації називається DFS , другий вид називається Backtracking .

Тепер ви бачите, якщо ви працюєте з мовами високого рівня, швидше за все, ви фактично використовуєте Backtracking під виглядом DFS. Більше того, відстеження відвіданих вузлів для дуже великої задачі може бути справді великою пам'яттю; закликаючи до ретельного проектування get_all_immediate_neighbors (або алгоритмів, які можуть обробляти перегляд вузла, не потрапляючи в нескінченний цикл).

[1] Стюарт Рассел та Пітер Норвіг, Штучний інтелект: сучасний підхід, 3-е видання


2

Перша глибина - це алгоритм переходу або пошуку дерева. Дивіться тут . Зворотний трек - це набагато більш широкий термін, який застосовується незалежно від того, де формується кандидат, який вирішується, а пізніше відкидається шляхом зворотного відстеження до колишнього стану. Дивіться тут . Перший глибинний пошук використовує зворотний трек для пошуку першої гілки (кандидат рішення), а якщо не вдалий - пошук іншої гілки.


2

DFS описує спосіб, яким ви хочете вивчити або пройти графік. Основна увага приділяється концепції проходження якомога глибшого даного вибору.

Хоча зворотний трек, як правило, реалізується за допомогою DFS, акцентує більше уваги на концепції обрізання неперспективних пошукових підпросторів якомога раніше.


1

Під час першого глибинного пошуку ви починаєте з кореня дерева, а потім досліджуєте якнайдалі по кожній гілці, а потім повертаєтеся до кожного наступного батьківського вузла і обміняєте його дітьми

Зворотний трек - це узагальнений термін для початку в кінці мети і поступово рухаючись назад, поступово будуючи рішення.


4
Зворотний трек не означає починати з кінця і рухатися назад. Він зберігає журнал відвіданих вузлів для зворотного відстеження, якщо виявлено безвихідь.
Günther Jena

1
"Починаючи з кінця ...", так !!
7kemZmani

1

IMO, на будь-якому конкретному вузлі зворотного відстеження ви намагаєтеся спочатку глибоко розгалужитись на кожного з його дітей, але перед тим, як ви розгалужите будь-який з дочірніх вузлів, вам потрібно "витерти" попередній стан дитини (цей крок еквівалентний назад ходити до батьківського вузла). Іншими словами, кожний стан побратимів не повинен впливати один на одного.

Навпаки, під час звичайного алгоритму DFS у вас зазвичай немає такого обмеження, вам не потрібно видаляти (зворотній трек) попередній стан побратимів, щоб побудувати наступний вузол побратимів.


1

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

Тепер зворотний трек і DFS - це 2 різних імені, що даються одній ідеї, застосованій для двох різних абстрактних типів даних.

Якщо ідея застосовується в матричній структурі даних, ми називаємо її зворотним відстеженням.

Якщо таку саму ідею застосувати на дереві чи графіку, ми називаємо її DFS.

Тут є те, що матриця може бути перетворена на графік, а графік може бути перетворений в матрицю. Отже, ми реально застосовуємо ідею. Якщо на графіку, то ми називаємо його DFS, а якщо на матриці, то називаємо зворотним відстеженням.

Ідея в обох алгоритму однакова.


0

Зворотний трек - це лише перший глибинний пошук з конкретними умовами припинення.

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

Тож коли я думаю про те, що мені цікаво, я хвилююся

  1. Держава
  2. Рішення
  3. Основні випадки (умови припинення)

Я пояснюю це у своєму відео про зворотній трек тут .

Нижче наведено аналіз коду зворотного відстеження. У цьому коді зворотного відстеження я хочу, щоб усі комбінації призвели до певної суми або цілі. Тому у мене є 3 рішення, які здійснюють дзвінки в мій стек дзвінків, при кожному рішенні я можу вибрати номер як частину мого шляху, щоб досягти цільового номера, пропустити цей номер або вибрати його та знову вибрати його. І тоді, якщо я досягну умови припинення, мій крок назад - просто повернутися . Повернення є етапом зворотного відстеження, оскільки він виходить з цього виклику в стеку викликів.

class Solution:    

"""

Approach: Backtracking 

State
    -candidates 
    -index 
    -target 

Decisions
    -pick one --> call func changing state: index + 1, target - candidates[index], path + [candidates[index]]
    -pick one again --> call func changing state: index, target - candidates[index], path + [candidates[index]]
    -skip one --> call func changing state: index + 1, target, path

Base Cases (Termination Conditions)
    -if target == 0 and path not in ret
        append path to ret
    -if target < 0: 
        return # backtrack 

"""

def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
    """
    @desc find all unique combos summing to target
    @args
        @arg1 candidates, list of ints
        @arg2 target, an int
    @ret ret, list of lists 
    """
    if not candidates or min(candidates) > target: return []

    ret = []
    self.dfs(candidates, 0, target, [], ret)
    return ret 

def dfs(self, nums, index, target, path, ret):
    if target == 0 and path not in ret: 
        ret.append(path)
        return #backtracking 
    elif target < 0 or index >= len(nums): 
        return #backtracking 


    # for i in range(index, len(nums)): 
    #     self.dfs(nums, i, target-nums[i], path+[nums[i]], ret)

    pick_one = self.dfs(nums, index + 1, target - nums[index], path + [nums[index]], ret)
    pick_one_again = self.dfs(nums, index, target - nums[index], path + [nums[index]], ret)
    skip_one = self.dfs(nums, index + 1, target, path, ret)
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.