คำนวณ Treewidth


14

ความน่าเชื่อถือของกราฟที่ไม่ได้บอกทิศทางเป็นแนวคิดที่สำคัญมากในทฤษฎีกราฟ อัลกอริธึมกราฟจำนวนมากได้รับการคิดค้นขึ้นมาซึ่งทำงานได้รวดเร็วหากคุณมีการสลายตัวของกราฟที่มีความกังวลเล็กน้อย

treewidth มักจะถูกกำหนดในแง่ของการย่อยสลายต้นไม้ นี่คือกราฟและแผนภูมิย่อยสลายของกราฟความเอื้อเฟื้อจาก Wikipedia:

ป้อนคำอธิบายรูปภาพที่นี่

การสลายตัวของต้นไม้เป็นต้นไม้ที่จุดยอดแต่ละจุดเชื่อมโยงกับเซตย่อยของจุดยอดของกราฟต้นฉบับด้วยคุณสมบัติดังต่อไปนี้:

  • จุดยอดทุกอันในกราฟต้นฉบับจะอยู่ในเซตย่อยอย่างน้อยหนึ่งค่า
  • ขอบทุกเส้นในกราฟดั้งเดิมมีจุดยอดทั้งคู่ในเซตย่อยอย่างน้อยหนึ่งค่า
  • จุดยอดทั้งหมดในการสลายตัวที่มีส่วนย่อยประกอบด้วยจุดสุดยอดเดิมที่กำหนดมีการเชื่อมต่อ

คุณสามารถตรวจสอบว่าการสลายตัวข้างต้นเป็นไปตามกฎเหล่านี้ ความกว้างของการย่อยสลายต้นไม้คือขนาดของเซตย่อยที่ใหญ่ที่สุดลบหนึ่ง ดังนั้นจึงเป็นเรื่องที่สองสำหรับการสลายตัวข้างต้น ความกว้างของกราฟคือความกว้างที่เล็กที่สุดของการย่อยสลายต้นไม้ของกราฟนั้น


ในความท้าทายนี้คุณจะได้รับกราฟที่เชื่อมต่อและไม่มีทิศทางและคุณจะต้องพบความกังวล

ในขณะที่การค้นหาการย่อยสลายต้นไม้เป็นเรื่องยากมีวิธีอื่นในการคำนวณ treewidth หน้าวิกิพีเดียมีข้อมูลเพิ่มเติม แต่วิธีหนึ่งในการคำนวณ treewidth ที่ไม่ได้กล่าวถึงซึ่งมักใช้ในอัลกอริทึมในการคำนวณ treewidth นั้นคือความกว้างขั้นต่ำในการกำจัด ดูที่นี่สำหรับกระดาษโดยใช้ข้อเท็จจริงนี้

ในการกำจัดการเรียงลำดับหนึ่งจะกำจัดจุดยอดทั้งหมดของกราฟทีละครั้ง เมื่อกำจัดจุดสุดยอดแต่ละจุดขอบจะถูกเพิ่มเข้ามาเชื่อมต่อกับเพื่อนบ้านของจุดสุดยอดทั้งหมดซึ่งกันและกัน สิ่งนี้ซ้ำจนกระทั่งจุดยอดทั้งหมดหายไป ความกว้างของการสั่งตัดเป็นจำนวนเพื่อนบ้านมากที่สุดที่จุดสุดยอดใด ๆ ที่ถูกกำจัดมีในระหว่างกระบวนการนี้ treewidth เท่ากับขั้นต่ำสำหรับการเรียงลำดับความกว้างของการคัดออกทั้งหมด นี่คือตัวอย่างโปรแกรมที่ใช้ข้อเท็จจริงนี้เพื่อคำนวณความน่าเชื่อถือ:

import itertools
def elimination_width(graph):
    max_neighbors = 0
    for i in sorted(set(itertools.chain.from_iterable(graph))):
        neighbors = set([a for (a, b) in graph if b == i] + [b for (a, b) in graph if a == i])
        max_neighbors = max(len(neighbors), max_neighbors)
        graph = [edge for edge in graph if i not in edge] + [(a, b) for a in neighbors for b in neighbors if a < b]
    return max_neighbors

def treewidth(graph):
    vertices = list(set(itertools.chain.from_iterable(graph)))
    min_width = len(vertices)
    for permutation in itertools.permutations(vertices):
        new_graph = [(permutation[vertices.index(a)], permutation[vertices.index(b)]) for (a, b) in graph]
        min_width = min(elimination_width(new_graph), min_width)
    return min_width

if __name__ == '__main__':
    graph = [('a', 'b'), ('a', 'c'), ('b', 'c'), ('b', 'e'), ('b', 'f'), ('b', 'g'),
            ('c', 'd'), ('c', 'e'), ('d', 'e'), ('e', 'g'), ('e', 'h'), ('f', 'g'), ('g', 'h')]
    print(treewidth(graph))

ตัวอย่าง:

[(0, 1), (0, 2), (0, 3), (2, 4), (3, 5)]
1

[(0, 1), (0, 2), (1, 2), (1, 4), (1, 5), (1, 6), (2, 3), (2, 4), (3, 4), (4, 6), (4, 7), (5, 6), (6, 7)]
2

[(0, 1), (0, 3), (1, 2), (1, 4), (2, 5), (3, 4), (3, 6), (4, 5), (4, 7), (5, 8), (6, 7), (7, 8)]
3

[(0, 1), (0, 2), (0, 3), (0, 4), (1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4)]
4

