Geriye dönük izleme ile derinlemesine ilk arama arasındaki fark nedir?


111

Geriye dönük izleme ile derinlemesine ilk arama arasındaki fark nedir?

Yanıtlar:


103

Geri izleme daha genel amaçlı bir algoritmadır.

Önce Derinlik arama , ağaç yapılarını aramayla ilgili özel bir geri izleme biçimidir. Wikipedia'dan:

Bir tanesi kökten başlar (grafik durumunda kök olarak bir düğüm seçer) ve geriye doğru izlemeye başlamadan önce her dal boyunca mümkün olduğunca uzağa araştırma yapar.

Bir ağaçla çalışmanın bir parçası olarak geri izleme kullanır, ancak bir ağaç yapısıyla sınırlıdır.

Geriye dönük izleme, mantıksal bir ağaç olsun ya da olmasın, etki alanının bölümlerinin elimine edilebildiği her tür yapıda kullanılabilir. Wiki örneği bir satranç tahtası ve belirli bir problem kullanır - belirli bir harekete bakabilir ve onu ortadan kaldırabilir, ardından bir sonraki olası hamleye geri dönebilir, onu ortadan kaldırabilirsiniz, vb.


15
Burada eski bir gönderiye yanıt vermek. Güzel cevap, ama ... satranç tahtası problemi aynı zamanda bir ağaç olarak temsil edilemez mi? :-) Belirli bir taş için satranç tahtasındaki herhangi bir konumdan, geleceğe doğru uzanan olası hareketler ağacı yok mu? Bir parçam, geri izlemenin kullanılabileceği herhangi bir durum gibi hissediyor, bir ağaç olarak da modellenebilir, ancak bu sezgide doğru olup olmadığımdan emin değilim.
The111

5
@ The111: Aslında, herhangi bir arama problemi bir ağaç olarak temsil edilebilir - her olası kısmi çözüm için bir düğümünüz ve bu durumda yapılabilecek bir veya daha fazla olası alternatif seçime her düğümden bir kenarınız var. Sanırım lcn'nin cevabı, geri izleme genellikle özyineleme sırasında oluşturulan (genellikle örtük) arama ağacındaki DFS anlamına gelir, gerçeğe en yakın olanıdır.
j_random_hacker

5
@j_random_hacker Yani, DFS bir ağacı keşfetmenin bir yoludur (veya daha genel olarak grafiği), geri izleme ise bir sorunu çözmenin bir yoludur (budama ile birlikte DFS kullanır). :-)
The111

Vikipedi kafa karıştırıcı bir şekilde, geriye doğru izlemeyi , görünüşe göre bu iki kavramı birbirine karıştıran derinlikli arama algoritması olarak tanımlıyor .
Anderson Green

30

Bir başka ilgili sorunun cevabının daha fazla içgörü sunduğunu düşünüyorum .

Bana göre, geriye dönük izleme ile DFS arasındaki fark, geri izlemenin örtük bir ağacı ele alması ve DFS'nin açık bir ağaçla ilgilenmesidir. Bu önemsiz görünüyor ama çok şey ifade ediyor. Geriye dönük izleme ile bir problemin arama alanı ziyaret edildiğinde, örtük ağaç içinden geçilir ve ortasında budanır. Yine de DFS için, ilgilendiği ağaç / grafik açıkça oluşturulmuştur ve kabul edilemez durumlar, herhangi bir arama yapılmadan önce zaten atılmış, yani budanmış, ortadan kaldırılmıştır.

Bu nedenle, geri izleme örtük ağaç için DFS'dir, DFS ise budama olmadan geriye doğru izlenir.


Geriye dönük izlemeyi örtük bir ağaçla uğraşmak olarak düşünmenin kafa karıştırıcı olduğunu düşünüyorum. Bunu ilk okuduğumda kabul ettim ama daha derine inerken "örtük ağacın" gerçekten olduğunu fark ettim .. özyineleme ağacı .. Geriye doğru izlemeyi kullanan herhangi bir örneği ele alalım, bir karakter dizisine izin vermek gibi, mantıksal ağaç yoktur (örtük ağaç yok) Sorunla ilgili her ne olursa olsun, ancak artımlı dizi oluşturma sürecini modelleyen bir özyineleme ağacımız var. Toplam kaba kuvvet yapılır tekrarlama ağacına ... yapılan budama olduğunu, budama gelince (devam edecek)
Çete Fang'ı

(devamı) Örneğin, "yanıt" dizesinin tüm permütasyonunu yazdırın ve 3. karakterin "a" karakteri olması gerektiğini varsayalım. Özyineleme ağacının ilk 2 seviyesi O (n!) 'Ya uyar ama 3. seviyede "a" ekleyenler dışındaki tüm dallar budanır (geriye doğru izlenir).
Gang Fang

8

