KCore问题
• KCore
一个图G的 KCore 是G的子图
这个子图的每个顶点的度≥K
• 计算方法
如果顶点的度小于k,从图中删除该顶点,然后给邻居发送消息
顶点收到消息后,得知被删掉的邻居顶点,更新自己的度
• 输入:无向图 (有成对的有向边)
• 输出: KCor 子图中的所有顶点
顶点
顶点
…
graphlite的安装和配制,其他博客有很好的介绍,这里就不讲了。
本文主要讲Kcore的编程实现。每遍历一次所有结点就是一个超步。
大家直接看代码,我会在关键部分添加注释。在注释中,尽力的表述了每个函数的用途。希望读者可以理解整个过程,而不是简单的照搬抄袭。
/**
* @file PageRankVertex.cc
* @author Songjie Niu, Shimin Chen
* @version 0.1
*
* @section LICENSE
*
* Copyright 2016 Shimin Chen (chensm@ict.ac.cn) and
* Songjie Niu (niusongjie@ict.ac.cn)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* @section DESCRIPTION
*
* This file implements the PageRank algorithm using graphlite API.
* Modified by Zhengyang Peng to accomplish Homework 2
*/
#include <stdio.h>
#include <string.h>
#include <math.h>
#include "GraphLite.h"
#define VERTEX_CLASS_NAME(name) KCore##name
#define EPS 1e-6
int K = 0; //初始化K的值,子图中每个点的度必须大于K。
class VERTEX_CLASS_NAME(InputFormatter): public InputFormatter {
public:
int64_t getVertexNum() {
unsigned long long n;
sscanf(m_ptotal_vertex_line, "%lld", &n);
m_total_vertex= n;
return m_total_vertex;
}
int64_t getEdgeNum() {
unsigned long long n;
sscanf(m_ptotal_edge_line, "%lld", &n);
m_total_edge= n;
return m_total_edge;
}
int getVertexValueSize() {
m_n_value_size = sizeof(double);
return m_n_value_size;
}
int getEdgeValueSize() {
m_e_value_size = sizeof(double);
return m_e_value_size;
}
int getMessageValueSize() {
m_m_value_size = sizeof(double);
return m_m_value_size;
}
void loadGraph() {
unsigned long long last_vertex;
unsigned long long from;
unsigned long long to;
double weight = 0;
double value = 1;
int outdegree = 0;
const char *line= getEdgeLine();
// Note: modify this if an edge weight is to be read
// modify the 'weight' variable
sscanf(line, "%lld %lld", &from, &to);
addEdge(from, to, &weight);
last_vertex = from;
++outdegree;
for (int64_t i = 1; i < m_total_edge; ++i) {
line= getEdgeLine();
// Note: modify this if an edge weight is to be read
// modify the 'weight' variable
sscanf(line, "%lld %lld", &from, &to);
if (last_vertex != from) {
value=outdegree;
addVertex(last_vertex, &value, outdegree);
last_vertex = from;
outdegree = 1;
} else {
++outdegree;
}
addEdge(from, to, &weight);
}
value=outdegree;
addVertex(last_vertex, &value, outdegree); //加载图后,可以传3个参数到函数addVertex中,为了方便,此问题,直接把value的值赋为outdegree。
}
};
class VERTEX_CLASS_NAME(OutputFormatter): public OutputFormatter {
public:
void writeResult() { //此函数为输出结果函数。
int64_t vid;
double value;
char s[1024];
for (ResultIterator r_iter; ! r_iter.done(); r_iter.next() ) {
r_iter.getIdValue(vid, &value);
if(value!=0){ //如果value值不为0,也就是度不为了0,度小于K的,都被赋值为0了。
int n = sprintf(s, "%lld\n", (unsigned long long)vid);
writeNextResLine(s, n);}
}
}
};
// An aggregator that records a double value tom compute sum
class VERTEX_CLASS_NAME(Aggregator): public Aggregator<double> {
public:
void init() {
m_global = 0;
m_local = 0;
}
void* getGlobal() {
return &m_global;
}
void setGlobal(const void* p) {
m_global = * (double *)p;
}
void* getLocal() {
return &m_local;
}
void merge(const void* p) {
m_global += * (double *)p;
}
void accumulate(const void* p) {
m_local += * (double *)p;
}
};
class VERTEX_CLASS_NAME(): public Vertex <double, double, double> {
public:
void compute(MessageIterator* pmsgs) {
double val;
printf("%d ",int(getValue())); //printf可以输出到日志,同学们可以使用printf观察各个变量的变化。
if (getSuperstep() == 0) { //每遍历一次所有结点,就是一个超步。
printf("K=%d ",K);
}
else
{
if (getSuperstep() > 2 ){
double global_val = * (double *)getAggrGlobal(0);
if (global_val < 0.00000001) { //如果此超步结束,总度不再变化,结束超步。
voteToHalt();
return;
}
}
if (getSuperstep() >= 1&&getValue()!=0){
double sum = 0;
for ( ; ! pmsgs->done(); pmsgs->next() ) {
sum += pmsgs->getValue(); //将邻居结点发送的消息的值相加,结果即所有邻接结点删除后,当前结点总共损失的度。
}
if(getValue()-sum < K){ //如果更新度后的结点的度小于K。
sendMessageToAllNeighbors(1); //向邻居结点广播消息,通知邻居,你们的度需要减少1。
double acc = fabs(sum); //acc为此结点的度的变化值。
accumulateAggr(0, &acc); //此函数记录此超步中每个结点的度的变化的和。
val=0; //此结点已被删除,度(value值)变为0。
* mutableValue() = val; //* mutableValue()赋值后,可以改变结点的value值,在此问题中即是出度。
}
else{ //如果更新度后的结点的度不小于K,则不需要向邻居结点广播消息。
val = getValue()-sum;
double acc = fabs(getValue() - val);
accumulateAggr(0, &acc);
* mutableValue() = val; //更新自己的出度。
}
}
}
const int64_t n = getOutEdgeIterator().size();
}
};
class VERTEX_CLASS_NAME(Graph): public Graph {
public:
VERTEX_CLASS_NAME(Aggregator)* aggregator;
public:
// argv[0]: KCore.so
// argv[1]: <input path>
// argv[2]: <output path>
// argv[3]: <K>
void init(int argc, char* argv[]) {
setNumHosts(5);
setHost(0, "localhost", 1411);
setHost(1, "localhost", 1421);
setHost(2, "localhost", 1431);
setHost(3, "localhost", 1441);
setHost(4, "localhost", 1451);
if (argc <= 3) {
printf ("Usage: %s <input path> <output path> <K>\n", argv[0]);
exit(1);
}
m_pin_path = argv[1];
m_pout_path = argv[2];
K= atoi(argv[3]); //读取参数K。
aggregator = new VERTEX_CLASS_NAME(Aggregator)[1];
regNumAggr(1);
regAggr(0, &aggregator[0]);
}
void term() {
delete[] aggregator;
}
};
/* STOP: do not change the code below. */
extern "C" Graph* create_graph() {
Graph* pgraph = new VERTEX_CLASS_NAME(Graph);
pgraph->m_pin_formatter = new VERTEX_CLASS_NAME(InputFormatter);
pgraph->m_pout_formatter = new VERTEX_CLASS_NAME(OutputFormatter);
pgraph->m_pver_base = new VERTEX_CLASS_NAME();
return pgraph;
}
extern "C" void destroy_graph(Graph* pobject) {
delete ( VERTEX_CLASS_NAME()* )(pobject->m_pver_base);
delete ( VERTEX_CLASS_NAME(OutputFormatter)* )(pobject->m_pout_formatter);
delete ( VERTEX_CLASS_NAME(InputFormatter)* )(pobject->m_pin_formatter);
delete ( VERTEX_CLASS_NAME(Graph)* )pobject;
}