• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

贪心算法-实现用户订单数最小划分求最优

武飞扬头像
东北一棵松
帮助3

贪心算法的概念

  • 贪心算法(又称贪婪算法)是指,在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,算法得到的是在某种意义上的局部最优解 。
    贪心算法不是对所有问题都能得到整体最优解,关键是贪心策略的选择 。

算法思路

  • 建立数学模型来描述问题。
  • 把求解的问题分成若干子问题。
  • 对每个子问题求解,等到子问题的局部最优解。
  • 把子问题的解局部最优解合成原来解问题的一个解。

个人理解

  • 我们可以将一个问题拆分成各个子问题进行解决,在每个子问题中我们选择一种贪心策略,选出最优解,最终将这些子问题的最优解组合起来,就是整个问题的最优解。

例题

  • 在开发购物车功能时,我们可以有这种规则的限定。每个用户可以有多个订单,每个订单上面包含多个商品,(每一个订单上的商品归属于一个用户),而且每笔订单的交易商品的总价格不能超过1000元。当用户购买完商品时,现要求生成的订单数量最少。

根据上面问题我们创建一下模型

1.首先从上面问题,我们可以将商品类和订单类创建出来,其中商品类Item包含价格 price,用户id sellerId。订单类Order 包含总价格 totalPrice,这个订单归属用户的id sellerId,以及商品列表 orderItems.
学新通

  • 订单类
import lombok.Data;

import java.util.List;

/**
 * 订单类
 */
@Data
public class Order {
    /**
     * 该订单对应的商品
     */
    List<Item> orderItems;
    /**
     * 该订单金额,单位分
     */
    Double totalPrice;
    /**
     * 该订单对应的卖家userId
     */
    long sellerId;
}

学新通
  • 商品类
import lombok.Data;

/**
 * 商品类
 */
@Data
public class Item {
    /**
     * 卖家用户id
     */
    long sellerId;
    /**
     * 商品价格,单位分
     */
    Double price;
}

学新通

2.其次我们分析 而且每笔订单的交易商品的总价格不能超过1000元。当用户购买完商品是,现要求生成的订单数量最少 一下该规则要求。可以设计出一种贪心策略。

  • 用户购买的商品可能超过1000,也可能超不过1000 ,超不过1000元的我们只需要一个订单就可以了。超过1000的的,我们应如何保持订单最少呢?
  • 当超过1000时,我们将该用户的商品列表List进行降序排列,然后从头往下去取数据,加入一个订单中,当一个订单中的总价格大于1000元时,我们就不在取数据了,将剩余的商品列表中的数据,在加入另外的订单中,直到该用户的商品类表为空。
  • 此时我们已经获得了一个用户的订单列表,此时你就以为获得了最少数量的订单类吗?
    ** 我们看一下下面的一组数据
    ** 用户a 购买了 888 950 112 40 四种价格的商品。
    ** 按我们上面的思路,首先对着四种商品进行排序变为 950 888 112 40
    ** 我们首先将最大的 950加入到一个订单,在加入888,因为950 888>1000,因此我们不能将其加入进去,因此 我们将 888 和112 封装成一个订单 40有封装成了一个订单。但是我们可以发现我们只需要两个订单就可以了,现在出现了3个,因此我们还需要对订单类表进行优化
    ** 因此,我们还需要对订单进行合并,将950和40合并成一个即可,订单如何合并呢?
    ** 首先我们根据订单的总价格进行排序,将总价格相加起来小于1000的进行排序,思想和对商品加入订单的思路相同。

运行结果

学新通

核心代码

