Подтвердить что ты не робот

Как найти связи между пользователями, чтобы я мог создать замкнутый круг друзей?

Привет всем,

Прежде всего, я не пытаюсь создать социальную сеть, facebook достаточно большой! (Комиксы) Я выбрал этот вопрос в качестве примера, потому что он точно соответствует тому, что я пытаюсь сделать.

Представьте, что в MySQL есть таблица users и таблица user_connections с запросами друзей. Если это так, это будет примерно так:

Users Table:

userid  username
1       John
2       Amalia
3       Stewie
4       Stuart
5       Ron
6       Harry
7       Joseph
8       Tiago
9       Anselmo
10      Maria


User Connections Table:

userid_request  userid_accepted
2               3
7               2
3               4
7               8
5               6
4               5
8               9
4               7
9               10
6               1
10              7
1               2

Теперь я хочу найти круги между друзьями и создать массив структуры и поместить этот круг в базу данных (ни один из массивов не может включать одних и тех же друзей что другой уже).

Return Example:

    // First Circle of Friends
    Circleid => 1
    CircleStructure => Array(
        1 => 2,
        2 => 3,
        3 => 4,
        4 => 5,
        5 => 6,
        6 => 1,
    )
    // Second Circle of Friends
    Circleid => 2
    CircleStructure => Array(
        7 => 8,
        8 => 9,
        9 => 10,
        10 => 7,
    )

Я пытаюсь придумать алгоритм, чтобы сделать это, но я думаю, что это займет много времени обработки, потому что он будет случайным образом искать базу данных, пока она не "закрывает" круг.

PS: Минимальная длина структуры окружности - 3 соединения, а предел равен 100 (поэтому демон не выполняет поиск по всей базе данных)

EDIT:

Я думаю о чем-то вроде этого:

function browse_user($userget='random',$users_history=array()){

    $user = user::get($userget);

    $users_history[] = $user['userid'];

    $connections = user::connection::getByUser($user['userid']);
    foreach($connections as $connection){
        $userid = ($connection['userid_request']!=$user['userid']) ? $connection['userid_request'] : $connection['userid_accepted'];

        // Start the circle array
        if(in_array($userid,$users_history)) return array($user['userid'] => $userid);

        $res = browse_user($userid, $users_history);

        if($res!==false){
            // Continue the circle array
            return $res + array($user['userid'] => $userid);
        }
    }

  return false;
}

while(true){

    $res = browse_user();

    // Yuppy, friend circle found!
    if($res!==false){
            user::circle::create($res);
    }

    // Start from scratch again!
}

Проблема с этой функцией заключается в том, что она может выполнять поиск по всей базе данных без поиска самого большого круга или наилучшего соответствия.

4b9b3361

Ответ 1

Ответ Альфреда Годой очень полезен, так как обработка должна выполняться постепенно. Я попытаюсь сформулировать некоторые конкретные детали.

Вместо того, чтобы "люди" были "друзьями", я расскажу о "узлах", связанных "краями". Неверно, что циклы растут от 3 до 4 (или n до n + 1) узлов. Несколько ребер, не образующих цикл, могут быть закрыты новым ребром и сформировать новый цикл.

Итак, вместо этого (или также) в качестве списка циклов мы должны вести список реберных цепей. например при условии, что эта таблица соединений:

userid_request  userid_accepted
2               7
3               7
3               8

Затем таблица Chain должна содержать следующее:

chainid  start  end  length
1        2      3    2
1        2      8    3
2        7      8    2

Эта структура позволяет записывать и извлекать цепочки любой длины и задавать два конца цепочки, следовать ей с использованием id и длины. Это занимает пространство... Но это стоимость памяти для поиска циклов в графике для вас. Его можно упростить, в зависимости от того, что вы хотите сделать; подробнее.

Кстати, я предполагаю, что граф неориентирован - другими словами, ребро от node к другому идет в обоих направлениях. В соединении node с наименьшим идентификатором всегда будет "запрос" и самый высокий идентификатор на "принятом" конце.

Когда добавлен новый node, вы хотите сохранить таблицу цепочек и посмотреть, закрывает ли новый node цепные циклы. Он включает в себя пару запросов. Я даю вам один.

Предположим, что в таблице Connection добавлена ​​новая запись:

userid_request  userid_accepted
@new_request    @new_accepted