Geriye dönük izleme genellikle DFS artı arama ayıklama olarak uygulanır. Önce yol boyunca kısmi çözümler inşa ederek arama alanı ağaç derinliğini aşarsınız. Brute-force DFS, pratik olarak anlamlı olmayanlar da dahil olmak üzere tüm arama sonuçlarını oluşturabilir. Bu aynı zamanda tüm çözümleri (n! Veya 2 ^ n) oluşturmak için çok verimsiz olabilir. Bu nedenle, gerçekte DFS'yi yaptığınız gibi, gerçek görev bağlamında anlam ifade etmeyen kısmi çözümleri de ayarlamanız ve geçerli optimum çözümlere yol açabilecek kısmi çözümlere odaklanmanız gerekir. Bu gerçek geri izleme tekniğidir - kısmi çözümleri olabildiğince erken atarsınız, geri adım atarsınız ve tekrar yerel optimum bulmaya çalışırsınız.

BFS kullanarak arama alanı ağacında gezinmek ve yol boyunca geri izleme stratejisi yürütmek için hiçbir şey durmuyor, ancak pratikte mantıklı değil çünkü arama durumunu katman katman kuyrukta saklamanız gerekecek ve ağaç genişliği katlanarak yüksekliğe büyüyor, böylece çok hızlı bir şekilde çok fazla alanı boşa harcardık. Bu nedenle ağaçlar genellikle DFS kullanılarak geçilir. Bu durumda arama durumu yığında saklanır (çağrı yığını veya açık yapı) ve ağaç yüksekliğini aşamaz.


7

IMHO, cevapların çoğu ya büyük ölçüde belirsizdir ve / veya doğrulanacak herhangi bir referans yoktur. Öyleyse çok net bir açıklamayı bir referansla paylaşmama izin verin .

İlk olarak, DFS genel bir grafik geçiş (ve arama) algoritmasıdır. Böylece herhangi bir grafiğe (hatta ormana) uygulanabilir. Ağaç özel bir Grafik türüdür, bu nedenle DFS, ağaç için de çalışır. Esasen, bunun sadece bir ağaç veya benzerleri için işe yaradığını söylemeyi bırakalım.

[1] 'e göre, Backtracking, esas olarak alan (bellek) tasarrufu için kullanılan özel bir DFS türüdür. Bahsetmek üzere olduğum ayrım kafa karıştırıcı görünebilir, çünkü bu türden Grafik algoritmalarında bitişik liste temsillerine çok alıştık ve bir düğümün tüm yakın komşularını ziyaret etmek için yinelemeli model ( ağaç için bu hemen çocuklarıdır ) , get_all_immediate_neighbors'ın kötü bir şekilde uygulanmasının , temel alınan algoritmanın bellek kullanımlarında bir farka neden olabileceğini genellikle göz ardı ederiz .

Ayrıca, bir grafik düğümünde dallanma faktörü b ve çap h varsa ( bir ağaç için bu ağaç yüksekliğidir ), bir düğümü ziyaret etmenin her adımında tüm yakın komşuları saklarsak, bellek gereksinimleri büyük-O (bh) olacaktır . Bununla birlikte, bir seferde yalnızca tek bir (hemen) komşu alır ve onu genişletirsek, bellek karmaşıklığı büyük-O (h) 'ye düşer . İlk uygulama türü DFS olarak adlandırılırken , ikinci tür Geriye İzleme olarak adlandırılır .

Şimdi görüyorsunuz, yüksek seviyeli dillerle çalışıyorsanız, büyük olasılıkla aslında DFS kisvesi altında Backtracking kullanıyorsunuz. Dahası, çok büyük bir problem seti için ziyaret edilen düğümleri takip etmek gerçekten bellek yoğun olabilir; get_all_immediate_neighbors (veya sonsuz döngüye girmeden bir düğümü yeniden ziyaret etmeyi başarabilen algoritmalar) için dikkatli bir tasarım çağrısı .

[1] Stuart Russell ve Peter Norvig, Yapay Zeka: Modern Bir Yaklaşım, 3. Baskı


6

Genellikle, önce derinlik arama, bir değer arayan gerçek bir grafik / ağaç yapısını yinelemenin bir yoludur, oysa geri izleme, bir çözüm arayan bir sorun alanı boyunca yinelemektir. Geriye dönük izleme, ağaçlarla ilgili bile olması gerekmeyen daha genel bir algoritmadır.



5

DFS'nin özel bir geri izleme biçimi olduğunu söyleyebilirim; geri izleme, DFS'nin genel biçimidir.

DFS'yi genel sorunlara genişletirsek, buna geri izleme diyebiliriz. Ağaç / grafikle ilgili sorunlara geri dönüşü kullanırsak, buna DFS diyebiliriz.

Algoritmik açıdan aynı fikri taşırlar.


