回答第一部分:遗传算法的基础是拥有一组演员,其中一些演员重现.选择适者进行繁殖,后代是略有变异的父母的副本.这是一个非常简单的概念,但要编程它,你必须有可以随机选择和动态修改的动作.对于战舰模拟我创建了一个叫做射手的类,因为它在某个位置“射击”.这里的假设是第一个位置被击中,射手现在正试图沉没战列舰.
public class Shooter implements Comparable {
private static final int NUM_SHOTS = 100;
private List shots;
private int score;
// Make a new set of random shots.
public Shooter newShots() {
shots = new ArrayList(NUM_SHOTS);
for (int i = 0; i < NUM_SHOTS; ++i) {
shots.add(newShot());
}
return this;
}
// Test this shooter against a ship
public void testShooter(Ship ship) {
score = shots.size();
int hits = 0;
for (Position shot : shots) {
if (ship.madeHit(shot)) {
if (++hits >= ship.getSize())
return;
} else {
score = score - 1;
}
}
}
// get the score of the testShotr operation
public int getScore() {
return score;
}
// compare this shooter to other shooters.
@Override
public int compareTo(Shooter o) {
return score - o.score;
}
// getter
public List getShots() {
return shots;
}
// reproduce this shooter
public Shooter reproduce() {
Shooter offspring = new Shooter();
offspring.mutate(shots);
return offspring;
}
// mutate this shooter's offspring
private void mutate(List pShots) {
// copy parent's shots (okay for shallow)
shots = new ArrayList(pShots);
// 10% new mutations, in random locations
for (int i = 0; i < NUM_SHOTS / 10; i++) {
int loc = (int) (Math.random() * 100);
shots.set(loc, newShot());
}
}
// make a new random move
private Position newShot() {
return new Position(((int) (Math.random() * 6)) - 3, ((int) (Math.random() * 6)) - 3);
}
}
这里的想法是射手有多达100次射击,随机选择在X中的-3和Y中的-3之间.是的,100次射击是过度的,但是嘿,无论如何.将一艘船发送到这个Shooter.testShooter,它将得分,100分是最好的分数,0分是最差的.
这个射手演员有重现和变异的方法,将返回一个后代,其中10%的镜头随机变异.一般的想法是,最好的射手已经“学会”尽快以交叉模式(”)射击,因为一艘船以四种方式之一(北,南,东,西)定向.
运行模拟的程序ShooterSimulation非常简单:
public class ShooterSimulation {
private int NUM_GENERATIONS = 1000;
private int NUM_SHOOTERS = 20;
private int NUM_SHOOTERS_NEXT_GENERATION = NUM_SHOOTERS / 10;
List shooters = new ArrayList(NUM_SHOOTERS);
Ship ship;
public static void main(String... args) {
new ShooterSimulation().run();
}
// do the work
private void run() {
firstGeneration();
ship = new Ship();
for (int gen = 0; gen < NUM_GENERATIONS; ++gen) {
ship.newOrientation();
testShooters();
Collections.sort(shooters);
printAverageScore(gen, shooters);
nextGeneration();
}
}
// make the first generation
private void firstGeneration() {
for (int i = 0; i < NUM_SHOOTERS; ++i) {
shooters.add(new Shooter().newShots());
}
}
// test all the shooters
private void testShooters() {
for (int mIdx = 0; mIdx < NUM_SHOOTERS; ++mIdx) {
shooters.get(mIdx).testShooter(ship);
}
}
// print the average score of all the shooters
private void printAverageScore(int gen, List shooters) {
int total = 0;
for (int i = 0, j = shooters.size(); i < j; ++i) {
total = total + shooters.get(i).getScore();
}
System.out.println(gen + " " + total / shooters.size());
}
// throw away the a tenth of old generation
// replace with offspring of the best fit
private void nextGeneration() {
for (int l = 0; l < NUM_SHOOTERS_NEXT_GENERATION; ++l) {
shooters.set(l, shooters.get(NUM_SHOOTERS - l - 1).reproduce());
}
}
}
代码从run方法读取为伪代码:make firstGeneration然后迭代多代.对于每一代,为ship设置newOrientation,然后执行testShooters,并使用Collections.sort对测试结果进行排序. printAverageScore的测试,然后构建nextGeneration.用你可以的平均分数列表,咳嗽咳嗽,做一个’分析’.
结果图如下所示:
正如你所看到的那样,平均得分相当低,但学得很快.但是,船的方向不断变化,除随机分量外还会产生一些噪音.一次又一次的突变使得这个群体变得有点混乱,但随着整个群体的改善而变得越来越少.
挑战,以及许多论文确定的原因,是使更多的事情变得可变,特别是以建设性的方式.例如,镜头数量可能是可变的.或者,用根据最后一次击中是击中还是未命中而树枝的树替换击球列表可能会改善一些事情,但很难说.这就是’决策’逻辑考虑因素的来源.有一个随机镜头列表或树根据先前的镜头决定采取哪个分支更好吗?更高层次的挑战包括预测哪些变化将使小组学得更快,并且不易受到不良突变的影响.
最后,考虑可能有多个团体,一组是战列舰猎人,另一组是潜艇猎人.每个组虽然由相同的代码组成,但可以“发展”不同的内部“遗传学”,使他们能够专注于他们的任务.
无论如何,一如既往,从一个简单的地方开始,随时学习,直到你变得足够好回去阅读论文.
PS>也需要这个:
public class Position {
int x;
int y;
Position(int x, int y ) {this.x=x; this.y=y;}
@Override
public boolean equals(Object m) {
return (((Position)m).x==x && ((Position)m).y==y);
}
}
UDATE:添加了Ship类,修复了一些错误:
public class Ship {
List positions;
// test if a hit was made
public boolean madeHit(Position shot) {
for (Position p: positions) {
if ( p.equals(shot)) return true;
}
return false;
}
// make a new orientation
public int newOrientation() {
positions = new ArrayList(3);
// make a random ship direction.
int shipInX=0, oShipInX=0 , shipInY=0, oShipInY=0;
int orient = (int) (Math.random() * 4);
if( orient == 0 ) {
oShipInX = 1;
shipInX = (int)(Math.random()*3)-3;
}
else if ( orient == 1 ) {
oShipInX = -1;
shipInX = (int)(Math.random()*3);
}
else if ( orient == 2 ) {
oShipInY = 1;
shipInY = (int)(Math.random()*3)-3;
}
else if ( orient == 3 ) {
oShipInY = -1;
shipInY = (int)(Math.random()*3);
}
// make the positions of the ship
for (int i = 0; i < 3; ++i) {
positions.add(new Position(shipInX, shipInY));
if (orient == 2 || orient == 3)
shipInY = shipInY + oShipInY;
else
shipInX = shipInX + oShipInX;
}
return orient;
}
public int getSize() {
return positions.size();
}
}