Etiketsiz ağaçların verimli sıkıştırılması


20

Etiketlenmemiş, köklü ikili ağaçları düşünün. Biz olabilir sıkıştırmak alt ağaç işaretçileri vardır zaman: bu ağaçlar ve ile (yorumlama yapısal eşitlik) aksine, depolamak (wlog) ve tüm işaretçiler yerine işaretçilerle . Örnek için uli'nin cevabına bakınız .TT'T=T'=TT'T

Yukarıdaki anlamda bir ağacı girdi olarak alan ve sıkıştırmadan sonra kalan (minimum) düğüm sayısını hesaplayan bir algoritma verin. Algoritma zaman çalışmalıdır ile (tek bir maliyet modelinde) giriş düğüm sayısına.nÖ(ngünlükn)n

Bu bir sınav sorusuydu ve ne güzel bir çözüm bulamadım ne de bir çözüm bulamadım.


Ve buradaki temel operasyon, “maliyet”, “zaman” nedir? Ziyaret edilen düğüm sayısı? Geçilen kenar sayısı? Ve girdinin boyutu nasıl belirlenir?
uli

Bu ağaç sıkıştırması hash consing örneğidir . Bunun genel bir sayma yöntemine yol açıp açmadığından emin değilim.
Gilles 'SO- kötü olmayı bırak'

@ n nin ne olduğunu açıkladım . Bence "zaman" yeterince spesifik. Eşzamanlı olmayan ayarlarda bu, Landau cinsinden sayma işlemlerine, en sık gerçekleşen temel işlem saymaya eşdeğerdir.
Raphael

@Raphael Tabii ki amaçlanan temel operasyonun ne olması gerektiğini tahmin edebilirim ve muhtemelen herkesle aynı şeyi seçeceğim. Ancak, burada bilgiç olduğumu biliyorum, “zaman sınırı” ne zaman verildiğinde neyin sayıldığını belirtmek önemlidir. Takas, karşılaştırma, eklemeler, bellek erişimleri, denetlenen düğümler, çapraz kenarlar, adını siz verirsiniz. Bu, fizikteki ölçü birimini atlamak gibidir. Öyle mi 10kg veya10ms ? Ve sanırım bellek erişimleri neredeyse her zaman en sık kullanılan işlemdir.
uli

@uli Bunlar, "tek tip maliyet modeli" nin aktarması gereken detaylardır. Hangi operasyonların temel olduğunu tam olarak tanımlamak acı vericidir, ancak vakaların% 99,99'unda (bu dahil) belirsizlik yoktur. Karmaşıklık sınıfları temelde birimlere sahip değildir, bir örneği gerçekleştirmek için gereken süreyi ölçmezler, ancak girdi büyüdükçe bu zamanın biçimi de değişir.
Gilles 'SO- kötü olmayı bırak'

Yanıtlar:


10

Evet, bu sıkıştırmayı zamanında yapabilirsiniz, ancak bu kolay değildir :) Önce bazı gözlemler yaparız ve sonra algoritmayı sunarız. Ağacın başlangıçta sıkıştırılmadığını varsayarız - bu gerçekten gerekli değildir, ancak analizi kolaylaştırır.O(nlogn)

İlk olarak, 'yapısal eşitliği' tümevarımsal olarak nitelendiriyoruz. Let ve T ' , iki (alt) ağaç olabilir. Eğer T ve T ' (da hiç tepe noktalarına sahip olan) boş ağaç hem de, yapısal eşdeğerdir. Eğer T ve T ' hem boş değil ağaçlar, bu durumda onlar da sol çocukları yapısal olarak eşdeğerdir ve onların sağ çocukları yapısal olarak eşdeğerdir IFF yapısal olarak eşdeğerdir. 'Yapısal eşdeğerlik', bu tanımlar üzerindeki minimum sabit noktadır.TTTTTT

Örneğin, herhangi iki yaprak düğümü yapısal olarak eşdeğerdir, çünkü her ikisi de yapısal olarak eşdeğer olan her iki çocuğu olarak da boş ağaçlara sahiptir.

'Sol çocukları yapısal olarak eşdeğerdir ve sağ çocukları da' demek oldukça can sıkıcı olduğu için, sık sık 'çocukları yapısal olarak eşdeğerdir' diyeceğiz ve aynı şeyi düşüneceğiz. Ayrıca, 'bu tepe noktasına dayanan alt ağaç' demek istediğimizde bazen 'bu tepe noktasını' söylediğimizi unutmayın.

Yukarıdaki tanım bize derhal sıkıştırmanın nasıl yapılacağına dair bir ipucu verir: tüm alt ağaçların derinliği en fazla olan yapısal eşdeğerliğini bilirsek, alt ağaçların yapısal eşdeğerliğini d + 1 derinliği ile kolayca hesaplayabiliriz . O ( n 2 ) çalışma süresinden kaçınmak için bu hesaplamayı akıllı bir şekilde yapmak zorundayız .dd+1O(n2)

