Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/sql/72.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sql 挑战,如何实现六度分离的算法?_Sql_Performance_Algorithm - Fatal编程技术网

Sql 挑战,如何实现六度分离的算法?

Sql 挑战,如何实现六度分离的算法?,sql,performance,algorithm,Sql,Performance,Algorithm,用户A用户B用户C用户D用户F 通过“-”连接的用户相互了解 我需要这两个任务的算法: 计算从UserX到UserY的路径 对于UserX,计算距离不超过3步的所有用户 有没有有效的解决办法 编辑 我的目的不是证明它是对还是错,而是在必要时实时计算结果 另外,我认为最具表现力的方式是代码,甚至是伪代码 再次编辑 我决定这种工作必须在数据库中完成,所以它必须是sql解决方案 你会发现很多 我怀疑你能找到伪代码(至少我还没有找到)。以下是一些有趣的内容: 图形算法可以在这里帮助您。了解他们也很有

用户A用户B用户C用户D用户F

通过“-”连接的用户相互了解

我需要这两个任务的算法:

  • 计算从UserX到UserY的路径
  • 对于UserX,计算距离不超过3步的所有用户
  • 有没有有效的解决办法

    编辑

    我的目的不是证明它是对还是错,而是在必要时实时计算结果

    另外,我认为最具表现力的方式是代码,甚至是伪代码

    再次编辑

    我决定这种工作必须在数据库中完成,所以它必须是sql解决方案

    你会发现很多

    我怀疑你能找到伪代码(至少我还没有找到)。以下是一些有趣的内容:




    图形算法可以在这里帮助您。了解他们也很有趣

    • 对于连接
    • Dijkstra(A*)表示用户之间的路径
    • 用于查找远离用户的所有用户N个节点的简单DFS
    如果您想在两个用户之间找到最短路径,则应使用Dijkstra或类似工具。它们之间可能有多条路径,Dijkstra注意到,当它发现一条比以前发现的短的路径时

    要找到所有用户之间的最短路径,您必须使用类似的方法。这是一个很好的动态规划示例,实现起来非常简单。维基百科的伪代码是:

     1 /* Assume a function edgeCost(i,j) which returns the cost of the edge from i to j
     2    (infinity if there is none).
     3    Also assume that n is the number of vertices and edgeCost(i,i) = 0
     4 */
     5
     6 int path[][];
     7 /* A 2-dimensional matrix. At each step in the algorithm, path[i][j] is the shortest path
     8    from i to j using intermediate vertices (1..k−1).  Each path[i][j] is initialized to
     9    edgeCost(i,j) or infinity if there is no edge between i and j.
    10 */
    11
    12 procedure FloydWarshall ()
    13    for k := 1 to n
    14       for i := 1 to n
    15          for j := 1 to n
    16             path[i][j] = min ( path[i][j], path[i][k]+path[k][j] );
    

    我在前一段时间研究过这个问题,但无法为web应用程序提供有效的解决方案

    我的成绩是5级而不是6级

    请参见此处了解我的示例,这里有一个SQL和一个C#解决方案

    注意:您应该在谷歌上搜索“Dijkstra算法”,因为它是一种很好的算法,可以找到最短路径

    编辑:试试这个


    顺便说一句CLR方法执行得最快。

    第一个问题可以使用dijkstra算法解决。 第二种方法是使用DFS算法。 其他人已经说过了,只是想指出,对于这两个问题,最有效的解决方案不是一种算法

    伪代码可在以下位置找到:

    [维基百科][1]

    对于dijkstra和一个python for DFS,位于:


    对于任务2,您不会比广度优先搜索做得更好,除非通过缓存

    对于任务1,将您的解决方案应用于任务2。查找距离用户X不超过3个跃点的所有用户。当然,如果用户Y在该集合中,您就完成了。如果不是,则从用户Y开始执行广度优先搜索,并在到达任何已知可从X访问的用户时立即停止


    (如果在任务2中缓存一些关于如何到达每个用户的信息,那么当您在任务1中找到链接时,就可以很容易地重建精确的路径。)

    以下脚本是用sybase sql编写的。您可能需要根据数据库服务器进行一些小的修改

    问题1

    create table #connections (
        my_user  varchar(10)  not null  ,
        knows varchar(10)  not null  ,
            CONSTRAINT connection_pk PRIMARY KEY CLUSTERED ( my_user, knows)   
    ) 
    
    create table #traversed (id varchar(10) primary key)
    
    insert into #connections VALUES ('UserA','UserB')
    insert into #connections VALUES ('UserB','UserA')
    insert into #connections VALUES ('UserB','UserC')
    insert into #connections VALUES ('UserC','UserB')
    insert into #connections VALUES ('UserC','UserD')
    insert into #connections VALUES ('UserD','UserC')
    insert into #connections VALUES ('UserD','UserF')
    insert into #connections VALUES ('UserF','UserD')
    
    DECLARE @str_sql   varchar(200)               
    DECLARE @str_order varchar(60)
    
    declare @start varchar(10)
    set @start = ('UserD')
    declare @end varchar(10)
    set @end = ('UserA')
    
    if (@start >= @end)
        set @str_order = " order by id desc"
    else
        set @str_order = " order by id asc"
    
    
    INSERT INTO #traversed VALUES (@start)
    
    WHILE (select count(*) from #traversed where id = @end) = 0    
    BEGIN     
      INSERT INTO #traversed (id)    
      SELECT DISTINCT knows  
      FROM #connections e JOIN #traversed p ON p.id = e.my_user  
      WHERE e.knows NOT IN (SELECT id FROM #traversed)     
      AND e.knows between (select case when @start < @end then @start else @end end)  
          and (select case when @start < @end then @end  else @start end) 
    END
    
    set @str_sql = "SELECT #traversed.id FROM #traversed" + @str_order 
    exec (@str_sql)
    
    创建表#连接(
    我的用户varchar(10)不为空,
    知道varchar(10)不为null,
    约束连接主键群集(我的用户,知道)
    ) 
    创建表#遍历(id varchar(10)主键)
    在#连接值('UserA','UserB')中插入
    在#连接值('UserB','UserA')中插入
    在#连接值('UserB','UserC')中插入
    在#连接值('UserC','UserB')中插入
    在#连接值('UserC','UserD')中插入
    在#连接值('UserD','UserC')中插入
    在#连接值('UserD','UserF')中插入
    在#连接值('UserF','UserD')中插入
    声明@str_sql varchar(200)
    声明@str_order varchar(60)
    声明@start varchar(10)
    设置@start=('UserD')
    声明@end varchar(10)
    set@end=('UserA')
    如果(@start>=@end)
    set@str_order=“按id说明订购”
    其他的
    set@str\u order=“按id订购asc”
    插入到#遍历的值(@start)
    而(从#遍历中选择count(*),其中id=@end)=0
    开始
    插入#已遍历(id)
    选择不同的知识
    FROM#connections e JOIN#在p.id=e.my_用户上遍历p
    其中e.knows不在(从#遍历中选择id)
    和e.knows between(在@start<@end时选择case,然后在@start else@end时选择case)
    和(当@start<@end然后@end else@start end时选择case)
    结束
    设置@str_sql=“从#traversed”+@str_顺序中选择#traversed.id
    exec(@str_sql)
    
    问题2

    create table #connections (
        my_user  varchar(10)  not null  ,
        knows varchar(10)  not null  ,
            CONSTRAINT connection_pk PRIMARY KEY CLUSTERED ( my_user, knows)   
    ) 
    
    create table #traversed (id varchar(10) primary key)
    
    insert into #connections VALUES ('UserA','UserB')
    insert into #connections VALUES ('UserB','UserA')
    insert into #connections VALUES ('UserB','UserC')
    insert into #connections VALUES ('UserC','UserB')
    insert into #connections VALUES ('UserC','UserD')
    insert into #connections VALUES ('UserD','UserC')
    insert into #connections VALUES ('UserD','UserF')
    insert into #connections VALUES ('UserF','UserD')
    
    declare @start varchar(10)
    set @start = ('UserB')
    
    declare @higher_counter int
    declare @lower_counter int
    
    set @higher_counter = 0
    set @lower_counter = 0
    
    INSERT INTO #traversed VALUES (@start)
    
    WHILE (@higher_counter < 3)
    BEGIN     
      INSERT INTO #traversed (id)    
      SELECT DISTINCT knows  
      FROM #connections e JOIN #traversed p ON p.id = e.my_user  
      WHERE e.knows NOT IN (SELECT id FROM #traversed)     
      AND e.knows > @start 
    
      set @higher_counter = @higher_counter +1
    END  
    
    WHILE (@lower_counter < 3)
    BEGIN     
      INSERT INTO #traversed (id)    
      SELECT DISTINCT knows  
      FROM #connections e JOIN #traversed p ON p.id = e.my_user  
      WHERE e.knows NOT IN (SELECT id FROM #traversed)     
      AND e.knows < @start 
    
      set @lower_counter = @lower_counter +1
    END   
    
    SELECT #traversed.id FROM #traversed
    
    创建表#连接(
    我的用户varchar(10)不为空,
    知道varchar(10)不为null,
    约束连接主键群集(我的用户,知道)
    ) 
    创建表#遍历(id varchar(10)主键)
    在#连接值('UserA','UserB')中插入
    在#连接值('UserB','UserA')中插入
    在#连接值('UserB','UserC')中插入
    在#连接值('UserC','UserB')中插入
    在#连接值('UserC','UserD')中插入
    在#连接值('UserD','UserC')中插入
    在#连接值('UserD','UserF')中插入
    在#连接值('UserF','UserD')中插入
    声明@start varchar(10)
    设置@start=('UserB')
    声明@higher_计数器int
    声明@lower_计数器int
    设置@higher_计数器=0
    设置@lower_计数器=0
    插入到#遍历的值(@start)
    而(@higher_counter<3)
    开始
    插入#已遍历(id)
    选择不同的知识
    FROM#connections e JOIN#在p.id=e.my_用户上遍历p
    其中e.knows不在(从#遍历中选择id)
    e.knows>@start
    设置@higher\u计数器=@higher\u计数器+1
    结束
    而(@下_计数器<3)
    开始
    插入#已遍历(id)
    选择不同的知识
    FROM#connections e JOIN#在p.id=e.my_用户上遍历p
    其中e.knows不在(从#遍历中选择id)
    而e.知道开始
    设置@lower_计数器=@lower_计数器+1
    结束
    选择#遍历
    
    CREATE PROCEDURE FindPath (@UserX int, @UserY int)
    
    CREATE TABLE #SixDegrees(
      ConnectedUser int,
      Depth int,
      Path varchar(100),
      CONSTRAINT PK_SixDegrees PRIMARY KEY CLUSTERED (
        ConnectedUser
      )
    )
    
    DECLARE @Depth int,
            @PathFound varchar(100)
    SET @Depth = 0
    
    INSERT INTO #SixDegrees (@UserX, 0, CAST(@UserX as varchar))
    /*Next line just in case X & Y are the same*/
    SET @PathFound = (SELECT Path 
                      FROM #SixDegrees 
                      WHERE ConnectedUser = @UserY)
    
    WHILE @Depth < 6 AND @PathFound IS NULL
    BEGIN
      SET @Depth = @Depth + 1
      INSERT INTO #SixDegrees
      SELECT  k.KnowsPersonID, @Depth, (SELECT Path 
                                        FROM #SixDegrees 
                                        WHERE ConnectedUser = k.Link) + ',' + CAST(k.KnowsPersonID AS varchar)
      FROM (
          SELECT  MIN(ConnectedUser) Link, KnowsPersonID
          FROM    #SixDegrees
                  JOIN Connections ON
                    PersonID = ConnectedUser
          WHERE   Depth = @Depth
                  /*EDIT: Added the following*/
                  AND KnowsPersonID NOT IN (
                      SELECT  ConnectedUser
                      FROM    #SixDegrees
                      )
          GROUP BY KnowsPersonID
          ) k
    
      SET @PathFound = (SELECT Path 
                        FROM #SixDegrees 
                        WHERE ConnectedUser = @UserY)
    END
    
    IF @Path IS NULL
      PRINT 'No path found'
    ELSE
      PRINT @Path
    GO
    
    SELECT  DISTINCT KnowsPersonID
    FROM    Connections
    WHERE   PersonID IN (
        SELECT  DISTINCT KnowsPersonID
        FROM    Connections
        WHERE   PersonID IN (
            SELECT  KnowsPersonID
            FROM    Connections
            WHERE   PersonID = @UserX
            ) l1
        ) l2
    
    M(A, B)= A is directly connected to B
    
    (M(A, B))^P= A is connected to B within P links.
    
    CREATE TABLE `entity` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `type` enum('ACTOR','MOVIE','TV MOVIE','TV MINI','TV SERIES','VIDEO MOVIE','VIDEO GAME','VOICE','ARCHIVE') NOT NULL,
      `name` varchar(128) COLLATE utf8_unicode_ci NOT NULL,
      PRIMARY KEY (`id`),
      UNIQUE KEY `type` (`type`,`name`) USING BTREE
    ) ENGINE=InnoDB;
    
    CREATE TABLE `link` (
      `rel_id` int(11) NOT NULL AUTO_INCREMENT,
      `link_from` int(11) NOT NULL,
      `link_to` int(11) NOT NULL,
      PRIMARY KEY (`rel_id`),
      KEY `link_from` (`link_from`,`link_to`),
      KEY `link_to` (`link_to`)
    ) ENGINE=InnoDB;
    
    CREATE TABLE movie_graph (
      latch SMALLINT UNSIGNED NULL,
      origid BIGINT UNSIGNED NULL,
      destid BIGINT UNSIGNED NULL,
      weight DOUBLE NULL,
      seq BIGINT UNSIGNED NULL,
      linkid BIGINT UNSIGNED NULL,
      KEY (latch, origid, destid) USING HASH,
      KEY (latch, destid, origid) USING HASH
    ) ENGINE=OQGRAPH 
      data_table='link' origid='link_from' destid='link_to';
    
    MariaDB [imdb]> SELECT
                 -> GROUP_CONCAT(name ORDER BY seq SEPARATOR ' -> ') AS path
                 -> FROM imdb_graph JOIN entity ON (id=linkid)
                 -> WHERE latch=1
                 -> AND origid=(SELECT a.id FROM entity a
                 ->             WHERE name='Kevin Bacon')
                 -> AND destid=(SELECT b.id FROM entity b
                                WHERE name='N!xau')\G
    *************************** 1. row ***************************
    path: Kevin Bacon -> The 45th Annual Golden Globe Awards (1988) -> Richard Attenborough -> In Darkest Hollywood: Cinema and Apartheid (1993) -> N!xau
    1 row in set (10 min 6.55 sec)