¿Cómo "descubrir" amigos perdidos durante muchos años? ¡El código te lo dice!

Autor | Cooper Song

Editor en jefe | Wu Xingling

Producido | Programa Life (ID: coder_life)

Recientemente, hay muchos insomnios. Accidentalmente descubrí que las personas que pueden estar interesadas en abrir la lista de seguidores / admiradores de otros en Weibo. Cuando hice clic, todos eran estudiantes de primaria, secundaria y preparatoria. Comencé a pensar en el pasado. Así es como se implementa el algoritmo.

 

¿Qué estructura de datos se utiliza para seguir la información de los fanáticos?

 

¡Por supuesto, usa fotos! Te seguí unilateralmente y me convertí en tu fanático, pero aún no eres mi fanático, por lo que la relación atención / fanático debe almacenarse utilizando un gráfico dirigido (Gráfico dirigido). Gráfico acíclico (DAG para abreviar), porque la siguiente imagen puede mostrar que te seguí, tú lo seguiste a él, y él siguió la relación triangular como yo.

Después de determinar la estructura de datos de los gráficos dirigidos, también debe determinar qué método de almacenamiento usar. Hay dos métodos de almacenamiento principales para los gráficos, la matriz de adyacencia y la tabla de adyacencia. Dado que no hay mucha preocupación entre los usuarios de Weibo u otras redes sociales, el uso del almacenamiento de matriz de adyacencia consumirá mucho espacio, por lo que utilizamos la lista de adyacencia para almacenar, puede colocar a las personas afectadas por un usuario en una lista vinculada Cuando se conecta al usuario, los ventiladores también se conectan al usuario en una lista vinculada. Por supuesto, la lista vinculada también se puede cambiar a una matriz dinámica con expansión automática de capacidad.

//结构体定义

struct node

{

    vector<int> follows;

    vector<int> followers;

    vector<int> unionSet;

};

//所有用户

node user[MAX_USER_NUM];

La matriz dinámica que sigue almacena a las personas que sigue un usuario; los seguidores almacena a los fanáticos de un usuario; unionSet almacena la unión del conjunto siguiente y el conjunto de seguidores, lo que indica que las personas que tienen una relación de "conocimiento" con un usuario se pueden encontrar en el Cuando los usuarios siguen / dejan de seguir a otros u otros siguen / dejan de seguir al usuario, se insertan / eliminan automáticamente. También puede usar el siguiente conjunto y el conjunto de seguidores para encontrar la intersección cuando se usan.

¿Cómo expresar esta relación?

 

Aquí creemos que mi atención y mis admiradores tienen una relación de "conocimiento" o "interés" conmigo. Por lo tanto, podemos tomar una unión de los seguidores y seguidores y ponerlos en un nuevo conjunto de relaciones.

Existen dos tipos de algoritmo de unión: uno es O (m * n), que es más violento y más fácil de entender, y el otro es O (m + n), que utiliza un algoritmo hash.

Tome el algoritmo de unión 1 (violento)

vector<int> getUnion(vector<int> follows,vector<int> followers)

{

    vector<int> ret=followers;

    int len1=follows.size();

    int len2=followers.size();

    for(int i=0;i<len1;i++)

    {

        //用来标记关注的人是不是已经出现在粉丝中

        bool found=false;

        for(int j=0;j<len2;j++)

        {

            if(follows[i]==followers[j])

            {

                //关注的人已经出现在粉丝中了

                flag=true;

                break;

            }

        }

        if(!flag)

        {

            //关注的人没有出现在粉丝中

            ret.push_back(follows[i]);

        }

    }

    return ret;

}

Primero coloque el siguiente conjunto (seguidores) en la unión, y luego atraviese el conjunto de seguidores (admiradores), verifique para cada ventilador si ha aparecido en el siguiente conjunto, si no, luego agregue el ventilador a la unión.

Tome el algoritmo de unión 2 (algoritmo hash)

vector<int> getUnion(vector<int> follows,vector<int> followers)