Algoritma, yürütülmesi sırasında her tepe noktasına tanımlayıcılar atayacaktır. Bir tanımlayıcı kümesindeki bir sayıdır . Tanımlayıcılar benzersizdir ve asla değişmez: bu nedenle algoritmanın başlangıcında bazı (global) değişkenleri 1 olarak ayarladığımızı ve bazı tepe noktalarına her tanımlayıcı atadığımızda, bu değişkenin geçerli değerini tepe noktasına ve artışa atarız bu değişkenin değeri.{1,2,3,,n}

İlk olarak giriş ağacını , ebeveynlerine bir işaretçi ile birlikte eşit derinliğe sahip köşeler içeren (en fazla ) listeye dönüştürüyoruz . Bu kolayca O ( n ) zamanda yapılabilir.nO(n)

Öncelikle tüm yaprakları (bu yaprakları listede 0 derinlik köşeleriyle bulabiliriz) tek bir tepe noktasına sıkıştırırız. Bu tepe noktasına bir tanımlayıcı atarız. İki köşenin sıkıştırılması, her bir tepe noktasının üst öğesini diğer tepe noktasına işaret edecek şekilde yönlendirerek yapılır.

İki gözlem yapıyoruz: ilk olarak, herhangi bir tepe noktasının kesinlikle daha küçük derinliğe sahip çocukları var ve ikincisi, den daha küçük tüm derinliklerde sıkıştırma gerçekleştirdiysek (ve onlara tanımlayıcılar verdiysek ), d derinliğinin iki köşesi yapısal olarak eşdeğerdir ve çocuklarının tanımlayıcıları çakıştığında sıkıştırılabilir. Bu son gözlem aşağıdaki argümandan kaynaklanmaktadır: iki köşesi, çocukları yapısal olarak eşdeğer olduğunda yapısal olarak eşdeğerdir ve sıkıştırmadan sonra, işaretçileri aynı çocukları işaret ettiği anlamına gelir, bu da çocuklarının tanımlayıcılarının eşit olduğu anlamına gelir.dd

Küçük derinlikten büyük derinliğe kadar eşit derinliğe sahip düğümleri olan tüm listeleri tekrarlıyoruz. Her seviye için, her bir çiftin o seviyedeki bazı tepe noktalarının çocuklarının tanımlayıcılarına karşılık geldiği bir tamsayı çiftleri listesi oluştururuz. Karşılık gelen tamsayı çiftleri eşit olduğu takdirde, bu seviyedeki iki köşenin yapısal olarak eşdeğer olduğuna sahibiz. Sözcük bilgisi düzenini kullanarak bunları sıralayabilir ve eşit olan tamsayı çiftleri kümesini elde edebiliriz. Bu setleri yukarıdaki gibi tek köşelere sıkıştırır ve tanımlayıcıları veririz.

Yukarıdaki gözlemler bu yaklaşımın işe yaradığını ve sıkıştırılmış ağaçla sonuçlandığını kanıtlamaktadır. Toplam çalışma süresi artı oluşturduğumuz listeleri sıralamak için gereken süredir. Oluşturduğumuz tamsayı çiftlerinin toplam sayısı n olduğundan bu, toplam çalışma süresinin gerektiği gibi O ( n log n ) olduğunu gösterir. Prosedürün sonunda kaç düğüm kaldığımızı saymak önemsizdir (sadece kaç tanımlayıcıyı dağıttığımıza bakın).O(n)nO(nlogn)


Cevabınızı ayrıntılı olarak okumadım, ancak sanırım, sorunlara özgü garip bir bakış açısıyla, az ya da çok yeniden yarattığınız hash consing.
Gilles 'SO- kötü olmayı bırak'

@Alex “kesinlikle daha küçük çocuklar degree” olmalıdır depth? Ve aşağı doğru büyüyen CS ağaçlarına rağmen, “bir ağacın yüksekliğini” “bir ağacın derinliğinden” daha az kafa karıştırıcı buluyorum.
uli

Güzel cevap. Sıralama yapmanın bir yolu olmalı gibi hissediyorum. @Gilles cevabı hakkındaki ikinci yorumum da burada geçerli.
Raphael

@uli: evet, haklısın, düzelttim (neden bu iki kelimeyi karıştırdığımdan emin değilim). Yükseklik ve derinlik iki farklı kavramdır ve ikincisine ihtiyacım vardı :) Bunları değiştirerek herkesi karıştırmak yerine geleneksel 'derinliğe' bağlı kalacağımı düşündüm.
Alex ten Brink

4

