2010-11-08 10 views
6

Tôi cố gắng để sử dụng thuật toán Breadth-First để lấy toàn bộ cụm các kết nối mà người dùng được lựa chọn thuộc về các cách sau:Phân tích đồ thị lớn - Lấy Clusters và Tính Paths Strongest

CREATE PROCEDURE Breadth_First (@StartNode varchar(50), @LinkStrength decimal(10,7) = 0.1, @EndNode varchar(50) = NULL) 
    AS 
    BEGIN 
     -- Automatically rollback the transaction if something goes wrong. 
     SET XACT_ABORT ON 
     BEGIN TRAN 

     -- Increase performance and do not intefere with the results. 
     SET NOCOUNT ON; 

     -- Create a temporary table for storing the discovered nodes as the algorithm runs 
     CREATE TABLE #Discovered 
     (
      DiscoveredUser varchar(50) NOT NULL, -- The Node Id 
      Predecessor varchar(50) NULL, -- The node we came from to get to this node. 
      LinkStrength decimal(10,7) NULL, -- positive - from predecessor to DiscoveredUser, negative - from DiscoveredUser to predecessor 
      OrderDiscovered int -- The order in which the nodes were discovered. 
     ) 

     -- Initially, only the start node is discovered. 
     INSERT INTO #Discovered (DiscoveredUser, Predecessor, LinkStrength, OrderDiscovered) 
     VALUES (@StartNode, NULL, NULL, 0) 

     -- Add all nodes that we can get to from the current set of nodes, 
     -- that are not already discovered. Run until no more nodes are discovered. 
     WHILE @@ROWCOUNT > 0 
     BEGIN 
      -- If we have found the node we were looking for, abort now. 
      IF @EndNode IS NOT NULL 
       IF EXISTS (SELECT TOP 1 1 FROM #Discovered WHERE DiscoveredUser = @EndNode) 
        BREAK 

      -- We need to group by ToNode and select one FromNode since multiple 
      -- edges could lead us to new same node, and we only want to insert it once. 
      INSERT INTO #Discovered (DiscoveredUser, Predecessor, LinkStrength, OrderDiscovered) 
      (SELECT mc.called_party, mc.calling_party, mc.link_strength, d.OrderDiscovered + 1 
      FROM #Discovered d JOIN monthly_connections mc ON d. DiscoveredUser = mc.calling_party 
      WHERE mc.called_party NOT IN (SELECT DiscoveredUser From #Discovered) AND mc.link_strength > @LinkStrength 
      UNION 
      SELECT mc.calling_party, mc.called_party, mc.link_strength * (-1), d.OrderDiscovered + 1 
      FROM #Discovered d JOIN monthly_connections mc ON d. DiscoveredUser = mc.called_party 
      WHERE mc.calling_party NOT IN (SELECT DiscoveredUser FROM #Discovered) AND mc.link_strength > @LinkStrength 
      ) 
     END; 

     -- Select the results. We use a recursive common table expression to 
     -- get the full path from the start node to the current node. 
     WITH BacktraceCTE(Predecessor, DiscoveredUser, LinkStrength, OrderDiscovered, Path) 
     AS 
     (
      -- Anchor/base member of the recursion, this selects the start node. 
      SELECT d.Predecessor, n. DiscoveredUser, d.LinkStrength, d.OrderDiscovered, 
       CAST(n. DiscoveredUser AS varchar(MAX)) 
      FROM #Discovered d JOIN users n ON d. DiscoveredUser = n. DiscoveredUser 
      WHERE d. DiscoveredUser = @StartNode 

      UNION ALL 

      -- Recursive member, select all the nodes which have the previous 
      -- one as their predecessor. Concat the paths together. 
      SELECT d.Predecessor, n. DiscoveredUser, d.LinkStrength, d.OrderDiscovered, 
       CAST(cte.Path + ',' + CAST(n. DiscoveredUser as varchar(30)) as varchar(MAX)) 
      FROM #Discovered d JOIN BacktraceCTE cte ON d.Predecessor = cte. DiscoveredUser 
      JOIN users n ON d. DiscoveredUser = n. DiscoveredUser 
     ) 

     SELECT Predecessor, DiscoveredUser, LinkStrength, OrderDiscovered, Path FROM BacktraceCTE 
     WHERE DiscoveredUser = @EndNode OR @EndNode IS NULL -- This kind of where clause can potentially produce 
     ORDER BY OrderDiscovered    -- a bad execution plan, but I use it for simplicity here. 

     DROP TABLE #Discovered 
     COMMIT TRAN 
     RETURN 0 
    END 

Sơ đồ (mạng xã hội) mà tôi hiện đang phân tích có 28M kết nối và không bỏ qua kết nối yếu (thiết lập treshold với @LinkStrength) thực hiện đang chạy rất dài thời gian (cho đến nay tôi đã không có bất kỳ kết quả và sẽ cố gắng để nó chạy qua đêm).

Dù sao bước tiếp theo là tính các liên kết ngắn nhất (mạnh nhất) giữa hai người dùng (có khoảng 3 người dùng). Tôi đã suy nghĩ về việc sử dụng thuật toán Djikstra nhưng không chắc liệu có thể phân tích mạng trên PC mà tôi hiện đang sử dụng (CPU Quad 2.66 GHz, RAM 4GB) và dữ liệu được lưu trữ trong cơ sở dữ liệu MS SQL Server 2008 hay không.

Để tóm tắt tôi sẽ đánh giá cao để có được một số câu trả lời/gợi ý cho các câu hỏi sau:

  1. Có thể thực hiện truy vấn phức tạp như một ở trên cho mô tả đồ thị (kết nối 28m, 3M người dùng) trên máy được mô tả (2,66 GHz, RAM 4GB)?
  2. Nếu không thể có các cách tiếp cận khác có thể theo đó thời gian thực hiện có thể bị giảm (ví dụ: tạo bảng có kết quả một phần)?
  3. Bạn có đề xuất bất kỳ thuật toán khác cho phát hiện cụm và tính toán đường đi ngắn nhất cho biểu đồ được mô tả không?

Cảm ơn bạn!

+0

Nếu bạn có thể nghĩ đến một số chẩn đoán tốt mà theo đó để đoán mà tại đó các liên kết có nhiều khả năng hơn những người khác sau đó bạn có thể muốn thử A * ... – fmark

+0

Bạn đang gần như chắc chắn I/O hơn CPU hoặc bộ nhớ bị ràng buộc, thêm cọc và phân phối các tập tin tạm thời và dữ liệu của bạn sẽ cải thiện hiệu suất SQL. Ngay cả với tối ưu hóa, về cơ bản bạn đang thực hiện một nhiệm vụ mà SQL thấy khó để tối ưu hóa, và chắc chắn sẽ tiếp tục nhấn nút cổ chai I/O khi tập dữ liệu của bạn phát triển. Hầu hết các phạm vi tiếp cận dân gian nhạy cảm với thời gian để giảm bản đồ tại thời điểm này. – stephbu

Trả lời

0

Cho dù bạn đi cho chiều rộng đầu tiên hoặc chiều sâu không thực sự quan trọng nếu bạn muốn có một câu trả lời chính xác. Câu trả lời chính xác sẽ yêu cầu tìm kiếm toàn diện, điều này sẽ chậm.

Giống như fmark gợi ý, một heuristic có thể giúp bạn tìm ra giải pháp tối ưu tiềm năng với mức độ chắc chắn hợp lý. Nó sẽ giúp bạn tiết kiệm rất nhiều thời gian, nhưng nó sẽ không chính xác.

Bạn phải chọn tốc độ hoặc độ chính xác, bạn thực sự không thể có cả hai. Nó giống như nén hình ảnh cho hình ảnh (hoặc âm thanh hoặc video): với hầu hết các hình ảnh của cảnh thiên nhiên png là lossless nhưng không nén nhiều, jpeg nén khá tốt nhưng với một số mất mát.

EDIT 1: Điều duy nhất tôi có thể nghĩ về điều đó có thể giúp bạn tìm kiếm chính xác là lý thuyết toán học về ma trận thưa thớt. Vấn đề của bạn tương tự như nâng ma trận sức mạnh quan hệ xã hội lên một loạt các quyền hạn khác nhau (power n = n bước giữa người A và B) và tìm ra những ô nào có giá trị cao nhất cho mỗi cặp (A, B). Đây là những gì bạn đang làm với truy vấn của bạn, chỉ có một truy vấn DB có thể không phải là cách nhanh nhất để đạt được điều này.

Tôi không thể giúp bạn nhiều hơn với điều này. Bạn có thể muốn xem Wikipedia for Sparse Matrix.

EDIT 2: Tôi chỉ nghĩ về một điều nữa.Tôi không thể thấy làm thế nào bạn có thể loại bỏ các nhánh mà bạn biết chắc chắn sẽ yếu với truy vấn SQL trong khi với thuật toán phù hợp để làm việc trên ma trận thưa thớt của bạn, nên dễ dàng loại bỏ các nhánh mà bạn biết có thể loại bỏ trên mô hình sức mạnh của bạn.

1

Đầu tiên, sử dụng chỉ mục.

Thứ hai, bạn cần giảm yêu cầu bộ nhớ của mình. Điều đó có nghĩa là trước tiên cung cấp một bí danh ngắn cho VARCHAR của bạn (50), chẳng hạn như int là 4 byte thay vì 50. Điều đó sẽ giúp bạn có được tốc độ gấp 10 lần.

declare @tmpPeople table(
    ixPerson int identity primary key, 
    UserNodeID varchar(50), 
    unique(UserNodeID, ix) -- this creates an index 
) 
Insert @tmpPeople(UserNodeID) select UserNodeID from NormalPeopleTable 
declare @relationships table(
    ixParent int, 
    ixChild int, 
    unique(ixParent, ixChild), 
    unique(ixChild, ixParent) 
) 
insert @relationships(ixParent, ixChild) 
select distinct p.ixPerson, c.ixPerson 
from NormalRelationshipsTable nr 
inner join @tmpPeople p on p.UserNodeID = nr.ParentUserNodeID 
inner join @tmpPeople c on c.UserNodeID = nr.ChildUserNodeID 

-- OK now got a copy of all relationships, but it is a fraction of the size 
-- because we are using integers for the keys. 
-- if we need to we can insert the reverse relationships too. 

Bạn cần phải viết truy vấn thực hiện những gì bạn muốn, không phải thứ gì đó "chung chung".

Nếu bạn muốn tìm khoảng cách ngắn nhất giữa hai nút, bạn có thể cắt thời gian tìm kiếm của mình bằng cách tìm kiếm từ cả hai đầu cùng một lúc.

declare @p1 table(
ix int identity primary key, 
ixParent int, 
ixChild int, 
nDeep int, 
-- Need indexes 
unique(ixParent, ixChild, nDeep, ix), 
unique(ixChild, ixParent, nDeep, ix) 
) 
-- That's now indexed both ways. 
-- If you only need one, you can comment the other out. 
-- define @p2 the same 

insert @p1 (ixParent, ixChild, nDeep) select @ixUserFrom, @ixUserFrom, 0 
insert @p2 ..... @ixUserTo, @ixUserTo, 0 

-- Breadth first goes like this. 
-- Just keep repeating it till you get as far as you want. 
insert @p1 (ixParent, ixChild, nDeep) 
select 
p1.ixChild, r.ixChild, p1.nDeep+1 
from @p1 p1 inner join @relationships r on r.ixParent = p1.ixChild 
-- may want to exclude those already in the table 
where not exists (
    select 1 from @p1 p1 
    where p1.ixParent = p.ixChild and p1.ixChild = r.ixChild 
) 

Đối với "khoảng cách từ Alice đến Bob", bạn thực hiện hai tìm kiếm song song và dừng khi tìm kiếm của Alice chứa bất kỳ ai cũng có trong tìm kiếm của Bob. Điều đó cũng làm giảm thời gian của bạn xuống bởi một yếu tố n^2 trong đó n là số lượng kết nối trung bình.

Đừng quên dừng nếu độ sâu quá lớn.

0

Có lẽ nó sẽ giúp di chuyển lần đầu tiên đến Graph DB trước khi thực hiện phân tích của bạn. Tôi đã không sử dụng chúng cá nhân, nhưng đã được khuyến cáo rằng tôi thử neo4j.

HTH