zoukankan      html  css  js  c++  java
  • 遗传编程GP-地图路径寻路

    本文介绍的是基于GP,并非A*算法,算是另类实现吧。

    先看看地图定义,在文本文件中定义如下字符串,代表30列11行大小的地图

    初始位置在左上角(0,0) ,值为1的是允许走的通的路,目标位置为右下角(29,10)

    1 1 1 1 1 1 1 0 0 0 0 0 0 0 1 0 0 0 0 
    0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 
    0 0 0 0 0 1 1 0 0 0 0 0 0 0 1 0 0 0 0 
    0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 
    0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 
    0 0 0 1 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 
    0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 
    0 0 0 1 1 1 1 1 1 0 0 0 0 0 1 0 0 0 0 
    0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 
    0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 
    0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1   

    算法运行效果如下:

    "C:Program FilesJavajdk1.8.0_211injava.exe" "-javaagent:C:Program FilesJetBrainsIntelliJ IDEA 2019.1.2libidea_rt.jar=54171:C:Program FilesJetBrainsIntelliJ IDEA 2019.1.2in" -Dfile.encoding=UTF-8 -classpath "C:Program FilesJavajdk1.8.0_211jrelibcharsets.jar;C:Program FilesJavajdk1.8.0_211jrelibdeploy.jar;C:Program FilesJavajdk1.8.0_211jrelibextaccess-bridge-64.jar;C:Program FilesJavajdk1.8.0_211jrelibextcldrdata.jar;C:Program FilesJavajdk1.8.0_211jrelibextdnsns.jar;C:Program FilesJavajdk1.8.0_211jrelibextjaccess.jar;C:Program FilesJavajdk1.8.0_211jrelibextjfxrt.jar;C:Program FilesJavajdk1.8.0_211jrelibextlocaledata.jar;C:Program FilesJavajdk1.8.0_211jrelibext
    ashorn.jar;C:Program FilesJavajdk1.8.0_211jrelibextsunec.jar;C:Program FilesJavajdk1.8.0_211jrelibextsunjce_provider.jar;C:Program FilesJavajdk1.8.0_211jrelibextsunmscapi.jar;C:Program FilesJavajdk1.8.0_211jrelibextsunpkcs11.jar;C:Program FilesJavajdk1.8.0_211jrelibextzipfs.jar;C:Program FilesJavajdk1.8.0_211jrelibjavaws.jar;C:Program FilesJavajdk1.8.0_211jrelibjce.jar;C:Program FilesJavajdk1.8.0_211jrelibjfr.jar;C:Program FilesJavajdk1.8.0_211jrelibjfxswt.jar;C:Program FilesJavajdk1.8.0_211jrelibjsse.jar;C:Program FilesJavajdk1.8.0_211jrelibmanagement-agent.jar;C:Program FilesJavajdk1.8.0_211jrelibplugin.jar;C:Program FilesJavajdk1.8.0_211jrelib
    esources.jar;C:Program FilesJavajdk1.8.0_211jrelib
    t.jar;C:Research-Codedemo1	argetclasses;C:UsersMcKay.m2
    epositoryiojeneticsjenetics5.1.0jenetics-5.1.0.jar;C:UsersMcKay.m2
    epositoryiojeneticsjenetics.ext5.1.0jenetics.ext-5.1.0.jar;C:UsersMcKay.m2
    epositoryiojeneticsjenetics.prog5.1.0jenetics.prog-5.1.0.jar;C:UsersMcKay.m2
    epositorycommons-langcommons-lang2.6commons-lang-2.6.jar" MapGame.GameDemo
    1 1 1 1 1 1 1 0 0 0 0 0 0 0 1 0 0 0 0 
    0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 
    0 0 0 0 0 1 1 0 0 0 0 0 0 0 1 0 0 0 0 
    0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 
    0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 
    0 0 0 1 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 
    0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 
    0 0 0 1 1 1 1 1 1 0 0 0 0 0 1 0 0 0 0 
    0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 
    0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 
    0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 
    G: 15769
    --------------------
    map-->Right-->Right-->Right-->Right-->Right-->Down-->Down-->Down-->Left-->Down-->Down-->Left-->Down-->Down-->Right-->Right-->Right-->Right-->Right-->Down-->Down-->Down-->Right-->Right-->Right-->Right-->Right-->Right-->Right-->Right-->Right-->Right-->
    E: 1320.0
    
    Process finished with exit code 0
    

    这边由于是套遗传编程,因此会定义几个固定操作算子:上移、下移、左移、右移;看看上移算子代码:

    public class GoUpOp implements Op<TempMapInfo> {
        private MapController mapController;              //地图控制工具类,比如判断能否移动到某个坐标、是否完成地图等
        public GoUpOp(MapController mapController) {
            this.mapController=mapController;
        }
    
        @Override
        public String name() {
            return "Up";
        }
    
        @Override
        public int arity() {
            return 1;
        }
    
        @Override
        public String toString() {
            return "Up";
        }
    
        @Override
        public TempMapInfo apply(TempMapInfo[] tempMapInfos) {
            TempMapInfo newInfo=tempMapInfos[0].cloneMe();        //需要深度克隆,防止多线程对象直接互相影响
    
            if(newInfo.currentLocationY==0)
            {
                newInfo.score-=1000;                      //已经在最上方了,不能再做上移动作了
                newInfo.tag+="-UP";                       //惩罚分,扣除1000分
                return newInfo;
            }
            if(!mapController.canMove2(newInfo.currentLocationX, newInfo.currentLocationY-1))    //是否上移位置是路
            {
                newInfo.score-=1000;                                      //惩罚扣除1000分
                newInfo.tag+="-UP";
                return newInfo;
            }
    
            newInfo.score+=10;                      //可以走,奖励10分
            newInfo.currentLocationY--;                 //递减y坐标
            if(newInfo.visited.contains(newInfo.currentLocationX+","+newInfo.currentLocationY))    //不能重复访问点
                newInfo.score-=1000;
            else
                newInfo.visited.add(newInfo.currentLocationX+","+newInfo.currentLocationY);
    
            if(mapController.isSuccess(newInfo.currentLocationX, newInfo.currentLocationY))      //判断是否地图完成
                newInfo.score+=1000;
            newInfo.tag+="-UP";
            return newInfo;
        }
    
    }  

      

     下面需要将这些操作算子嵌进GP中:

    public static void main(String[] args) {
    
            Integer[][] map=GetMap();
            dispalyMap(map);
    
            TempMapInfo mapInfo=new TempMapInfo();
            mapInfo.score=0;
            mapInfo.currentLocationX=0;
            mapInfo.currentLocationY=0;          //左上角为起点
    
            List<Op<TempMapInfo>> terminals=new ArrayList<>();
            terminals.add(Const.of("map", mapInfo));
    
            MapController mapController=new MapController(map);
    
            final ISeq<Op<TempMapInfo>> TMS = ISeq.of(terminals);
            final ISeq<Op<TempMapInfo>> OPS = ISeq.of(new GoLeftOp(mapController), new GoRightOp(mapController), new GoUpOp(mapController), new GoDownOp(mapController));
            final GameSearcher gameSearcher =  GameSearcher.of(
                                                                GameSearcher.codecOf(
                                                                        OPS, TMS, 20,
                                                                        t -> t.getGene().size() < 60
                                                                )
                                                    );
    
            final Engine<ProgramGene<TempMapInfo>, Double> engine = Engine
                    .builder(gameSearcher)
                    .populationSize(500)
                    .maximizing()
                    .alterers(
                            new SingleNodeCrossover<>(0.1),
                            new Mutator<>(0.3),
                            new UniformCrossover<>(0.5)
                    )
                    .offspringSelector(new TournamentSelector<>(2))
                    .survivorsSelector(new TournamentSelector<>())
                    .build();
    
            final EvolutionResult<ProgramGene<TempMapInfo>, Double> er =
                    engine.stream()
                            .limit(Limits.byExecutionTime(Duration.ofSeconds(60)))
                            .collect(EvolutionResult.toBestEvolutionResult());
    
            final ProgramGene<TempMapInfo> program = er.getBestPhenotype()
                    .getGenotype()
                    .getGene();
    
            final TreeNode<Op<TempMapInfo>> tree = program.toTreeNode();
            System.out.println("G: " + er.getTotalGenerations());
            printTree(tree.depthFirstStream().collect(Collectors.toList()));
            System.out.println("E: " + gameSearcher._fitness(tree));
        }
    
        private static void printTree(List<TreeNode<Op<TempMapInfo>>> lst) {
            System.out.println("--------------------");
            for(TreeNode<Op<TempMapInfo>> node:lst)
                System.out.print(node.getValue()+"-->");
            System.out.println();
        }  

     上面的terminals变量是存放终结符的,此处是直接把操作动作放进去了,包含了分数、访问步骤、当前xy坐标等,只有1个变量

    public class TempMapInfo {
        public int currentLocationX;
        public int currentLocationY;
        public double score;
        public List<String> visited=new ArrayList<>();
        public String tag="";
    
        public TempMapInfo()
        {
            visited.add("0,0");
        }
    
        public TempMapInfo cloneMe()
        {
            TempMapInfo info=new TempMapInfo();
    
            info.currentLocationX=this.currentLocationX;
            info.currentLocationY=this.currentLocationY;
            info.score=this.score;
            info.tag=this.tag;
            info.visited=new ArrayList<>();
            for(String i:this.visited)
                info.visited.add(i);
    
            return info;
        }
    }
    

      

    GameSearcher是对GP算法的编码、解码封装、计算分值,算是核心:
    public final class GameSearcher
    	implements Problem<Tree<Op<TempMapInfo>, ?>, ProgramGene<TempMapInfo>, Double>
    {
    
    	private final Codec<Tree<Op<TempMapInfo>, ?>, ProgramGene<TempMapInfo>> _codec;
    
    	private GameSearcher(
    		final Codec<Tree<Op<TempMapInfo>, ?>, ProgramGene<TempMapInfo>> codec
    	) {
    		_codec = requireNonNull(codec);
    	}
    
    	@Override
    	public Function<Tree<Op<TempMapInfo>, ?>, Double> fitness() {
    		return this::_fitness;
    	}
    
    	@Override
    	public Codec<Tree<Op<TempMapInfo>, ?>, ProgramGene<TempMapInfo>> codec() {
    		return _codec;
    	}
    
    
    	public double _fitness(final Tree<Op<TempMapInfo>, ?> program) {
    
    		List<TempMapInfo> lst=new ArrayList<>();
    		lst.add(new TempMapInfo());
    
    		List<TempMapInfo> results=lst.stream().map(args -> Program.eval(program, args)).collect(Collectors.toList());
    
    		double score=results.stream().mapToDouble(a->a.score).sum();              //这行是用来统计整个操作算子序列总得分用的,很重要
    		return score;
    	}
    
    	public static GameSearcher of(
    		final Codec<Tree<Op<TempMapInfo>, ?>, ProgramGene<TempMapInfo>> codec
    	) {
    		return new GameSearcher(codec);
    	}
    
    	public static Codec<Tree<Op<TempMapInfo>, ?>, ProgramGene<TempMapInfo>>
    	codecOf(
    		final ISeq<Op<TempMapInfo>> operations,
    		final ISeq<Op<TempMapInfo>> terminals,
    		final int depth,
    		final Predicate<? super ProgramChromosome<TempMapInfo>> validator
    	) {
    		if (depth > 200 || depth < 0) {
    			throw new IllegalArgumentException(format(
    				"Tree depth out of range [0, 30): %d", depth
    			));
    		}
    
    		return Codec.of(
    			Genotype.of(ProgramChromosome.of(
    				depth,
    				validator,
    				operations,
    				terminals
    			)),
    			Genotype::getGene
    		);
    	}
    }
    

      

     算法介绍完毕,pom依赖如下:

    <dependencies>
            <!-- https://mvnrepository.com/artifact/io.jenetics/jenetics -->
            <dependency>
                <groupId>io.jenetics</groupId>
                <artifactId>jenetics</artifactId>
                <version>5.1.0</version>
            </dependency>
            <dependency>
                <groupId>io.jenetics</groupId>
                <artifactId>jenetics.ext</artifactId>
                <version>5.1.0</version>
            </dependency>
            <dependency>
                <groupId>io.jenetics</groupId>
                <artifactId>jenetics.prog</artifactId>
                <version>5.1.0</version>
            </dependency>
            <dependency>
                <groupId>commons-lang</groupId>
                <artifactId>commons-lang</artifactId>
                <version>2.6</version>
            </dependency>
        </dependencies>
  • 相关阅读:
    Max Sum Plus Plus HDU
    Monkey and Banana HDU
    Ignatius and the Princess IV HDU
    Extended Traffic LightOJ
    Tram POJ
    Common Subsequence HDU
    最大连续子序列 HDU
    Max Sum HDU
    畅通工程再续
    River Hopscotch POJ
  • 原文地址:https://www.cnblogs.com/aarond/p/GPMap.html
Copyright © 2011-2022 走看看