一、简介 :
这是我的本科生毕业设计,我毕业于浙江工业大学,因为当时想考东南大学的研究生,所以和老师交流了(我的导师是香港科技大学的博士后,年轻的大牛!!),她帮我选了这个课题,并打算把这个作为我研究生复试时所讲的课题,但是遗
憾,我的英语没有过线啊,但是没有后悔选择这个课题,并真的很感谢我的导师,从中学到了很多,说实话毕业设计也折腾了三个月啊,大学完美了额!
二、课题背景
首先要理解什么是小型世界,什么叫交互可视化。小型世界:通俗的将就是讲现实世界中的事物进行抽象,使得整个效果简单,就比如一个图中有红点代表人物,绿代表建筑,然后连线代表之间的关系。交互可视化是指我们将小型世界以某种可视化的形式表达出来,实现可视化,并且能够交互,就是能做一些操作啊。那我做的这个到底是什么呢?其实就是微博互助,你关注我,我关注他,他关注她,你又关注她,那我们之间就有着很很复杂的关系,然后我要通过图的形式表示出这些复杂的关系,而且要比较清楚的表示,并能对其进行交互。比如用500个用户,并平均每个人关系度是5,那意味着最后的图上有500个点,而且有2500条线,这可以想象,根本看不清楚的。所以我毕业设计的任务有两个:1)合理得到这500个点的位置,因为清晰显示和它们的坐标也有关系。2)通过某种方式,能够实现交互。我的毕业设计里是用力模型实现这500个点的合理布局,然后用鱼眼技术实现交互的。我是根据我的外文Frank van Ham的Interactive Visualization of Small World Graphs,如果需哟可以去我的百度网盘下载(我的ID是653970803)。
链接:http://pan.baidu.com/share/link?shareid=669630734&uk=2903577078
关于什么是力模型,什么是鱼眼技术,自己最好百度下。其中里面要用到力模型、梯度算法(最速下降法)、抽象视图算法、圆柱体平移算法和一些小的算法,其中主要是前面几个算法。
三、数据准备
3.1数据来源
我用的数据是微博用户数据,你也可以自己去找,或者用我给的,下面给出两个可用数据的链接:
链接1:http://pan.baidu.com/share/link?shareid=893390342&uk=2903577078
链接2:http://pan.baidu.com/share/link?shareid=895602539&uk=2903577078
3.2 数据处理
对于数据处理,需要额外写一个程序去将数据读入,然后进行处理,必须要处理的,因为对于我的微博数据,他都是一个人关注了4、500个人,然后很多之间都没有关联的,这样会导致菊花形的图形,所以必须将一些人的关注进行删除,使得每个人的关注度大约差不多都是5左右,而且这500人之间相互关注的关系比较合理,使得最后是这样的形状,当然这是200个点左右的,500个根本看不清的啊。
上面是最后200个点初始化的结果,但是我们仅仅上面的图就需要做很多工作,比如将最初一个文件里的数据读入,然后进行删选。我是这么做的,将得到每一个用户的ID,并记录这个用户关注人的ID,为每一个用户创建一个text文件,保持在user***.text中,(如:1.7496e+009 1.72349e+009 1.77366e+009 1.94275e+009 2.43512e+009),第一个表示这个user的ID,后面的表示他关注的人的ID,但是提示微博的ID不能用int类型数据保存的,可以用float的而且在text中要注意一定不能有字符,不然跑程序的时候读数据会出现死循环(有些时候text会包含隐藏的字符,你看不到,但是就是存在字符),且文件最后不要有空格,这些都是我碰到的,这些都是删选用户的工作。然后还要为每个用户节点初始化坐标,首先这需要设计数据结构体。我都是根据我的数据结构体和数据存储来设计函数的,如果你的和不一样,那就不能用了,当然我的只是做一个参考,我的设计如下:
//用来存放节点的各个坐标值
struct Point{
double x; //x轴坐标点
double y; //y轴坐标点
double z; //z轴坐标点
};
//用于存放用户节点信息的结构体
struct User{
float ID; //存放该用户ID
Point point; //表示节点坐标
vector<float> PayAtten; //表示这个用户所关注的用户节点ID
int number; //记录该节点所关联的节点个数
};
void redInit(){ //调用initUsers函数为每个用户新建用户节点
initUers(path01);
initUers(path02);
initUers(path03);
***********
}
//全局变量
vector<User> users; //存放文件中所有用户节点
int allCount=0; //allCount用于标记本系统一共有创建了多少个用户节点
//初始化各个节点的坐标和相互关系
void initUsers(char* path){
fstream fileName;
User user; //临时存放从文件读取出来的用户节点信息
int i=0,j,m,n;
float getNum, payAttent;
fileName.open(path,ios::in); //以二进制的形式,打开文件
if (!fileName.is_open()) {
cout<<"read files error!,please check it"<<endl;
exit(0);
}
fileName>>getNum; //得到用户节点的ID,即文件的一个数字
//遍历所有的用户,是否已经有该用户
for (m=0;m<allCount;++m){
if(getNum==users[m].ID)
break;
++i;
}
if (i==allCount){ //该节点不存在,则新建
user.ID=getNum;
user.number=0;
user.PayAtten.clear(); //清除临时变量上次存储的信息
user.point.x=rand()%divisor; //随机产生x轴坐标
user.point.y=rand()%divisor; //随机产生y轴坐标
user.point.z=rand()%divisor; //随机产生z轴坐标
users.push_back(user); //将该新用户放入向量中
++allCount; //用户总数加一
}
while(!fileName.eof()){ //继续读取文件剩下的数字,此后读出的都是前面那个用户
//所关联的节点ID,直到结束
fileName>>payAttent;
users[i].number++; //该节点所关联节点个数加一
users[i].PayAtten.push_back(payAttent); //并记录它的ID
j=0;
for (n=0;n<allCount;++n){ //遍历该ID,查看是否已经存在该节点,若无,则新建
if(payAttent==users[n].ID)
break;
++j;
}
if(j==allCount){
user.ID=payAttent;
user.number=0;
user.PayAtten.clear();
user.point.x=rand()%divisor;
user.point.y=rand()%divisor;
user.point.z=rand()%divisor;
users.push_back(user);
++allCount;
}
}
fileName.close();
}
四、算法介绍
4.1 力模型
假设一个无向图G =(V,E),其中V是节点的集合,E是边的集合。我们通过边Ei j连接节点i和j,使得Ei j的权重为1.力定向算法模型图作为一个物理系统,且尝试着在所有节点中找到总能量最小的节点。图中一个节点上的力取决于它的事件产生的引力和其他节点边缘产生的斥力。两个节点之间距离功能即是力。
Fi = Σf (pi j) *(ˉpi j)−Σg(pi j) *(ˉpi j)
ei j∈E i≠j
我们可以在一个顶点上记录下pi j= |pi− pj| and ¯pi j= (pi− pj)/pi j。这些决定了引力和斥力的函数f(x)和g(x)通常都遵循物理现象。对于f(x)而言,通常是万向弹簧法则。
f(x) = A · (x − x0)
其中X0是标志着零能量的弹簧长度。且斥力受到静电场作用遵循平方反比定律。
G(x)=B/X2
其中,A和B是分别代表引力和斥力强度的常量。系统总势能表示为:
P=A/2 ·∑ (pi j − x0)2 − B · ∑1/Pij
ei j∈E i≠j
在最优节点系统中势能最小。由于高维度的问题,要直接找到全局最小通常是不可行的,因此不得不使用迭代方法。一种常见的方法就是通过最陡下降法来优化.从随机配置开始,节点受到外部力而移动,这样就使得最后每个节点的力都为零,或者换句话说,达到了最小能量的状态。通常情况这只是一个局部最小,但能让人对此视觉感到满意了。
传统的力模型最小化了边缘长度中的总方差,且试图使得所有边的长度都和P0相近。在统一节点分布中,对于小半径密集图形(如小型世界图表)的结果,是因为所有节点都保持与邻节点相近。相反,那些节点位置非常紧密的和松散的力模型可能会效果更好。Noack是派生于属性包含r-PolyLog能量模型的派生类。在这些模型中总势能定义为:
P = ∑ ( pi j− x0)r − ∑ ln(pi j)
ei j∈E i≠j
注意:3-PolyLog有一个等价于[11]所使用的能量函数。然而在这方面最有趣是与力函数相关的1-PolyLog模型。
f (x) = 1
和
G(x)=1/x
Noack证明了通过使用1-PolyLog(配音为LinLog)模型我们获得了一种嵌入,在这种嵌入中两个节点群集之间的距离和他们耦合度是成反比的。这种在两个群集之间的耦合Cl和 C2 是他们之间的连通性的一种估量且被定义为:E(C1,C2)/|C1||C2|,其中E(C1,C2)表明所有Cl和C2中连接节点总边数。一开始使用恒引力可能看起来会有点奇怪,但是它使得在群集之间的边缘收缩时,仍然允许群集内部边缘增长。在[24]中获得了一些人工生成的具有高聚类指标图表。
对于使用LinLog能源模型的真实世界图表的实验结果表明:它开始从一个随机的最初节点配置时比传统方法更容易陷入局部最小值。虽然这在一般情况下很难证明,但是我们推理出LinLog中由一条长边到图表的其他边连接的外表节点力比2-PolyLog节点中的大大减少。这就更容易从其他节点到排斥力相结合从未来防止该节点达到它的最佳位置。一个更可行的方法是一开始创建一个有力模型的可接受的布局,而不再使用随机布局作为起点。我们从总步骤M中使用一个r-PolyLog力模型的步骤m(0 m<M ? 1)的优化过程来归纳这一点.这使得更容易为排斥力相结合从其他节点来防止该节点到达它的最佳位置。更好的解决方案是不使用一个随机布局作为起点,但首先创建一个可接受的布局与另一个力模型。应用这个我们使用r-PolyLog力模型步骤总共的m的步骤(0≤m < m−1)的优化过程
其中
r = rstart if 0 ≤ m < t1M
r = αm rstart+(1− αm)*1 if t1M ≤ m < t2M
r = 1 if t2≤ m < M
且0 ≤ t1< t2< 1,rstart ≥ 2和αm=(t2M-m)/(t2M-t1M)
这使得在相同数量的迭代次数,比那些从随机初始位置所获得的布局拥有更小的能量。
我设计实现的代码,仅供参考啊,不建议cope!!!