DFS ve Backtracking arasındaki ilişki aslında tam tersidir. Bunu detaylandıran cevabımı kontrol edin.
KGhatak

2

Önce derinlik, bir ağaçta gezinmek veya arama yapmak için bir algoritmadır. Buraya bakın . Geriye dönük izleme, bir çözüm adayının oluşturulduğu ve daha sonra eski bir duruma geri dönülerek atıldığı her yerde kullanılan çok daha geniş bir terimdir. Buraya bakın . Önce derinlik araması, önce bir şubeyi (çözüm adayı) aramak için geriye doğru izlemeyi kullanır ve başarılı olmazsa diğer şubeleri / dalları araştırır.


2

IMO, geri izlemenin herhangi bir belirli düğümünde, ilk olarak alt düğümlerinin her birine dallanmayı derinleştirmeye çalışırsınız, ancak alt düğümlerden herhangi birine dalmadan önce, önceki çocuğun durumunu "silmeniz" gerekir (bu adım geri ana düğüme yürüyün). Başka bir deyişle, her kardeşin durumu birbirini etkilememelidir.

Aksine, normal DFS algoritması sırasında, genellikle bu kısıtlamaya sahip olmazsınız, sonraki kardeş düğümü oluşturmak için önceki kardeş durumunu silmeniz (geri izleme) gerekmez.


2

DFS, bir grafiği keşfetmek veya grafikte gezinmek istediğiniz yolu açıklar. Seçim verildiğinde mümkün olduğunca derine inme kavramına odaklanır.

Geri izleme, genellikle DFS aracılığıyla uygulanırken, daha çok, ödün vermeyen arama alt alanlarını olabildiğince erken budama kavramına odaklanır.


1

Bir de derinlemesine arama , sen ağacın kökünde başlar ve daha sonra, her şube boyunca kadarıyla sizi keşfetmek sarfınazar sonraki her ebeveyn düğümüne ve 's çocukları çapraz

Geriye dönük izleme, bir hedefin sonundan başlamak ve aşamalı olarak geriye doğru giderek bir çözüm geliştirmek için kullanılan genel bir terimdir.


5
Geriye dönük izleme, sondan başlayıp geriye doğru gitmek anlamına gelmez. Bir çıkmaz bulunursa, geri izlemek için ziyaret edilen düğümlerin günlüğünü tutar.
Günther Jena

1
"sonunda ...", ha !!
7kemZmani

1

Fikir - Herhangi bir noktadan başlayın, istenen son nokta olup olmadığını kontrol edin, eğer öyleyse, o zaman bir çözüm bulduk, sonraki olası tüm konumlara gider ve daha ileri gidemezsek, önceki konuma geri dönün ve o akımı işaretleyen diğer alternatifleri arayın yol bizi çözüme götürmez.

Artık geri izleme ve DFS, 2 farklı soyut veri türünde uygulanan aynı fikre verilen 2 farklı addır.

Fikir matris veri yapısına uygulanırsa, biz buna geri izleme diyoruz.

Aynı fikir ağaç veya grafiğe uygulanıyorsa ona DFS diyoruz.

Buradaki klişe, bir matrisin grafiğe ve grafiğin matrise dönüştürülebilmesidir. Yani aslında fikri uyguluyoruz. Bir grafikte buna DFS, matriste ise geri izleme diyoruz.

Her iki algoritmadaki fikir aynı.


0

Geriye dönük izleme, belirli sonlandırma koşulları ile derinlemesine ilk araştırmadır.

Her adım için bir karar verdiğiniz, bu kararın çağrı yığınına bir çağrı olduğu (ilk derinlemesine araştırmanızı gerçekleştiren) bir labirentte yürümeyi düşünün ... sonuna ulaşırsanız, yolunuza geri dönebilirsiniz. Ancak, bir çıkmaza ulaşırsanız, belirli bir karardan geri dönmek istersiniz, özünde çağrı yığınınızdaki bir işlevden geri dönersiniz.

Geriye dönmeyi düşündüğümde umursuyorum

  1. Durum
  2. Kararlar
  3. Temel Vakalar (Fesih Koşulları)

Ben geriye benim videoda açıklamak burada .

Geri izleme kodunun bir analizi aşağıdadır. Bu geri izleme kodunda, belirli bir toplam veya hedefle sonuçlanacak tüm kombinasyonları istiyorum. Bu nedenle, çağrı yığınımı çağıran 3 kararım var, her kararda hedef numaraya ulaşmak için yolumun bir parçası olarak bir numara seçebilir, bu sayıyı atlayabilir veya seçip tekrar seçebilirim. Ve sonra bir fesih koşuluna ulaşırsam, geri izleme adımım sadece geri dönmektir . Geri dönüş, geri izleme adımıdır çünkü çağrı yığınındaki çağrıdan çıkar.

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)
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.