คุณจะได้รับกราฟเป็นอินพุตและคุณต้องส่งคืน treewidth เป็นเอาต์พุต รูปแบบอินพุตมีความยืดหยุ่น คุณอาจใช้รายการของขอบแผนที่ adjacency หรือเมทริกซ์ adjacency เป็นอินพุต หากคุณต้องการใช้รูปแบบการป้อนข้อมูลอื่นให้ถามในความคิดเห็น คุณอาจสมมติว่าอินพุตนั้นเชื่อมต่อกันและคุณอาจสร้างสมมติฐานนั้นในรูปแบบอินพุตของคุณเช่นโดยใช้รายการขอบ

แก้ไข: การดำเนินการในตัวซึ่งคำนวณความน่าเชื่อถือไม่ได้รับอนุญาต ฉันขอโทษที่ไม่ได้ระบุสิ่งนี้ไว้ล่วงหน้า

รหัสที่สั้นที่สุดชนะ


เนื่องจากกราฟเป็น tuple อย่างเป็นทางการ(V,E)นี่จะเป็นอินพุตที่ถูกต้องหรือไม่
ბიმო

@Bruce_Forte อย่างแน่นอน
isaacg

คำตอบ:


7

คู่, 195 ไบต์

function x=F(a)r=rows(a);P=perms(s=1:r);x=r;for m=s;b=a;n=0;for z=P(m,:);(T=sum(b)(z))&&{b|=(k=accumarray(nchoosek(find(b(z,:)),2),1,[r r]))|k';n=max(T,n);b(z,:)=0;b(:,z)=0}{4};end;x=min(x,n);end

ฟังก์ชั่นที่ใช้เป็นอินพุตเมทริกซ์ adjacency ใช้หน่วยความจำจำนวนมากดังนั้นจึงไม่มีประโยชน์หากจำนวนจุดยอดมากกว่า 10-12

  • ไม่จำเป็นต้องมีendfunctionอย่างไรก็ตามควรเพิ่มลงใน tio

ลองออนไลน์!

Ungolfed:

function min_width = treewidth(graph_adj)
    Nvertices = rows(graph_adj)
    Permutations = perms(1:Nvertices);                                                            % do not try it for large number of vertices
    min_width = Nvertices;
    for v = 1:Nvertices;
        new_graph=graph_adj;
        max_neighbors=0;
        for p = Permutations(v,:)
            Nneighbors=sum(new_graph)(p);
            if(Nneighbors)>0
                connection=accumarray(nchoosek(find(new_graph(p,:)),2),1,[Nvertices Nvertices]);  % connect all neighbors
                new_graph|=connection|connection';                                                % make the adjacency matrix symmetric
                new_graph(p,:)=0;new_graph(:,p)=0;                                                % eliminate the vertex
                max_neighbors=max(Nneighbors,max_neighbors);
            end
        end
        min_width=min(min_width,max_neighbors);
    end
end

5

SageMath, 29 ไบต์ที่ไม่เข้ากัน *

lambda L:Graph(L).treewidth()

* คำตอบนี้โพสต์ก่อนการเปลี่ยนแปลงของ OP ของคำถามที่ว่า "Buildins ถูกแบน" ดังนั้นฉันจึงทำการแมปไม่ใช่การคอมไพล์

การสาธิตออนไลน์!


1
ลูกหมี มันไม่น่าเบื่อเลย น่าเสียดายที่ฉันจะต้องห้ามตัวอาคารขออภัยด้วย
isaacg

@isaacg ไม่มีปัญหา ฉันมีสิ่งอื่นในมือ :)
rahnema1

@isaacg คำตอบนี้ไม่ละเมิดช่องโหว่มาตรฐานใช่หรือไม่
PyRulez

rahnema1 ดูการแก้ไขของฉัน Builtins ถูกแบนดังนั้นคำตอบนี้ไม่ได้รับอนุญาต โปรดลบหรือทำเครื่องหมายว่าไม่ใช่การแข่งขัน
isaacg

@isaacg ขอบคุณฉันทำเครื่องหมายว่าไม่ใช่การแข่งขัน
rahnema1

5

Haskell (แลมบ์บ็อต), 329 321 245 ไบต์

Eqนี่คือทางออกของฉันขอบคุณความยืดหยุ่นของการป้อนข้อมูลที่ทำงานบนกราฟด้วยกราฟที่มีชนิดที่เป็นตัวอย่างของการใด ๆ

(&)=elem
l=length
t n g s=last$minimum[max(t n g b)$t(n++b)g$s\\b|b<-filterM(\_->[0>1,1>0])s,l b==div(l s)2]:[l[d|d<-fst g,not$d&n,d/=s!!0,(d&)$foldr(\x y->last$y:[x++y|any(&y)x])[s!!0]$join(>>)[e|e<-snd g,all(&(s!!0:d:n))e]]|1==l s]
w=t[]<*>fst

ลองออนไลน์!

เวอร์ชันที่ไม่ถูกปรับแต่ง:

type Vertex a = a
type Edge a   = [Vertex a]
type Graph a  = ([Vertex a],[Edge a])

vertices = fst
edges = snd

-- This corresponds to the function w
treeWidth :: (Eq a) => Graph a -> Int
treeWidth g = recTreeWidth g [] (vertices g)

-- This is the base case (length s == 1) of t
recTreeWidth graph left [v] =
    length [ w | w <- vertices graph
               , w `notElem` left
               , w /= v
               , w `elem` reachable (subGraph w)
           ]

  where subGraph w = [ e | e <- edges graph, all (`elem` v:w:left) e ]

        reachable g = foldr accumulateReachable [v] (g>>g)
        accumulateReachable x y = if any (`elem` y) x
                                  then x++y
                                  else y

-- This is the other case of t
recTreeWidth graph left sub =
  minimum [ comp sub' | sub' <- filterM (const [False,True]) sub
                      , length sub' == div (length sub) 2
          ]

  where comp b = max (recTreeWidth graph left b)
                     (recTreeWidth graph (left++b) (sub\\b))
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.