{

    vector<int> ret;

    int len1=follows.size();

    int len2=followers.size();

    //哈希表

    unordered_map<int,bool> mp;

    for(int i=0;i<len1;i++)

    {

        mp[follows[i]]=true;

        ret.push_back(follows[i]);

    }

    for(int i=0;i<len2;i++)

    {

        if(!mp[followers[i]])

        {

            ret.push_back(followers[i]);

        }

    }

    return ret;

}

Primero recorra el siguiente conjunto (personas que siguen), coloque todos los elementos del siguiente conjunto en una unión, y use una tabla hash para marcar si ya existe en el conjunto de unión, y luego recorra el conjunto de seguidores (fanáticos), verifique cada ventilador. Si el valor de la esperanza es verdadero. Si es verdadero, significa que el elemento ya existe en el conjunto de la unión. Si es falso, significa que no existe tal elemento en el conjunto de la unión. Luego agregue el elemento a la unión y finalmente regrese a la unión.

¿Y comprobar si es factible?

 

Si hay un total de MAX_USER_NUM usuarios, abrimos una matriz f [MAX_USER_NUM] e inicializamos el valor del elemento i-ésimo en i.

int f[MAX_USER_NUM];

for(int i=0;i<MAX_USER_NUM;i++)

{

    f[i]=i;

}

Si el usuario a siguió al usuario b o el usuario b siguió al usuario a, haga lo siguiente.

f[findFather(a)]=findFather(b);

El significado del código anterior es establecer el nodo padre del nodo padre de a como el nodo padre de b. La llamada función findFather es una función que devuelve el nodo padre.

findFather(int x)

{

    if(x==f[x])

    {

        return x;

    }

    return f[x]=findFather(f[x]);

}

En el conjunto de búsqueda paralela, no existe una relación jerárquica entre el nodo primario y el nodo secundario. Si el nodo primario del nodo primario de a se convierte en el nodo primario de b, se llama a findFather (a) para encontrar el nodo primario de un En ese momento, su nodo padre también se convierte en el nodo padre de b, es decir, los nodos padres del usuario a, el usuario b y el usuario c son consistentes en este momento.

Para juzgar si dos amigos de todos los usuarios pueden ser conocidos por sus amigos, simplemente haga el siguiente juicio.

bool haveConnection(int a,int b)

{

    bool result;

    if(findFather(a)==findFather(b))

    {

        result=true;

    }

    else

    {

        result=false;

    }

    return result;

}

De esta manera, los usuarios que llaman a la función anterior en el conjunto de unión de los seguidores y admiradores de mi usuario de observación actual en unionSet devuelven verdadero, y obtengo una lista de personas en las que podría estar interesado.

vector<int> getInterestedList(vector<int> unionSet,int myId)

