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);
}
}