贪心算法+Java实现C的函数指针
2015-11-12 17:16
495 查看
先囧一下,其实本来没有这篇的。
最近在读算法的相关书籍,正好读到贪心算法,大家知道算法的书籍大多是通过C或C++来提供源码的,贪心算法经常会涉及到多个计算结果的对比然后取最优解,C++的处理方式一般都是函数指针,提供一个类似函数指针的数组,然后遍历循环每个函数获得result的结果集。然而java是没有指针的,当然更没有函数指针这一说,那遇到这种情况我们代码要怎么写呢。
百度喵了一眼,看到两个字,接口。
没错,就是接口。接口 + 多态,抽象来看不就是一个方法,多个实现嘛。不过区别在于函数指针可以随便指定函数,而接口相当于一个规范,不满足这个规范的方法我们是无法通过接口去调用的。不过,既然现在需求是循环一系列函数获取某个值并进行比较,那这些函数一定是遵循一个规范的,所以我们用接口一定可以搞定。
顺便简单介绍下贪心算法:
贪心算法(又称贪婪算法)是指,在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,他所做出的是在某种意义上的局部最优解。
贪心算法不是对所有问题都能得到整体最优解,关键是贪心策略的选择,选择的贪心策略必须具备无后效性,即某个状态以前的过程不会影响以后的状态,只与当前状态有关。
贪心算法的经典问题就是0-1背包问题,N个物品,1个背包,每个物品的重量为w1到wN,价值为p1到pN,背包能承载的最大重量为C,求解最优的装法。当然前提是物品只有装和不装两个选择,不存在装多个的问题,所以叫01背包问题。
好了,铺垫完了,上代码:
首先定义我们的物品类,封装重量和价值。下面定义接口:
很简单,就一个getResult方法来获取算法的结果,而CAPACITY为设定的背包的最大重量。然后来看三个贪心的实现,分别是:每次取最贵的,每次取最轻的(让物品数量最多),每次取价格密度最高的(price / weight):
代码很好理解,就不多说了,注意拿到传进来的list记得要clone,不然做了remove操作后,原来的list也被你改了。这里发现个bug,就是循环退出的条件不够,如果所有物品重量加起来都没有超过150程序就要死循环了,嘛,如果这样这个最优解也没有意义了,各位可自行修改,本来重点也不是这个。
好,当我们涉及多个算法的时候,脑子里的第一反应是什么,没错,策略模式,我们需要封装一个类供外部调用,隐藏算法的细节和调用方式。来看GreedyManager类:
OK,这篇的重点基本都在这个类了。C++的函数指针数组我们没法实现,但别忘了我们有反射,所以我们可以配置一个类名的数组供循环来调用。然后通过循环,得到每一种贪心计算结果的result,再取出最大的结果给外部调用者。以后再有新的贪心计算规则时,只要增加一个IGreedy的子类,并在数组中增加一个String就可以啦~ 传说中的对修改关闭,对扩展开放。
最后看一下客户端调用:
看一下输出结果:Result->170。
大家有兴趣可以自己算一下,第三种贪心算法就是最优解170~
就酱~
最近在读算法的相关书籍,正好读到贪心算法,大家知道算法的书籍大多是通过C或C++来提供源码的,贪心算法经常会涉及到多个计算结果的对比然后取最优解,C++的处理方式一般都是函数指针,提供一个类似函数指针的数组,然后遍历循环每个函数获得result的结果集。然而java是没有指针的,当然更没有函数指针这一说,那遇到这种情况我们代码要怎么写呢。
百度喵了一眼,看到两个字,接口。
没错,就是接口。接口 + 多态,抽象来看不就是一个方法,多个实现嘛。不过区别在于函数指针可以随便指定函数,而接口相当于一个规范,不满足这个规范的方法我们是无法通过接口去调用的。不过,既然现在需求是循环一系列函数获取某个值并进行比较,那这些函数一定是遵循一个规范的,所以我们用接口一定可以搞定。
顺便简单介绍下贪心算法:
贪心算法(又称贪婪算法)是指,在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,他所做出的是在某种意义上的局部最优解。
贪心算法不是对所有问题都能得到整体最优解,关键是贪心策略的选择,选择的贪心策略必须具备无后效性,即某个状态以前的过程不会影响以后的状态,只与当前状态有关。
贪心算法的经典问题就是0-1背包问题,N个物品,1个背包,每个物品的重量为w1到wN,价值为p1到pN,背包能承载的最大重量为C,求解最优的装法。当然前提是物品只有装和不装两个选择,不存在装多个的问题,所以叫01背包问题。
好了,铺垫完了,上代码:
package com.amuro.greedy; public class Goods { public int weight; public int price; }
首先定义我们的物品类,封装重量和价值。下面定义接口:
package com.amuro.greedy.algorithm; import java.util.List; import com.amuro.greedy.Goods; public interface IGreedy { public static final int CAPACITY = 150; public int getResult(List<Goods> goodsList); }
很简单,就一个getResult方法来获取算法的结果,而CAPACITY为设定的背包的最大重量。然后来看三个贪心的实现,分别是:每次取最贵的,每次取最轻的(让物品数量最多),每次取价格密度最高的(price / weight):
package com.amuro.greedy.algorithm; import java.util.ArrayList; import java.util.List; import com.amuro.greedy.Goods; /** * 每次优先选最贵的 * @author Amuro * */ public class Greedy1 implements IGreedy { @Override public int getResult(List<Goods> goodsList) { if(goodsList == null || goodsList.size() == 0) { return -1; } @SuppressWarnings("unchecked") List<Goods> tempList = (List<Goods>) ((ArrayList<Goods>)goodsList).clone(); int result = 0; int currentWeight = 0; while(true) { Goods maxPriceGoods = selectTheMostExpensiveGoods(tempList); currentWeight += maxPriceGoods.weight; if(currentWeight > 150) { break; } result += maxPriceGoods.price; tempList.remove(maxPriceGoods); } return result; } private Goods selectTheMostExpensiveGoods(List<Goods> tempList) { Goods maxPriceGoods = tempList.get(0); for(int i = 1; i < tempList.size(); i++) { Goods goods = tempList.get(i); if(goods.price > maxPriceGoods.price) { maxPriceGoods = goods; } } return maxPriceGoods; } }
package com.amuro.greedy.algorithm; import java.util.ArrayList; import java.util.List; import com.amuro.greedy.Goods; /** * 每次选最轻的 * @author Amuro * */ public class Greedy2 implements IGreedy { @Override public int getResult(List<Goods> goodsList) { if(goodsList == null || goodsList.size() == 0) { return -1; } @SuppressWarnings("unchecked") List<Goods> tempList = (List<Goods>) ((ArrayList<Goods>)goodsList).clone(); int result = 0; int currentWeight = 0; while(true) { Goods lightestGoods = selectedTheLightestGoods(tempList); currentWeight += lightestGoods.weight; if(currentWeight > 150) { break; } result += lightestGoods.price; tempList.remove(lightestGoods); } return result; } private Goods selectedTheLightestGoods(List<Goods> tempList) { Goods lightestGoods = tempList.get(0); for(int i = 1; i < tempList.size(); i++) { Goods goods = tempList.get(i); if(goods.weight < lightestGoods.weight) { lightestGoods = goods; } } return lightestGoods; } }
package com.amuro.greedy.algorithm; import java.util.ArrayList; import java.util.List; import com.amuro.greedy.Goods; /** * 每次选价值密度最高的 * @author Amuro * */ public class Greedy3 implements IGreedy { @Override public int getResult(List<Goods> goodsList) { if(goodsList == null || goodsList.size() == 0) { return -1; } @SuppressWarnings("unchecked") List<Goods> tempList = (List<Goods>) ((ArrayList<Goods>)goodsList).clone(); int result = 0; int currentWeight = 0; while(true) { Goods mostDensityGoods = selectedMostDensityGoods(tempList); currentWeight += mostDensityGoods.weight; if(currentWeight > 150) { break; } result += mostDensityGoods.price; tempList.remove(mostDensityGoods); } return result; } private Goods selectedMostDensityGoods(List<Goods> tempList) { Goods mostDensityGoods = tempList.get(0); for(int i = 1; i < tempList.size(); i++) { Goods goods = tempList.get(i); float mostDensity = mostDensityGoods.price / mostDensityGoods.weight; float currentDensity = goods.price / goods.weight; if(currentDensity > mostDensity) { mostDensityGoods = goods; } } return mostDensityGoods; } }
代码很好理解,就不多说了,注意拿到传进来的list记得要clone,不然做了remove操作后,原来的list也被你改了。这里发现个bug,就是循环退出的条件不够,如果所有物品重量加起来都没有超过150程序就要死循环了,嘛,如果这样这个最优解也没有意义了,各位可自行修改,本来重点也不是这个。
好,当我们涉及多个算法的时候,脑子里的第一反应是什么,没错,策略模式,我们需要封装一个类供外部调用,隐藏算法的细节和调用方式。来看GreedyManager类:
package com.amuro.greedy; import java.util.List; import com.amuro.greedy.algorithm.IGreedy; public class GreedyManager { private List<Goods> goodsList; private String[] algorithems = { "com.amuro.greedy.algorithm.Greedy1", "com.amuro.greedy.algorithm.Greedy2", "com.amuro.greedy.algorithm.Greedy3"}; public GreedyManager(List<Goods> goodsList) { this.goodsList = goodsList; } public int doAlgorithm() throws InstantiationException, IllegalAccessException, ClassNotFoundException { int result = getClass(algorithems[0]).getResult(goodsList); for(int i = 1;i < algorithems.length; i++) { int temp = getClass(algorithems[i]).getResult(goodsList); if(temp > result) { result = temp; } } return result; } private IGreedy getClass(String className) throws InstantiationException, IllegalAccessException, ClassNotFoundException { return ((IGreedy)Class.forName(className).newInstance()); } }
OK,这篇的重点基本都在这个类了。C++的函数指针数组我们没法实现,但别忘了我们有反射,所以我们可以配置一个类名的数组供循环来调用。然后通过循环,得到每一种贪心计算结果的result,再取出最大的结果给外部调用者。以后再有新的贪心计算规则时,只要增加一个IGreedy的子类,并在数组中增加一个String就可以啦~ 传说中的对修改关闭,对扩展开放。
最后看一下客户端调用:
package com.amuro.greedy; import java.util.ArrayList; import java.util.List; public class Client { public static void main(String[] args) { Client clt = new Client(); clt.doAlgorithm(); } private List<Goods> goodsList; private GreedyManager greedyManager; public Client() { init(); } private int[] weights = {35, 30, 60, 50, 40, 10, 25}; private int[] prices = {10, 40, 30, 50, 35, 40, 30}; private void init() { goodsList = new ArrayList<>(); for(int i = 0;i < weights.length;i++) { Goods goods = new Goods(); goods.weight = weights[i]; goods.price = prices[i]; goodsList.add(goods); } greedyManager = new GreedyManager(goodsList); } public void doAlgorithm() { try { System.out.println("Result->" + greedyManager.doAlgorithm()); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } } }
看一下输出结果:Result->170。
大家有兴趣可以自己算一下,第三种贪心算法就是最优解170~
就酱~
相关文章推荐
- Java .class文件保护原理
- 单例模式到Java内存模型
- java 设计作业——学生类的基本练习
- Java — 事件监听、事件处理 初体验
- spark streaming 接收 kafka 数据java代码WordCount示例
- SpringJDBC--NamedParameterJdbcTemplate
- java堆栈(转)
- 【Java并发编程】之五:volatile变量修饰符—意料之外的问题(含代码)
- Java中String为什么是Immutable的
- 利用Spring MVC 上传图片文件 博客分类: Java总结文档
- SpringMVC 解读——<mvc:annotation-driven/>
- 从头认识java-8.5 在方法和作用域里的内部类
- spring学习笔记
- Spring、AOP详解
- Spring、AOP详解
- eclipse dynamic web project pom.xml配置
- Struts2系统学习(2)Struts2-HelloWorld
- Java Map遍历方式方式及性能测试
- 小编辑 Java 中十进制和十六进制的相互转换
- JAVA8 十大新特性详解