PageRank的Java代码实现及图形操作(带详细注释)!
从Git上找到的一段代码,修改了一些东西,加了一些自己遇到的问题的注释以及增加了阻尼系数变量。
废话不说直接上代码:
package cn.wx.PageRank;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
/**
* 求取PageRank值的代码 用户可以通过文件选择器选择读入指定的文件进行计算,文件的第一行为初始PR值,
*
* 余下的行为矩阵,其中矩阵行元素间用“,”隔开,行与行之间无需分隔符。
*
* 也可以通过选择随机来输入矩阵的维数、初始PR值以及矩阵的复杂度(矩阵中有多少个1)生成矩阵进行计算。
*/
public class newPageRank {
static JTextField jtf;// 显示文件路径的框
static JTextArea jta;// 显示计算结果的框
static JFrame jf;// 显示的窗体
static JFrame jf1;// 显示输入矩阵的窗体
static float[] data;// 存数输入数据的数组
static int time = 20;// 指定的运行次数
static float alpha = 0.85f; // 阻尼系数d或称为alpha
public static void main(String[] args) {
newPageRank mine = new newPageRank();
mine.showUI();
}
/**
* 展示计算结果的界面
*/
public void showUI() {
// 初始化界面以及按钮、输入框等
jf = new JFrame();
jf.setTitle("PR值计算~~");
jf.setSize(500, 500);
jf.setDefaultCloseOperation(3);
jf.setResizable(false);
jf.setLayout(null);
jf.setLocationRelativeTo(null);
JButton btn = new JButton("打开");
btn.setActionCommand("Open");
btn.setBounds(20, 20, 80, 30);
JButton btn1 = new JButton("随机");
btn1.setActionCommand("Input");
btn1.setBounds(110, 20, 80, 30);
jtf = new JTextField();
jtf.setBounds(200, 20, 260, 30);
jtf.setEditable(true);
jtf.setActionCommand("Over");
jta = new JTextArea();
jta.setEditable(false);
jta.setLineWrap(true);
jta.setAutoscrolls(true);
JScrollPane jsp = new JScrollPane(jta);// 创建滚动条
jsp.setBounds(20, 70, 460, 380);
// 添加监听器
ActionListener al = new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (e.getActionCommand().equals("Open")) {
// 弹出选择框
jta.setText("");// 每次重新选择之前清空输入框
JFileChooser fc = new JFileChooser();
int value = fc.showOpenDialog(null);
fc.setFileSelectionMode(JFileChooser.FILES_ONLY);
if (value == JFileChooser.CANCEL_OPTION) {
return;
}
File file = fc.getSelectedFile();
jtf.setText(file.getAbsolutePath());
readFile(file);
}
if (e.getActionCommand().equals("Input")) {
init();// 初始化窗体,获取值
}
if (e.getActionCommand().equals("Over")) {
jta.setText("");
// 输入路径结束,执行以下操作
File file = new File(jtf.getText());
readFile(file);
}
}
};
jf.add(btn);
jf.add(btn1);
jf.add(jtf);
jf.add(jsp);
btn.addActionListener(al);
btn1.addActionListener(al);
jtf.addActionListener(al);
jf.setVisible(true);
}
/**
* 读取相应路径文件的方法 定义文件中第一行是节点对应的初始化PR值,
* 接下来是N*N的矩阵 同行数据之间均通过","分隔,不同行无需分隔符
*
* @param file
* :文件
*/
public static void readFile(File file) {
try {
// 创建读文件的流
FileReader fr = new FileReader(file);
BufferedReader br = new BufferedReader(fr);
String PR = br.readLine();
String[] sPR = PR.split(",");// 将第一行PR值存入对应数组
// 将读取的PR值从String转为float
float[] initPR = new float[sPR.length];
for (int i = 0; i < sPR.length; i++) {
initPR[i] = Float.parseFloat(sPR[i]);
}
jta.append("各页面的初始PR值………………………………………………………………" + "\n");
for (int i = 0; i < initPR.length; i++) {
jta.append(initPR[i] + "\t");// 打印出PR值
}
jta.append("\n");
// 读取并创建N*N矩阵,在这里使用float,精度为小数点后7位
float[][] array = new float[initPR.length][initPR.length];
int count = 0;// 用计数器控制行的变化
// 当行数小于指定数N时进行读取
while (count < initPR.length) {
String value = br.readLine();// 读取一行的值
String[] rowV = value.split(",");
// 对第count行第i列赋值
for (int i = 0; i < initPR.length; i++) {
array[count][i] = Float.parseFloat(rowV[i]);
}
count++;
}
jta.append("原始矩阵为………………………………………………………" + "\n");
print(array);// 打印出矩阵
// 读取完成后,调用计算的函数对矩阵进行计算
long start = System.currentTimeMillis();
calculate(initPR, array);
long end = System.currentTimeMillis();
jta.append("total time is:" + (end - start));
br.close(); // 关闭流
}
catch (FileNotFoundException e) {
javax.swing.JOptionPane.showMessageDialog(jf, "文件未找到,请重试!");
}
catch (IOException e) {
javax.swing.JOptionPane.showMessageDialog(jf, "文件读取有误,请重试!");
}
}
/**
* 打印矩阵的方法
*
* @param s
*/
static void print(float[][] f) {
for (int i = 0; i < f.length; i++) {
for (int j = 0; j < f.length; j++) {
jta.append(f[i][j] + "\t");
}
jta.append("\n");
}
}
/**
* 计算矩阵特征值以及特征向量的方法
*
* @param initPR
* :存储初始的PR值的float数组
* @param array
* :存储初始矩阵的float二维数组
*/
static void calculate(float[] initPR, float[][] array) {
array = changeRC(array);// 转置矩阵
jta.append("转置后的矩阵…………………………………………………………");
jta.append("\n");
print(array);
array = randomization(array);// 概率化矩阵
jta.append("\n");
jta.append("概率化后的矩阵………………………………………………………………");
jta.append("\n");
print(array);
for (int i = 1; i <= time; i++) {
initPR = formular(initPR, array);
jta.append("\n");
jta.append("经过" + i + "次计算,PR值为………………………………………………");
jta.append("\n");
for (int j = 0; j < initPR.length; j++) {
jta.append("PR[" + j + "]的值为:" + initPR[j]);
jta.append("\n");
}
}
jta.repaint();
}
/**
* 转置矩阵的方法
*
* @param array
* :需要转置的矩阵
* @return:将转置后的矩阵返回
*/
static float[][] changeRC(float[][] array) {
float temp = 0;// 临时变量,用于交换
for (int i = 0; i < array.length; i++) {
for (int j = 0; j < array.length; j++) {
// 对矩阵的每一行、列遍历,如果下标i<j就倒换
if (i < j) {
temp = array[i][j];
array[i][j] = array[j][i];
array[j][i] = temp;
}
}
}
return array;
}
/**
* 概率化矩阵的方法
*
* @param array
* :需要概率化的矩阵
* @return:返回已经概率化的矩阵
*/
static float[][] randomization(float[][] array) {
int count = 0;// 控制列变化的计数器
int size = 0;// 计量列中连接为1的元素数量的计数器
while (count < array.length) {
size = 0;// 每次计数之前要清零
for (int i = 0; i < array.length; i++) {
if (array[i][count] == 1) {
size++;// 先统计出数量为多少
}
}
jta.append("the size of " + count + " is:" + size + " ");
for (int i = 0; i < array.length; i++) {
if (array[i][count] == 1) {
array[i][count] = (float) 1 / size;
}
}
count++;
}
return array;
}
/**
* 通过公式计算PR值
*
* @param initPR
* :初始PR值
* @param array
* :经处理的矩阵
* @return:返回计算后的PR值
*/
static float[] formular(float[] initPR, float[][] array) {
for (int i = 0; i < initPR.length; i++) {
initPR[i] = 0;// 重算一个PR之前,将其清零
int count = 0;// 计量每一行0元素个数
for (int j = 0; j < initPR.length; j++) {
if (i != j && array[i][j] != 0) {
initPR[i] = (float) (initPR[i] + ((initPR[j] * array[i][j]) * alpha + (1.0f-alpha)));
} else
count++;
}
if (count == initPR.length) {
initPR[i] = (float) 0.15;
}
}
return initPR;
}
static void input(float[] data) {
float size = data[0];
float complex = data[1];
float[] initPR = new float[data.length - 2];
for (int i = 0; i < initPR.length; i++) {
initPR[i] = data[i + 2];
}
float[][] array = new float[(int) size][(int) size];// 创建输入大小的矩阵
// 初始化矩阵元素为0
for (int i = 0; i < array.length; i++) {
for (int j = 0; j < array.length; j++) {
array[i][j] = 0;
}
}
int num = (int) (complex / 100 * array.length * array.length);// 需要被赋为1的元素个数
// 为矩阵按照复杂程度赋值
while (num > 0) {
int row = (int) (Math.random() * (array.length));
int column = (int) (Math.random() * (array.length));// 随机生成行列数
if (row != column && array[row][column] != 1) {
array[row][column] = 1;
num--;
}
}
// 打印随机生成的PR和矩阵
jta.append("各页面的初始PR值………………………………………………………………" + "\n");
for (int i = 0; i < initPR.length; i++) {
jta.append(initPR[i] + "\t");// 打印出PR值
}
jta.append("\n");
jta.append("生成的矩阵…………………………………………………………………………" + "\n");
print(array);
// 将计算好的初始PR数组和矩阵传入计算
long start = System.currentTimeMillis();
calculate(initPR, array);
long end = System.currentTimeMillis();
jta.append("total time is:" + (end - start));
}
/**
* 初始化一个输入值的窗体
*/
static void init() {
jf1 = new JFrame("生成随机矩阵");
jf1.setSize(300, 160);
jf1.setResizable(false);
jf1.setDefaultCloseOperation(2);
jf1.setLocationRelativeTo(null);
jf1.setLayout(new FlowLayout());
JLabel lb = new JLabel("矩阵维度:");
final JTextField jtf = new JTextField(15);
JLabel lb1 = new JLabel("初始PR值:");
final JTextField jtf1 = new JTextField(15);
JLabel lb2 = new JLabel(" 复杂度: ");
final JTextField jtf2 = new JTextField(15);
jtf2.setActionCommand("sure");
JLabel lb3 = new JLabel("% ");
JButton btn = new JButton("确认");
btn.setActionCommand("sure");
JButton btn1 = new JButton("取消");
btn1.setActionCommand("cancle");
ActionListener al = new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (e.getActionCommand().equals("sure")) {
// 点击确认后,存储输入值
float size = Integer.parseInt(jtf.getText());// 取值
String[] s = jtf1.getText().split(",");
float complex = Integer.parseInt(jtf2.getText());
// 初始化数组,将输入框中的值存入数组
data = new float[s.length + 2];
// 先存入矩阵维度,再放入复杂度,最后存入各个初始值
data[0] = size;
data[1] = complex;
for (int i = 0; i < s.length; i++) {
data[2 + i] = Float.parseFloat(s[i]);
}
jf1.dispose();
input(data);
}
if (e.getActionCommand().equals("cancle")) {
jf1.dispose();
}
}
};
jf1.add(lb);
jf1.add(jtf);
jf1.add(lb1);
jf1.add(jtf1);
jf1.add(lb2);
jf1.add(jtf2);
jf1.add(lb3);
jf1.add(btn);
jf1.add(btn1);
jtf2.addActionListener(al);
btn.addActionListener(al);
btn1.addActionListener(al);
jf1.setVisible(true);
}
}