{

    vector<int> ret;

    int len=unionSet.size();

    for(int i=0;i<len;i++)

    {

        if(haveConnection(myId,unionSet[i])

        {

            ret.push_back(unionSet[i]);

        }

    }

    return ret;

}

La esencia del conjunto de verificación paralela es una relación conectada. El concepto de un gráfico conectado es que cualquiera de los dos vértices de un gráfico puede comenzar de un vértice a otro vértice, por lo que cuando los nodos principales de todos los vértices del conjunto de verificación paralela son el mismo vértice , El gráfico es un gráfico conectado y tiene un solo componente conectado; y si hay más de un nodo padre para todos los vértices, el gráfico no es un gráfico conectado, hay múltiples componentes conectados, hay varios nodos conectados y hay varios componentes conectados. Los amigos interesados ​​en el algoritmo de búsqueda paralela y la teoría de gráficos pueden verificar la información por sí mismos.

Y verificar la colección puede determinar con precisión si existe una conexión entre los dos usuarios, pero la realidad que debemos enfrentar es una conjetura matemática muy famosa en el campo de las redes sociales: la teoría del espacio de seis grados, también conocida como la teoría de la segmentación de seis grados. El contenido es que no habrá más de 6 personas entre usted y cualquier extraño.

Por ejemplo, solo necesita 6 intermediarios para conocer al Presidente de los Estados Unidos. Esto suena ridículo, pero tiene sentido. Por ejemplo, tengo una buena comprensión de los amigos y familiares para hacer negocios en el extranjero, debido a los embajadores extranjeros se reunieron para hacer negocios, mientras que el jefe embajador extranjero probablemente se dio la mano con los subordinados del presidente o por teléfono, y el presidente del entendimiento bajo el presidente . Por lo tanto, si yo, mis parientes y amigos, los embajadores extranjeros en China y los subordinados del presidente y los subordinados del presidente y el presidente se registraron en la misma red social, aunque no establecí directamente una relación de preocupación y atención con el presidente , a través del algoritmo sindical Se puede saber que hay una relación entre mí y el presidente .

 

Estadísticas de ruta

 

Si se usa junto con el conjunto de verificación, primero llame y verifique la función findFather para saber si hay una ruta entre los dos usuarios. Si hay una ruta, puede usar el algoritmo de búsqueda profunda + iterativo para contar la ruta de un usuario a otro usuario Número, cuanto mayor es el número de rutas, más inextricablemente hay una conexión entre los dos usuarios.

Como se muestra en la figura anterior, soy el usuario 1, estoy viendo la lista de amigos del usuario 6, el usuario 6 tiene el amigo 3 y el amigo 5, el amigo 3 ya es mi amigo, contacté al amigo 5, hay 6 rutas, respectivamente:

1-> 2-> 5

1-> 2-> 3-> 5

1-> 2-> 3-> 6-> 5

1-> 3-> 2-> 5

1-> 3-> 5

1-> 3-> 6-> 5

Esto muestra que debo haberme vinculado inextricablemente con el usuario 5.

Sin embargo, la complejidad temporal de este algoritmo depende del grado de todos los vértices y del número de usuarios intermedios que pasan entre dos usuarios.Si hay demasiados usuarios intermedios, la pila puede explotar y hacer que el sistema se bloquee cuando sea recursivo. .

Además de los problemas de la complejidad del tiempo y la explosión de la pila, si ve la esencia del problema, el algoritmo no puede eliminar la influencia de seis grados de espacio. Por ejemplo, si agrego otro usuario 7 a la lista de amigos del usuario 5, el usuario 7 solo tiene un amigo del usuario 5, por lo que el número de rutas al usuario 7 es el mismo que el número de rutas al usuario 5, pero el usuario 1 llega al usuario 7 al menos. Para pasar a dos intermediarios, la relación no es mucho.

 

Opción factible uno

 

Desde el punto de vista actual, la solución más factible es comparar y obtener la intersección de la lista de amigos, y clasificarlos de acuerdo con el número de elementos en la intersección. Después de observar una variedad de situaciones, descubrí que las personas que podrían estar interesadas en recomendarme tienen una línea a continuación que indica que XX también lo siguió, y XXX es mi amigo. Resulta que el algoritmo de recomendación de intereses de Weibo no es tan alto, debería ser el mismo que el amigo común de QQ. La única diferencia con respecto a QQ es que Weibo tiene dos colecciones, una para seguidores y otra para fanáticos.

También hay dos algoritmos para encontrar intersecciones, que son similares a la búsqueda de uniones: uno es la fuerza bruta y la complejidad temporal es O (m * n); el otro es hash y la complejidad temporal es O (m + n).

Algoritmo de intersección 1 (violento)

vector<int> getInterp(vector<int> myFriends,vector<int> yourFriends)

{

    int len1=myFriends.size();

    int len2=yourFriends.size();

    for(int i=0;i<len1;i++)

    {

        bool flag=false;

        for(int j=0;j<len2;j++)

        {

            if(myFriends[i]==yourFriends[j])

            {

                flag=true;

                break;

            }

        }

        if(flag)

        {

            ret.push_back(myFriends[i]);

        }

    }

    return ret;

}

Tome el algoritmo de intersección 2 (algoritmo hash)

vector<int> getInterp(vector<int> myFriends,vector<int> yourFriends)

{

    vector<int> ret;

    int len1=myFriends.size();

    int len2=yourFriends.size();

    unordered_map<int,bool> mp;

    for(int i=0;i<len1;i++)

    {

        mp[myFriends[i]]=true;

    }

    for(int i=0;i<len2;i++)

    {

        if(mp[yourFriends[i]])

        {

            ret.push_back(yourFriends[i]);

        }

    }

    return ret;

}

Atraer a personas que puedan estar interesadas

vector<int> getInterestedList(vector<int> myFriends,vector<int> yourFriends)

{

    vector<int> ret;

    int len=yourFriends.size();

    for(int i=0;i<len;i++)

    {

        vector<int> temp=getInterp(user[yourFriends[i]].unionSet,myFriends);

        if(temp.size()>0)

        {

            ret.push_back(yourFriends[i]);

        }

    }

    return ret;

}

Opción factible dos

 

Después de atravesar a todos los amigos de mis amigos, usando una tabla hash para contar la cantidad de ocurrencias, ordenando por la cantidad de ocurrencias de mayor a menor, y luego excluyendo a los usuarios que ya son mis amigos, encontré a mis "personas probablemente conocidas" y nos conseguí ¿Cuántos amigos comunes hay?

//判断某个id是不是我的好友

bool isFriend(int id,vector<int> myFriends)

{

    int len=myFriends.size();

    for(int i=0;i<len;i++)

    {

        if(id==myFriends[i])

        {

            return true;

        }

    }

    return false;

}

//获取可能认识的人

vector<int> getPossibleFriends(vector<int> myFriends)

{

    vector<int> ret;

    int len1=myFriends.size();

    map<int,int> mp;

    //遍历我所有的朋友

    for(int i=0;i<len1;i++)

    {

        //遍历朋友的朋友

        int len2=user[myFriends[i]].unionSet.size();

        for(int j=0;j<len2;j++)

        {

            mp[user[myFriends[i]].unionSet[j]]++;

        }

    }

    //剔除我的好友

    for(map<int,int>::iterator it=mp.end()-1;it>=mp.begin();it--)

    {

        pair<int.int> temp=*it;

        if(!check(temp.second,myFriends))

        {

            ret.push_back(temp.first);

        }

    }

    return ret;

}

De hecho, todo en el mundo está relacionado y conectado. La conexión no es un producto de la era de Internet, pero la aparición de las redes sociales ha hecho que nuestra conexión sea más estrecha. Si hay algún error, espero que sea más indulgente y señale. El autor también quisiera escuchar cómo los programadores de Weibo se dan cuenta de la función de "personas probablemente interesadas".

Recientemente, también es la temporada de oro, plata y cuatro entrevistas. Todavía me gusta hacer este tipo de preguntas durante la entrevista, porque puede reflejar la capacidad del candidato para analizar el problema, resolver el problema y satisfacer las necesidades específicas. En el uso diario del software, si ve funciones interesantes y piensa más en su implementación, no hay daño, incluso si hay una discrepancia entre la implementación de la conjetura y la implementación real.

【FIN】


Más recomendaciones interesantes

¿Qué pasa con Platón, que procesa mil millones de cálculos de gráficos de nodos cada minuto?

Detrás de cada lección en línea, se expone la tecnología hardcore negra

base de datos de agitar durante 40 años, un análisis en profundidad PostgreSQL, NewSQL Evolución

¿Los hackers usan el aprendizaje automático para entrar en pánico? ¡Ven y conoce estas 7 nuevas formas de robar datos!

☞Súper detallado! ¡Un artículo te dice cómo SparkStreaming integra Kafka! El código adjunto se puede practicar

En los idiomas Mover Libra, y 10 líneas de código para lograr su primer contrato inteligentes

Cada "observación" que pides, me lo tomo en serio

Artículos originales publicados en 1984 · Más de 40,000 elogios · 18.44 millones de visitas

Supongo que te gusta

Origin blog.csdn.net/csdnnews/article/details/105697455
Recomendado
Clasificación