Вы можете использовать запрос для выбора любого такого цикла. Вы уже знаете о новой ссылке и ее двух узлах, поэтому вы ищете цепочку, которая ее закрывает:

select chainid, length
from chain
where (start = @new_request and end = @new_accepted)
or (end = @new_request and start = @new_accepted)

(обратите внимание, что поскольку график не направлен, вам нужно искать две конфигурации циклов).

Чтобы поддерживать таблицу цепочек, каждый раз, когда добавляется ребро:

  • найдите существующие цепи, затянутые node
  • найдите новые цепочки длины 2.

И затем, когда узлы удаляются, вы должны вынуть соответствующие цепочки и циклы - таблица цепочек будет достаточно большой, как есть.

Ответ 2

Выполнение такого рода операций над большими наборами данных почти всегда является большой работой для одной партии. При работе с большими объемами данных вы должны постоянно создавать индексы. Выполняйте кругный тест каждый раз, когда "пользователь" добавляется или становится "другом" с другим "пользователем" и сохраняет полученные круги в таблице индексов. Затем, когда новый "пользователь" подписывается или становится "другом" с другим "пользователем", вы должны использовать свою базу данных индексов для поиска новых кругов на основе старых.

Edit:

Я очень восторженно относился к этой проблеме, поэтому я сделал класс доказательств концептуального класса идей о том, как решить эту проблему. Я поместил код в GitHub по адресу: https://github.com/alfreddatakillen/Six-Degrees-of-Separation (было бы немного беспорядочно размещать его здесь).

Кажется, он работает очень хорошо. Однако я не тестировал его с огромным количеством данных. Я уверен, что вам придется оптимизировать это, так как в основном это атака грубой силы, сохраняющая каждый шаг на пути... (Если вы оптимизируете или используете код, я был бы счастлив, если вы нажмете изменения к GitHub - я мог бы использовать его в будущем проекте самостоятельно.)

: -)

Ответ 3

Вы пытаетесь сделать что-то вроде Six Degrees of Seperation

Это сначала выглядит как NP-трудная проблема, потому что для вычисления каждого круга вы должны пересечь каждый node в графе, чтобы найти все возможные круги. Я считаю, что нет простого способа сделать это, и, скорее всего, нет возможности сделать это в режиме реального времени.

Итак, вы должны, вероятно, выполнять такие вычисления в заданиях, кэшируя результаты для эффективности.

Сверху моей головы, если мы рассмотрим эту проблему как график с каждым пользователем как node, и каждое отношение как край с весом 1, мы можем использовать поиск по ширине, чтобы найти от пользователя 1 все пути с максимальным весом 6. На каждом обходе node (до общего веса 6) мы ищем, если начальный node и текущий node разделяют ребро. Если это так, мы закрываем круг. Он начнется с круга из 2 и развернется вперед. Если мы достигнем веса 6, а окончательный node не разделяет ребро со стартовым node, мы отбрасываем круг.

Вероятно, в сценарии реального случая, чтобы ускорить процесс, вы можете попытаться рассчитать круги до меньшего общего веса (скажем, 3) для каждого пользователя, а затем попытаться присоединиться к кругам, чтобы получить до 6.

Ответ 4

Просто идея создания кругов. Эта функция будет загружаться каждый раз, когда пользователь пытается просмотреть круги:

  • Найдите пользователя (# 1) с наибольшим количеством друзей, которые еще не в кругах.
  • Завершите всех своих друзей, которые ищут одного (# 2), у которого больше всего друзей, и еще не в кругах.
  • Завершить всех новых друзей друзей друзей таким же образом, пока цикл не закончится, ограничение 20?
  • проверить, найден ли последний пользователь (# 20), дружит с 1-м пользователем
  • если да, завершите круг
  • Если нет, проверьте, дружат ли друзья (# 19) с 1-м пользователем.
  • Если нет, проверьте, дружили ли друзья (# 18) с 1-м пользователем.
  • продолжайте циклически перемещать пользователей, пока кто-то не станет друзьями с 1-м пользователем.
  • круг завершен

Нечетный способ этого, я знаю

Ответ 5

Я думаю, что для этого типа JOB вам следует переслать базу данных NOSQL, которые хороши в том, что вы ищете, например, в базе данных диаграмм, например Neo4j.