Skip to content

Commit a1bf70c

Browse files
author
linyiqun
committed
ACO蚁群算法工具类
ACO蚁群算法工具类
1 parent dce1e86 commit a1bf70c

File tree

1 file changed

+341
-0
lines changed

1 file changed

+341
-0
lines changed

Others/DataMining_ACO/ACOTool.java

Lines changed: 341 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,341 @@
1+
package DataMining_ACO;
2+
3+
import java.io.BufferedReader;
4+
import java.io.File;
5+
import java.io.FileReader;
6+
import java.io.IOException;
7+
import java.text.MessageFormat;
8+
import java.util.ArrayList;
9+
import java.util.Collections;
10+
import java.util.HashMap;
11+
import java.util.Map;
12+
import java.util.Random;
13+
14+
/**
15+
* 蚁群算法工具类
16+
*
17+
* @author lyq
18+
*
19+
*/
20+
public class ACOTool {
21+
// 输入数据类型
22+
public static final int INPUT_CITY_NAME = 1;
23+
public static final int INPUT_CITY_DIS = 2;
24+
25+
// 城市间距离邻接矩阵
26+
public static double[][] disMatrix;
27+
// 当前时间
28+
public static int currentTime;
29+
30+
// 测试数据地址
31+
private String filePath;
32+
// 蚂蚁数量
33+
private int antNum;
34+
// 控制参数
35+
private double alpha;
36+
private double beita;
37+
private double p;
38+
private double Q;
39+
// 随机数产生器
40+
private Random random;
41+
// 城市名称集合,这里为了方便,将城市用数字表示
42+
private ArrayList<String> totalCitys;
43+
// 所有的蚂蚁集合
44+
private ArrayList<Ant> totalAnts;
45+
// 城市间的信息素浓度矩阵,随着时间的增多而减少
46+
private double[][] pheromoneMatrix;
47+
// 目标的最短路径,顺序为从集合的前部往后挪动
48+
private ArrayList<String> bestPath;
49+
// 信息素矩阵存储图,key采用的格式(i,j,t)->value
50+
private Map<String, Double> pheromoneTimeMap;
51+
52+
public ACOTool(String filePath, int antNum, double alpha, double beita,
53+
double p, double Q) {
54+
this.filePath = filePath;
55+
this.antNum = antNum;
56+
this.alpha = alpha;
57+
this.beita = beita;
58+
this.p = p;
59+
this.Q = Q;
60+
this.currentTime = 0;
61+
62+
readDataFile();
63+
}
64+
65+
/**
66+
* 从文件中读取数据
67+
*/
68+
private void readDataFile() {
69+
File file = new File(filePath);
70+
ArrayList<String[]> dataArray = new ArrayList<String[]>();
71+
72+
try {
73+
BufferedReader in = new BufferedReader(new FileReader(file));
74+
String str;
75+
String[] tempArray;
76+
while ((str = in.readLine()) != null) {
77+
tempArray = str.split(" ");
78+
dataArray.add(tempArray);
79+
}
80+
in.close();
81+
} catch (IOException e) {
82+
e.getStackTrace();
83+
}
84+
85+
int flag = -1;
86+
int src = 0;
87+
int des = 0;
88+
int size = 0;
89+
// 进行城市名称种数的统计
90+
this.totalCitys = new ArrayList<>();
91+
for (String[] array : dataArray) {
92+
if (array[0].equals("#") && totalCitys.size() == 0) {
93+
flag = INPUT_CITY_NAME;
94+
95+
continue;
96+
} else if (array[0].equals("#") && totalCitys.size() > 0) {
97+
size = totalCitys.size();
98+
// 初始化距离矩阵
99+
this.disMatrix = new double[size + 1][size + 1];
100+
this.pheromoneMatrix = new double[size + 1][size + 1];
101+
102+
// 初始值-1代表此对应位置无值
103+
for (int i = 0; i < size; i++) {
104+
for (int j = 0; j < size; j++) {
105+
this.disMatrix[i][j] = -1;
106+
this.pheromoneMatrix[i][j] = -1;
107+
}
108+
}
109+
110+
flag = INPUT_CITY_DIS;
111+
continue;
112+
}
113+
114+
if (flag == INPUT_CITY_NAME) {
115+
this.totalCitys.add(array[0]);
116+
} else {
117+
src = Integer.parseInt(array[0]);
118+
des = Integer.parseInt(array[1]);
119+
120+
this.disMatrix[src][des] = Double.parseDouble(array[2]);
121+
this.disMatrix[des][src] = Double.parseDouble(array[2]);
122+
}
123+
}
124+
}
125+
126+
/**
127+
* 计算从蚂蚁城市i到j的概率
128+
*
129+
* @param cityI
130+
* 城市I
131+
* @param cityJ
132+
* 城市J
133+
* @param currentTime
134+
* 当前时间
135+
* @return
136+
*/
137+
private double calIToJProbably(String cityI, String cityJ, int currentTime) {
138+
double pro = 0;
139+
double n = 0;
140+
double pheromone;
141+
int i;
142+
int j;
143+
144+
i = Integer.parseInt(cityI);
145+
j = Integer.parseInt(cityJ);
146+
147+
pheromone = getPheromone(currentTime, cityI, cityJ);
148+
n = 1.0 / disMatrix[i][j];
149+
150+
if (pheromone == 0) {
151+
pheromone = 1;
152+
}
153+
154+
pro = Math.pow(n, alpha) * Math.pow(pheromone, beita);
155+
156+
return pro;
157+
}
158+
159+
/**
160+
* 计算综合概率蚂蚁从I城市走到J城市的概率
161+
*
162+
* @return
163+
*/
164+
public String selectAntNextCity(Ant ant, int currentTime) {
165+
double randomNum;
166+
double tempPro;
167+
// 总概率指数
168+
double proTotal;
169+
String nextCity = null;
170+
ArrayList<String> allowedCitys;
171+
// 各城市概率集
172+
double[] proArray;
173+
174+
// 如果是刚刚开始的时候,没有路过任何城市,则随机返回一个城市
175+
if (ant.currentPath.size() == 0) {
176+
nextCity = String.valueOf(random.nextInt(totalCitys.size()) + 1);
177+
178+
return nextCity;
179+
} else if (ant.nonVisitedCitys.isEmpty()) {
180+
// 如果全部遍历完毕,则再次回到起点
181+
nextCity = ant.currentPath.get(0);
182+
183+
return nextCity;
184+
}
185+
186+
proTotal = 0;
187+
allowedCitys = ant.nonVisitedCitys;
188+
proArray = new double[allowedCitys.size()];
189+
190+
for (int i = 0; i < allowedCitys.size(); i++) {
191+
nextCity = allowedCitys.get(i);
192+
proArray[i] = calIToJProbably(ant.currentPos, nextCity, currentTime);
193+
proTotal += proArray[i];
194+
}
195+
196+
for (int i = 0; i < allowedCitys.size(); i++) {
197+
// 归一化处理
198+
proArray[i] /= proTotal;
199+
}
200+
201+
// 用随机数选择下一个城市
202+
randomNum = random.nextInt(100) + 1;
203+
randomNum = randomNum / 100;
204+
// 因为1.0是无法判断到的,,总和会无限接近1.0取为0.99做判断
205+
if (randomNum == 1) {
206+
randomNum = randomNum - 0.01;
207+
}
208+
209+
tempPro = 0;
210+
// 确定区间
211+
for (int j = 0; j < allowedCitys.size(); j++) {
212+
if (randomNum > tempPro && randomNum <= tempPro + proArray[j]) {
213+
// 采用拷贝的方式避免引用重复
214+
nextCity = allowedCitys.get(j);
215+
break;
216+
} else {
217+
tempPro += proArray[j];
218+
}
219+
}
220+
221+
return nextCity;
222+
}
223+
224+
/**
225+
* 获取给定时间点上从城市i到城市j的信息素浓度
226+
*
227+
* @param t
228+
* @param cityI
229+
* @param cityJ
230+
* @return
231+
*/
232+
private double getPheromone(int t, String cityI, String cityJ) {
233+
double pheromone = 0;
234+
String key;
235+
236+
// 上一周期需将时间倒回一周期
237+
key = MessageFormat.format("{0},{1},{2}", cityI, cityJ, t);
238+
239+
if (pheromoneTimeMap.containsKey(key)) {
240+
pheromone = pheromoneTimeMap.get(key);
241+
}
242+
243+
return pheromone;
244+
}
245+
246+
/**
247+
* 每轮结束,刷新信息素浓度矩阵
248+
*
249+
* @param t
250+
*/
251+
private void refreshPheromone(int t) {
252+
double pheromone = 0;
253+
// 上一轮周期结束后的信息素浓度,丛信息素浓度图中查找
254+
double lastTimeP = 0;
255+
// 本轮信息素浓度增加量
256+
double addPheromone;
257+
String key;
258+
259+
for (String i : totalCitys) {
260+
for (String j : totalCitys) {
261+
if (!i.equals(j)) {
262+
// 上一周期需将时间倒回一周期
263+
key = MessageFormat.format("{0},{1},{2}", i, j, t - 1);
264+
265+
if (pheromoneTimeMap.containsKey(key)) {
266+
lastTimeP = pheromoneTimeMap.get(key);
267+
} else {
268+
lastTimeP = 0;
269+
}
270+
271+
addPheromone = 0;
272+
for (Ant ant : totalAnts) {
273+
// 每只蚂蚁传播的信息素为控制因子除以距离总成本
274+
addPheromone += Q / ant.calSumDistance();
275+
}
276+
277+
// 将上次的结果值加上递增的量,并存入图中
278+
pheromone = p * lastTimeP + addPheromone;
279+
key = MessageFormat.format("{0},{1},{2}", i, j, t);
280+
pheromoneTimeMap.put(key, pheromone);
281+
}
282+
}
283+
}
284+
285+
}
286+
287+
public void antStartSearching() {
288+
// 蚁群寻找的总次数
289+
int loopCount = 0;
290+
// 选中的下一个城市
291+
String selectedCity = "";
292+
293+
pheromoneTimeMap = new HashMap<String, Double>();
294+
totalAnts = new ArrayList<>();
295+
random = new Random();
296+
297+
while (loopCount < 10) {
298+
initAnts();
299+
300+
while (true) {
301+
for (Ant ant : totalAnts) {
302+
selectedCity = selectAntNextCity(ant, currentTime);
303+
ant.goToNextCity(selectedCity);
304+
}
305+
306+
// 如果已经遍历完所有城市,则跳出此轮循环
307+
if (totalAnts.get(0).isBack()) {
308+
break;
309+
}
310+
}
311+
312+
// 周期时间叠加
313+
currentTime++;
314+
refreshPheromone(currentTime);
315+
}
316+
317+
// 根据距离成本,选出所花距离最短的一个路径
318+
Collections.sort(totalAnts);
319+
bestPath = totalAnts.get(0).currentPath;
320+
for (String cityName : bestPath) {
321+
System.out.println(MessageFormat.format("-->{0}", cityName));
322+
}
323+
}
324+
325+
/**
326+
* 初始化蚁群操作
327+
*/
328+
private void initAnts() {
329+
Ant tempAnt;
330+
ArrayList<String> nonVisitedCitys;
331+
totalAnts.clear();
332+
333+
// 初始化蚁群
334+
for (int i = 0; i < antNum; i++) {
335+
nonVisitedCitys = (ArrayList<String>) totalCitys.clone();
336+
tempAnt = new Ant(pheromoneMatrix, nonVisitedCitys);
337+
338+
totalAnts.add(tempAnt);
339+
}
340+
}
341+
}

0 commit comments

Comments
 (0)