Değişken olmayan bir veri yapısının, yapısal olarak eşit herhangi bir arayı kopyalamaması için sıkıştırılması, karma eksing olarak bilinir . Bu fonksiyonel programlamada bellek yönetiminde önemli bir tekniktir. Hash consing, veri yapıları için bir tür sistematik nottur.

Ağacı hash-cons olarak yapacağız ve hash consing'den sonra düğümleri sayacağız. boyutunda bir veri yapısına sahip olan karma her zaman O ( n) olarak yapılabilirn işlemleri; sondaki düğüm sayısının sayılması, düğüm sayısında doğrusaldır.O(nlg(n))

Ağaçları aşağıdaki yapıya sahip olarak değerlendireceğim (burada Haskell sözdiziminde yazılmıştır):

data Tree = Leaf
          | Node Tree Tree

Her kurucu için, kurucuyu bu argümanlara uygulama sonucuna kadar olası argümanlarından bir eşleme yapmamız gerekir. Yapraklar önemsizdir. Düğüm için, sınırlı bir kısmi haritasını sağlamak , T ağaç tanımlayıcıları grubu olup , N düğümü tanımlayıcıları grubu olduğu; T = N { } burada tek yaprak tanımlayıcıdır. (Somut olarak, tanımlayıcı bir bellek bloğunun göstergesidir.)nodes:T×TNTNT=N{}

nodesDengeli bir ikili arama ağacı gibi logaritmik-zaman veri yapısını kullanabiliriz . Aşağıda veri yapısında lookup nodesbir anahtarı arayan işlemi ve yeni bir anahtarın altına bir değer ekleyen ve bu anahtarı döndüren işlemi çağıracağım .nodesinsert nodes

Şimdi ağacı hareket ettiriyoruz ve ilerledikçe düğümleri ekliyoruz. Her ne kadar Haskell benzeri sözde kod yazsam da, nodesküresel değişken bir değişken olarak ele alacağım ; buna yalnızca ekleyeceğiz, ancak eklemelerin her tarafa geçirilmesi gerekiyor. addFonksiyon olan alt ağaçlar ekleyerek, ağaç recurses nodesharita ve kök tanıtıcısı döndürür.

insert (p1,p2) =
add Leaf = $\ell$
add (Node t1 t2) =
    let p1 = add t1
    let p2 = add t2
    case lookup nodes (p1,p2) of
      Nothing -> insert nodes (p1,p2)
      Just p -> p

Aynı insertzamanda nodesveri yapısının son boyutu olan çağrı sayısı, maksimum sıkıştırmadan sonraki düğüm sayısıdır. (Gerekirse boş ağaç için bir tane ekleyin.)


" büyüklüğünde bir veri yapısının O ( n l g ( n ) ) işlemlerinde her zaman yapılabileceğini belirten karma" için bir referans verebilir misiniz ? İstenen çalışma zamanını elde etmek için dengeli ağaçlara ihtiyacınız olacağını unutmayın . nO(nlg(n))nodes
Raphael

Sadece aynı ağaç için hashın bağımsız olarak hesaplanmasının her zaman aynı sonucu vermesi için, alt yapıları sayılara yapılandırılmış bir şekilde hash etmeyi düşünüyordum. Elimizde değişebilir veri yapıları olması koşuluyla çözümünüz de iyi. Bence biraz temizlenebileceğini düşünüyorum; serpiştirilmesi insertve addaçık hale getirilmesi gerekir ve problemi gerçekten çözen bir fonksiyon imho verilmelidir.
Raphael

1
@Raphael Hash consing, işaretçi / tanımlayıcı tuples üzerinde sonlu bir harita yapısına dayanır, bunu arama ve ekleme için logaritmik zamanla uygulayabilirsiniz (örneğin dengeli bir ikili arama ağacı ile). Benim çözümüm değişebilirlik gerektirmez; nodesKolaylık sağlamak için değişken bir değişken yapıyorum , ancak bunu boyunca iş parçacığı oluşturabilirsiniz. Ben tam kod vermeyeceğim, bu SO değil.
Gilles 'SO- kötü olmayı bırak'

1
@Raphael Hashing yapıları, onlara rasgele sayılar atamak yerine, biraz tehlikeli. Tek tip maliyet modelinde, herhangi bir şeyi büyük bir tamsayıya kodlayabilir ve üzerinde gerçekçi olmayan sabit zamanlı işlemler yapabilirsiniz. Gerçek dünyada, sonsuz setlerden sonlu bir tamsayı aralığına fiili birebir eşleme yapmak için kriptografik karmaları kullanabilirsiniz, ancak yavaştırlar. Karma olarak kripto dışı bir sağlama toplamı kullanırsanız, çarpışmalar hakkında düşünmeniz gerekir.
Gilles 'SO- kötü olmayı bırak'

3

İşte ağaçların yapısını keyfi olarak etiketlemek yerine (enjekte ederek) sayılara kodlamayı amaçlayan başka bir fikir. Bunun için herhangi bir sayının asal çarpanlaştırmasının benzersiz olduğunu kullanıyoruz.

