복붙노트

[SQL] 도전, 어떻게 분리의 여섯 개 학위 알고리즘을 구현하는 방법?

SQL

도전, 어떻게 분리의 여섯 개 학위 알고리즘을 구현하는 방법?

사용자 A-사용자 B-UserC에게-userd에-UserF

로 연결된 사용자 '-'서로를 알아.

그리고 나는이 두 작업을위한 알고리즘이 필요합니다 :

효율적인 솔루션이 있습니까?

편집하다

내 목적은 옳고 그름을 증명하기 위해,하지만 필요한 경우 결과를 실시간으로 계산하지 않는 것입니다.

게다가, 나는 대부분의 표현 방법은 코드, 심지어 의사들이라고 생각합니다.

편집 다시

그것은 SQL 솔루션을해야합니다, 그래서 나는, 작업의 종류는 데이터베이스 내부에서 수행해야한다고 결정했습니다!

해결법

  1. ==============================

    1.그래프에 의해 사용자의리스트를 나타내는

    그래프에 의해 사용자의리스트를 나타내는

    이 질문은 너무 밀접하게 동일한 알고리즘이 실제로 모두를 해결하는 것이 관련이 있습니다. 당신은 "다 익스트라의 무게 1을 가진 모든 가장자리와 알고리즘,"또는 호출 할 수 있습니다 "폭 우선 검색 할 수 있습니다."

    기본적으로, 첫 번째 노드에서 시작, 모든 친척을 방문; 다음 이들 각각에 대한 각각의 최단 경로 (그들에게 최단 경로 + 그냥 통과 가장자리), 반복 기록, 방문으로 모두 표시합니다. 후 정지 문제 # 2> 최단 경로가 정지 후, 문제 # 1 (3) 목적지에 도달했습니다.

    분리의 여섯 정도의 빠른 O (n)이 알고리즘은 아마 1 단계 멀리 UserX 및 UserY에서 모든 사용자의 집합을 발견하고, 두 집합의 교집합을 찾는 것입니다. 없음 경우, 사용자에게 UserX 및 교차에서 2 단계를 추가; 다음 사용자에게 UserY 및 교차에서 2 단계를 추가; 등 3까지.

    각 사람이 100 친구의 평균이있는 경우 다 익스트라의 알고리즘에 대한 1,010 억 반대로, 이것은 최대 2,020,200에 대한 사용자를 찾고 요구할 수 있습니다. 자주 두 친구는 서로 친구 때문에 실제로,이 숫자는 훨씬 작은 것이다.

    이것은 실제로 작동합니다 (그 지금까지 언급 한의) 분리의 여섯 학위를 해결하는 유일한 방법입니다.

  2. ==============================

    2.그래프 알고리즘은 여기에 당신을 도울 수 있습니다. 그들에 대해 배우는 것은 너무 재미!

    그래프 알고리즘은 여기에 당신을 도울 수 있습니다. 그들에 대해 배우는 것은 너무 재미!

    두 사용자 사이의 최단 경로를 찾으려면 익스트라 또는 이와 유사한 사용해야합니다. 이 그들 사이에 여러 경로, 그리고 다 익스트라는 전에 발견 된 그 다른 것보다 단락 경로를 발견했을 때 지적을 담당 할 수있다.

    모든 사용자 사이의 최단 경로를 찾으려면 플로이드 - Warshall 같은 것을 사용해야합니다. 그것은 동적 프로그래밍의 좋은 예입니다 구현하기 매우 간단합니다. 위키 백과에서 의사 코드는 다음과 같습니다

     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] );
    
  3. ==============================

    3.나는 당신이 이미 가지고있는 것들과 상당히 다른 제안이있다. 당신은 SQL 데이터베이스를 고수해야하고 당신이 어떤 자바를 모르는 경우이 제안은별로 효과가 없습니다.

    나는 당신이 이미 가지고있는 것들과 상당히 다른 제안이있다. 당신은 SQL 데이터베이스를 고수해야하고 당신이 어떤 자바를 모르는 경우이 제안은별로 효과가 없습니다.

    내가 저장하기 위해 SQL 데이터베이스를 사용하는 동안 그래프는 다른 접근 방식은 그래프 문제를 위해 특별히 만들어진 것입니다 솔루션을 사용하는 것, 작동 할 것이라고 제안 있도록 문제는, 특히 그래프 문제입니다.

    Neo4j 프로젝트를 함께 그래프 알고리즘의 많은 함께 작동합니다, 디스크 기반 그래프 데이터베이스를 제공합니다. 인용문:

    자신의 위키에 Neo4j 사용의 적절한 예는 IMDB 데이터를 사용하여도-의 분리 웹 응용 프로그램을 보여줍니다. 예는 배우 케빈 베이컨 간의 최단 경로의 계산을 도시한다.

    이 그래프가 나타냅니다 도메인을 모델링에 대해 많은 이야기 때문에 나는이 예제를 좋아한다. 당신 같은 것들에 대해 생각 도메인 보장하지만 모델링 :

    다른 게시물에서 언급 한 바와 같이, 이러한 익스트라 플로이드 Warshall 또는 BFS 같은 최단 경로를 계산하기위한 알고리즘들이있다. 이러한 모든 Neo4j에 구현 된 몇 가지 예는 여기에 제공됩니다.

  4. ==============================

    4.소스 데이터를 가정하면 테이블에 : 연결 : (PersonID, KnowsPersonID)

    소스 데이터를 가정하면 테이블에 : 연결 : (PersonID, KnowsPersonID)

    1)는 폭 첫 번째 방법을 사용해야합니다. 좋은 성능에 대한 귀하의 가능성 때문에 문제의 기하 급수적 인 특성으로 제한된다 (이론적으로 만 6도 필요한 이유이 지수 특성은 있지만 : D)을. 당신이 검색의 깊이를 제한합니다. 당신이 선택하든 SQL의 맛 당신은 아마 순수한 세트 기반 솔루션 반대의 반복 확장을 사용하여 더 나을 수 있습니다.

    다음은 마이크로 소프트의 T-SQL을 사용하는 기본적인 방법이 될 것입니다 :

    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
    

    편집 : 위의 솔루션에서 내가 원래 #SixDegrees 임시 테이블에 이미 사용자를 제외하는 것을 잊었다.

    2) 3의 깊이에 항상 루프 위에 작은 비틀기는 관심있는 모든 사용자를 포함 #SixDegrees 당신을 떠날 것이다.

    그러나 다음과 같은 순수 세트 기반 솔루션은보다 효율적으로해야한다 :

    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
    
  5. ==============================

    5.다음 스크립트는베이스 SQL로 작성됩니다. 당신은 당신의 DB 서버에 따라 약간의 수정을 통해 이동해야 할 수도 있습니다.

    다음 스크립트는베이스 SQL로 작성됩니다. 당신은 당신의 DB 서버에 따라 약간의 수정을 통해 이동해야 할 수도 있습니다.

    문제 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)
    

    문제 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
    
  6. ==============================

    6.나는 얼마 전에 이것을 한 번 봐 가지고 및 웹 애플리케이션을위한 효율적인 솔루션을 가지고 올 수 없었다.

    나는 얼마 전에 이것을 한 번 봐 가지고 및 웹 애플리케이션을위한 효율적인 솔루션을 가지고 올 수 없었다.

    나는 5 단계보다는 육으로 돌아가 셨습니다

    는 SQL과 C #을 솔루션이 여기 내 구글 그룹 게시물을 참조하십시오.

    참고 :이 최단 경로를 찾는 좋은 알고리즘으로 알려져있다 당신이 "다 익스트라의 알고리즘"에 대한 구글해야한다고.

    편집 :이 링크를 시도하십시오

    BTW CLR을 방법은 빠른 실행.

  7. ==============================

    7.첫 번째 질문은 다 익스트라의 알고리즘을 사용하여 해결할 수 있습니다. DFS 알고리즘을 사용하여 두 번째. 이것은 이미 단지 두 문제에 대한 가장 효율적인 솔루션을 하나의 알고리즘에 사용할 수 없음을 지적하고 싶었, 다른 사람에 의해 말했다되었습니다.

    첫 번째 질문은 다 익스트라의 알고리즘을 사용하여 해결할 수 있습니다. DFS 알고리즘을 사용하여 두 번째. 이것은 이미 단지 두 문제에 대한 가장 효율적인 솔루션을 하나의 알고리즘에 사용할 수 없음을 지적하고 싶었, 다른 사람에 의해 말했다되었습니다.

    의사에서 찾을 수 있습니다 :

    [위키] [1]

    에서 DFS에 대한 파이썬에서 익스트라과 하나 :

    http://en.wikipedia.org/wiki/Depth-first_search

  8. ==============================

    8.작업 2의 경우, 캐싱에 의해 어쩌면 제외하고, 폭 우선 검색보다 더 잘하지 않을거야.

    작업 2의 경우, 캐싱에 의해 어쩌면 제외하고, 폭 우선 검색보다 더 잘하지 않을거야.

    작업 1의 경우, 사용자 Y가 세트에, 당신이 완료하는 경우 작업에 대한 솔루션 멀리 물론 사용자 X.에서 더 이상 3 홉 이상 모든 사용자를 찾습니다 적용되지 않습니다. 그렇지 않다면, 당신은 모든 사용자에 도달 당신이 이미 알고있는 X.에서 연결할 수 있는지 곧 같은 폭 우선 사용자 Y부터 검색 및 정지를 할

    (당신은 당신이 작업이 동안 각 사용자에 도달하는 방법에 대한 약간의 정보를 캐시 경우 작업 1에서 링크를 찾을 때, 그것은 정확한 경로를 재구성하기 쉬운 것입니다)

  9. ==============================

    9.(이 답변 Djikstra의 동일합니다. 그것은 기본적으로 구현 세부 사항입니다.)

    (이 답변 Djikstra의 동일합니다. 그것은 기본적으로 구현 세부 사항입니다.)

    # 2에 대답하기 위해, 당신은 정도 P. 연결성을 결정하는 부울 행렬 곱셈을 사용할 수 있습니다

    어디 부울 행렬 M을 가정 :

    M(A, B)= A is directly connected to B
    

    그때

    (M(A, B))^P= A is connected to B within P links.
    

    행렬의 곱셈은 곱셈과 OR 추가 및 사용해야합니다 :

    당신은 이전에 허위 항목에 대한 곱셈을 수행뿐만 아니라 행렬은 대칭 실현하여 많은입니다을 최적화 할 수 있습니다. 즉, 독자에게 연습으로 남아 있습니다.

  10. ==============================

    10.MariaDB가 OQGraph를 사용하여이 작업을 수행 할 수있는 합리적 효율적인 방법은 실제로 있습니다.

    MariaDB가 OQGraph를 사용하여이 작업을 수행 할 수있는 합리적 효율적인 방법은 실제로 있습니다.

    데이터를 가정하면 두 테이블에 포함되어 있습니다 :

    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;
    

    OQGraph 가상 테이블은 다음과 같이 선언 할 수 있습니다 :

    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)
    

    30,000,000 가장자리와 약 370 만 노드의 그래프입니다. 테이블 노트북 하드 디스크에 512 메가 바이트 버퍼 풀에 대해 구성된 3.5GB와 InnoDB에 대해입니다. 약 1600 만 보조 키를 읽습니다. 감기는, 데이터가 버퍼 풀에 미리로드하지 않습니다. 2010 맥북 프로.

    테이블이 버퍼 풀에 보관 될 수 있다면 물론, 그것은 훨씬 빨리이다.

    https://www.slideshare.net/AntonyTCurtis/oqgraph-scale2013-17332168/21 :이 예에서 온다

  11. ==============================

    11.그것을 구글과는 많이 찾을 수 있습니다.

    그것을 구글과는 많이 찾을 수 있습니다.

    나는 당신이 (적어도 내가 아직했습니다) 의사 코드를 찾을 수 있습니다 의심한다. 여기에 흥미로운 중 일부는 읽고 있습니다 :

    새 컴퓨터 알고리즘에 설명 "분리의 여섯 학위" CU의 컴퓨터 과학자를 설명하는 데 도움이 방법 작품 '분리의 여섯 명 정도' SO - 어떻게 프로그래밍 개념 "분리의 여섯 학위"를 증명할 수 있습니까?

  12. ==============================

    12.나는 SQL 솔루션에만 응답하고 있습니다. 이는 대규모 데이터 세트에 대한 "효율적"하지 않을 수 있지만 3 단계 만 거치면 모든 경로를 제공합니다. 등 테이블 "KNOW", "KNOW_1"은 모두 동일하며, 두 필드 P1 및 P2를 갖는다. 이는 1) P1 P2는 2를 알고있는 경우에만 기입) P2는 P1을 알고있다. P1 및 P2에서의 데이터는 각각의 사용자에 대응하는 임의의 스트링 일 수있다.

    나는 SQL 솔루션에만 응답하고 있습니다. 이는 대규모 데이터 세트에 대한 "효율적"하지 않을 수 있지만 3 단계 만 거치면 모든 경로를 제공합니다. 등 테이블 "KNOW", "KNOW_1"은 모두 동일하며, 두 필드 P1 및 P2를 갖는다. 이는 1) P1 P2는 2를 알고있는 경우에만 기입) P2는 P1을 알고있다. P1 및 P2에서의 데이터는 각각의 사용자에 대응하는 임의의 스트링 일 수있다.

    A는 B가 C 사이클없이 D 알고 알고 알고 곳 Acccess SQL 쿼리 (예를 들어, A는 B가 C가 알고 알고 알고) 모든 경로를 산출한다. 당신은 여전히 ​​중복 (ABCD = dcba) 제거하기 위해 필요하지만, 두 번째 단계에서 쉽게 할 수 있어야합니다. 싸이클은 어디 제표에 이전 사람들의 반복을 방지하여 제거된다.

    SELECT Know.P1, Know.P2, Know_1.P2, Know_2.P2

    FROM (INNER 알고 Know.P2 = Know_1.P1 통해 알려 AS Know_1 가입)

    INNER는 Know_1.P2 = Know_2.P1 ON 알고있는 Know_2 가입

    WHERE (((Know_1.P2) <> [알고]. [P1]) AND ((Know_2.P2) <> [알고]. [P1]을 (Know_2.P2) <> [알고]. [P2]) )

    ORDER BY Know.P1, Know.P2, Know_1.P2, Know_2.P2;

    같은 이전의 솔루션 우아하지만 작업 확인을 보인다 없습니다. 우리는 제약 프로그래밍과 비슷한 일을 몇 가지 경험과 SQL 프로세스가 빠르게 특정 문제에 대한 것으로 나타났습니다.

  13. from https://stackoverflow.com/questions/2076715/challenge-how-to-implement-an-algorithm-for-six-degree-of-separation by cc-by-sa and MIT license