贪心算法+Java实现C的函数指针

先囧一下,其实本来没有这篇的。
最近在读算法的相关书籍,正好读到贪心算法,大家知道算法的书籍大多是通过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~
就酱~

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值