public static List<Order> packageItemsToOrders(List<Item> items) {
        //定义函数返回的订单
        List<Order> orders = new ArrayList<>();
        //根据用户id对items进行分组,为实现:每笔交易订单的商品只能是同一个卖家
        Map<Long, List<Item>> listItemMap = items.stream()
                .collect(Collectors.groupingBy(Item::getSellerId));
        //得到该商品列表中所以普的用户id/初步优化
        Object sellerIds[] = listItemMap.keySet().toArray();
        for (int i = 0; i < sellerIds.length; i  ) {
            //定义用户的订单列表
            List<Order> addOrder=new ArrayList<>();
            //获取到用户所有的商品列表
            List<Item> getItems = listItemMap.get((long) sellerIds[i]);
            //对商品列表进行排序
            getItems = getItems
                    .stream()
                    .sorted(Comparator.comparing(Item::getPrice).reversed())
                    .collect(Collectors.toList());
            /**
             * 将用户的商品列表的数据,
             * 取出来封装成订单,
             * 加入用户的订单列表,
             * 用户的商品列表为空结束循环
             */
            while (getItems.size() > 0) {
                double countPrice = 0.0;
                //新建一个订单接收商品
                Order o = new Order();
                o.setSellerId((long) sellerIds[i]);
                int j = 0;
                for (; j < getItems.size(); j  ) {
                    countPrice  = getItems.get(j).getPrice();
                    if (countPrice > 1000.0) {
                        break;
                    }
                }
                List<Item> removers=new ArrayList<>();
                //记录已经加入到了订单中的商品,等一下将他们删除
                removers = getItems.subList(0, j );
                if(removers.size()>0){
                    //加入到订单中的闪频列表大于0,说明订单中有值
                    o.setTotalPrice(removers.stream().map(Item::getPrice)
                            .reduce(Double::sum).get());
                    List<Item> items1=new ArrayList<>();
                    removers.forEach(remover->{
                        items1.add(remover);
                            }
                    );
                    o.setOrderItems(items1);
                    //我们将订单,加入用户的订单列表中
                    addOrder.add(o);
                    //将加入到订单的商品的数据,从订单列表中删除
                    getItems.removeAll(removers);
                }
            }
            //对用户的订单列表进行排序
            List<Order> orderList=addOrder.parallelStream()
                    .sorted(Comparator.comparing(Order::getTotalPrice).reversed())
                    .collect(Collectors.toList());
            int k =0;
            //进行订单的合并操作
            for(;k<orderList.size();k  ){
                double countPrice = 0.0;
                int j = k;
                for (; j < orderList.size(); j  ) {
                    countPrice  = orderList.get(j).getTotalPrice();
                    if (countPrice > 1000.0) {
                        break;
                    }
                }
                if(j-k>1){
                    //j-k>1代表有订单可以合并
                    //需要合并的订单,也是需要将该订单移除出点订单列表的订单
                    List<Order> orders1=orderList.subList(k,j);
                    List<Order> removeOrder=new ArrayList<>();
                    orders1.parallelStream().forEach(
                            o->{
                                removeOrder.add(o);
                            }
                    );
                    //定义一个新订单,用户合并其他订单
                    Order order=new Order();
                    order.setSellerId(orders1.get(0).sellerId);
                    List<Item> itemList=new ArrayList<>();
                    orders1.forEach(o->{
                        itemList.addAll(o.getOrderItems());
                    });
                    order.setOrderItems(itemList);
                    order.setTotalPrice(itemList.parallelStream().map(Item::getPrice)
                            .reduce(Double::sum).get());
                    //订单列表移除出合并订单
                    orderList.removeAll(removeOrder);
                    //订单列表加入新的订单
                    orderList.add(order);
                    //对合并后的新的订单列表进行重新排序
                    orderList=orderList.parallelStream()
                            .sorted(Comparator.comparing(Order::getTotalPrice).reversed())
                            .collect(Collectors.toList());
                    //将k值归0
                    k=0;
                }
            }
            orders.addAll(orderList);
        }
        return orders;
    }
学新通

Main函数

    public static void main(String[] args) {

        // TODO Auto-generated method stub
        List<Item> items = new ArrayList<>();

        // TODO Auto-generated method stub
        for (int i = 0; i < 20; i  ) {
            Item item = new Item();
            Random random = new Random();
            item.price = random.nextInt(999) random.nextDouble();
            item.sellerId = random.nextInt(10)   100;
            items.add(item);
        }
        List<Order> orders = packageItemsToOrders(items);
        for (Order order : orders) {
            System.out.println("订单价格"   order.getTotalPrice()   "元,"
                     " 商品:"   order.getOrderItems()
                      "商家id"   order.getSellerId());
        }
    }
学新通

运行结果

学新通

参考链接

1.从零开始学贪心算法
2. 贪心算法与动态规划的区别
3.贪心算法(百度百科)

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhffebgh
系列文章
更多 icon
同类精品
更多 icon
继续加载