Bizim amacımız için, ağaçta boş bir konumu ve N ( l , r ) sol alt ağaç l ve sağ alt ağaç r olan bir düğümü belirtmesine izin verin . N ( E , E ) bir yaprak olurdu. Şimdi izin verEN(l,r)lrN(E,E)

f(E)=0f(N(l,r))=2f(l)3f(r)

kullanarak , bir ağaç altta bulunan tüm alt ağaçların kümesini hesaplayabiliriz; her düğümde, çocuklardan elde edilen kodlama kümelerini birleştirir ve yeni bir sayı ekleriz (bu, çocukların kodlamalarından sabit zamanda hesaplanabilir).f

Bu son varsayım gerçek makinelerde bir gerginliktir; bu durumda, üstel olmak yerine Cantor'un eşleştirme işlevine benzer bir şey kullanmayı tercih ederim .

Bu algoritmanın çalışma zamanı ağacın yapısına bağlıdır (dengeli ağaçlarda, doğrusal zamanda birleşmeye izin veren herhangi bir uygulama ile). Genel ağaçlar için, basit bir analizle logaritmik zaman birliğine ihtiyacımız olurdu. Belki de karmaşık bir analiz yardımcı olabilir; her zamanki en kötü durum ağacı olan doğrusal listenin burada O ( n log n ) zamanını kabul ettiğine dikkat edin , bu yüzden en kötü durumun ne olduğu çok açık değildir.O(nlogn)O(nlogn)


1

Yorumlarda resimlere izin verilmediği için:

resim açıklamasını buraya girin

sol üst: giriş ağacı

sağ üst: düğüm 5 ve 7'de köklenen alt ağaçlar da izomorfiktir.

sol alt ve sağ: sıkıştırılmış ağaçlar benzersiz bir şekilde tanımlanmamıştır.

7+5|T|6+|T|


Bu gerçekten istenen operasyonun bir örneği, teşekkürler. Orijinal ve eklenen referanslar arasında ayrım yapmazsanız, son örneklerinizin aynı olduğunu unutmayın.
Raphael

-1

Düzenleme: T ve T same aynı ebeveynin çocukları olarak soruyu okudum. Sıkıştırma tanımını da özyinelemeli olarak aldım, yani önceden sıkıştırılmış iki alt ağacı sıkıştırabilirsiniz. Asıl soru bu değilse, cevabım işe yaramayabilir.

Ö(ngünlükn) için yalvarır T(n)=2T(n/2)+cnböl ve çöz çözümü. Düğümleri yinelemeli olarak sıkıştırın ve sıkıştırmadan sonra her alt ağaçtaki torun sayısını hesaplayın. İşte bazı python-esque sözde kodu.

def Comp(T):
   if T == null:
     return 0
   leftCount = Comp(T.left)
   rightCount = Comp(T.right)
   if leftCount == rightCount:
     if hasSameStructure(T.left, T.right):
       T.right = T.left
       return leftCount + 1
     else
       return leftCount + rightCount + 1    

hasSameStructure()Aynı yapıya sahip olup olmadıklarını görmek için önceden sıkıştırılmış iki alt çizgiyi doğrusal zamanda karşılaştıran bir işlev nerede . Herbiri gezen ve diğerinin her yaptığında bir alt ağacın sol çocuğu olup olmadığını kontrol eden doğrusal zaman özyinelemeli bir işlev yazmak zor olmamalıdır.

İzin Vermek n ve nrsırasıyla sol ve sağ alt ağaçların boyutları (sıkıştırmadan sonra) olmalıdır. Sonra çalışma süresi

T(n)=T(n1)+T(n2)+Ö(1) Eğer nnr
ve
2T(n/2)+Ö(n) aksi takdirde

Alt ağaçlar kardeş değilse ne olur? ((T1, T1), (T2, T1)) T1'in bakımı, üçüncü oluşumun ikinci göstericisi kullanılarak iki kez kaydedilebilir.
uli

@uli Ne dediğinden emin değilim. Soruyu şu şekilde okudumT ve T'aynı ebeveynin çocuklarıydı. Asıl soru bu değilse, cevabım işe yaramayabilir. Sıkıştırma tanımını da özyinelemeli olarak aldım, yani önceden sıkıştırılmış iki alt ağacı sıkıştırabilirsiniz.
Joe

Sorular sadece iki alt gerilimin izomorfik olarak tanımlandığını belirtir. Aynı ebeveyne sahip oldukları hakkında hiçbir şey söylenmez. Bir alt ağaç T1, bir önceki örnekte ((T1, T1), (T1, T2)) olduğu gibi bir ağaçta üç kez görünüyorsa, üçüncü olayı göstererek iki olay sıkıştırılabilir.
uli
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.