资源调度之最大最小分配算法java实现

    Mini-max算法是一种递归或回溯算法,用于决策和博弈论。它为资源分配提供了一个最佳的分配方式,最近在项目中将这种算法应用到了资源分配。当每个需求方的权重相等时,需求方对于资源都是公平的,应该按照先后顺序分配,直到资源分配完,或者全部满足即分配结束。


  1. 下面的代码为基础的无权分配方式,即所有权重都是一样的

    最大最小公平算法定义如下(不带权):

         1、资源按照需求递增的顺序进行分配;

         2、不存在用户获得的资源超过自身的需求;

         3、对于未满足的用户,等价分享剩余资源。​​​​​​​

  2.  实现逻辑描述:首先假定用户集合有n个用户,1到n,他们对资源的需求已经排序完毕,满足s1<s2< .... <sn,资源总量为S。   

  3. 将资源S/n分配给需求最小的1用户,这很可能已经超出了用户1的需求; 

  4. 将超出的部分回收,再次将(S-s1)/(n-1)的资源分配给用户2,依次次重复上述过程,直到某一次分给该用户的资源不满足该用户的需求;

package com.yzf.task.decision.utils;

import java.util.*;

/**
 * @Author PSY
 * @Date 2022/3/24 21:34
 * 最大最小公平算法分配,不带权
 * @Desc
 */
public class MaxMinAllocate {

    public static void main(String[] args) {
        //总共资源数
        int total = 137;
        List<Integer> needList = new ArrayList<>(Arrays.asList(10, 22, 33, 45, 1, 2, 34, 12));
        int n = needList.size();
        //求出平均值
        int average = total / n;
        System.out.println("资源的平均数为:" + average);
        //结果集
        List<Integer> result = new ArrayList<>();
        //剩余数目
        int remain = 0;
        //存入满足的坐标
        Map<Integer, Boolean> okMap = new HashMap<>();
//        第一轮分配
        for (int i = 0; i < n; i++) {
            if (needList.get(i) >= average) {
                result.add(i, average);
            } else {
                result.add(i, needList.get(i));
                okMap.put(i, true);
                remain += average - needList.get(i);
            }
        }
        System.out.println("分配需求数据:" + needList);
        System.out.println("第一次分配结果:" + result + ",剩下的结果:" + remain);
        //如果还有剩余继续分配
        while (remain > 0) {
            if (n == okMap.size()) break;
            average = remain / (n - okMap.size());
            remain = 0;
            for (int i = 0; i < n; i++) {
                if (Objects.nonNull(okMap.get(i)) && okMap.get(i)) {
                    continue;
                }
                if (needList.get(i) >= average + result.get(i)) {
                    result.set(i, average + result.get(i));
                } else {
                    System.out.println("第" + i + "个已经满足需求");
                    okMap.put(i, true);
                    remain += average + result.get(i) - needList.get(i);
                    result.set(i, needList.get(i));
                }
            }
        }
        System.out.println("分配需求数据:" + needList);
        System.out.println("最终分配结果:" + result + ",剩下资源:" + remain);
    }
}

二:带有权重的分配方式

最大最小公平算法定义如下(带权):

  1、通过权重实现分配的标准化;

  2、不存在用户得到的资源超过自己的需求;

  3、未得到满足的用户,按照权重共享资源。

创建一个需求方对象

package com.yzf.task.decision.utils;

import lombok.Data;

/**
 * @Author PSY
 * @Date 2022/3/24 21:51
 * 最大最小分配实例
 * @Desc
 */
@Data
public class AllocateCaseInfo {

    /**
     * 需求方id
     */
    private Long caseId;

    /**
     * 分配的个数,即计算的理应分配的数量
     */
    private Integer allocateCount;

    /**
     * 权重,如果没有,默认为1
     */

    private Integer weight = 1;

    /**
     * 需要的数量
     */

    private Integer needCount;

}

2:这里计算权重会出现小数点的情况,我这里采用向上取整的方式进行统计,因为只需要一个需求量,至于具体的分配动作由另外的程序控制。

package com.yzf.task.decision.utils;

import cn.hutool.core.util.NumberUtil;

import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Author PSY
 * @Date 2022/3/24 22:15
 * 带有权重的分配实现逻辑,完美兼容无权的分配,
 * jar包中需要导入hutool工具类作为运算工具
 * @Desc
 */
public class MaxMinAllocateWithWeight {

    public static void main(String[] args) {
//      总资源数
        int total = 1;
        //构造数据,导致数据少一个或者多一个
        DecimalFormat decimalFormat = new DecimalFormat("######0");
        decimalFormat.setRoundingMode(RoundingMode.UP);

        AllocateCaseInfo allocateCaseInfo = new AllocateCaseInfo();
        allocateCaseInfo.setCaseId(1L);
        allocateCaseInfo.setWeight(1);
        allocateCaseInfo.setNeedCount(5);

        AllocateCaseInfo allocateCaseInfo1 = new AllocateCaseInfo();
        allocateCaseInfo1.setCaseId(22222L);
        allocateCaseInfo1.setWeight(2);
        allocateCaseInfo1.setNeedCount(6);

        AllocateCaseInfo allocateCaseInfo2 = new AllocateCaseInfo();
        allocateCaseInfo2.setWeight(1);
        allocateCaseInfo2.setCaseId(3L);
        allocateCaseInfo2.setNeedCount(44);

        AllocateCaseInfo allocateCaseInfo3 = new AllocateCaseInfo();
        allocateCaseInfo3.setWeight(1);
        allocateCaseInfo3.setCaseId(83L);
        allocateCaseInfo3.setNeedCount(31);

        List<AllocateCaseInfo> needCaseList = new ArrayList<>();
        needCaseList.add(allocateCaseInfo);
        needCaseList.add(allocateCaseInfo1);


        //第一波分配,按照权重计算出比例,多退少补
        int sumWeight = needCaseList.stream().mapToInt(AllocateCaseInfo::getWeight).sum();
        needCaseList.forEach(needCase -> {
            double div = NumberUtil.div((double) needCase.getWeight(), (double) sumWeight);
            //计算的结果
            double allocateCount = NumberUtil.mul((float) total, div);
            int weightCount = Integer.parseInt(decimalFormat.format(allocateCount));
            //第一轮的权重
            needCase.setAllocateCount(weightCount);
        });

        Map<Long, AllocateCaseInfo> resultMap = new HashMap<>();
        //满足条件的计算
        List<Long> okList = new ArrayList<>();

        for (AllocateCaseInfo caseInfo : needCaseList) {
            int allocateCount = caseInfo.getAllocateCount();
            int needCount = caseInfo.getNeedCount();
            //分配的结果是否大于需要的数量
            if (allocateCount < needCount) {
                caseInfo.setAllocateCount(allocateCount);
            } else {
                okList.add(caseInfo.getCaseId());
                caseInfo.setAllocateCount(needCount);
            }
            resultMap.put(caseInfo.getCaseId(), caseInfo);
        }
        //剩余的数量
        int remain = total - needCaseList.stream().mapToInt(AllocateCaseInfo::getAllocateCount).sum();
        System.out.println("第一轮计算剩余:" + remain + ",满足的条件的个数为:" + okList.size() + ",结果大小:" + resultMap.size());
        for (Long key : resultMap.keySet()) {
            AllocateCaseInfo info = resultMap.get(key);
            System.out.println("权重:" + info.getWeight() + ",需要的数量为" + info.getNeedCount() + ",结果:" + info.getAllocateCount());
        }

        while (remain > 0) {
            if (okList.size() == needCaseList.size()) break;
            List<AllocateCaseInfo> needContinueList = needCaseList.stream().filter(filterCase -> !okList.contains(filterCase.getCaseId())).collect(Collectors.toList());
            //剩余的权重总和
            int sumContinueWeight = needContinueList.stream().mapToInt(AllocateCaseInfo::getWeight).sum();
            int finalRemain = remain;
            //进行剩下的权重计算
            needContinueList.forEach(needCase -> {
                double div = NumberUtil.div((double) needCase.getWeight(), (double) sumContinueWeight);
                //计算的结果
                double allocateCount = NumberUtil.mul((float) finalRemain, div);
                int continueWeight = Integer.parseInt(decimalFormat.format(allocateCount));
                //计算的结果
                needCase.setAllocateCount(needCase.getAllocateCount() + continueWeight);
            });

            for (AllocateCaseInfo caseInfo : needContinueList) {
                int allocateCount = caseInfo.getAllocateCount();
                int needCount = caseInfo.getNeedCount();
                if (allocateCount < needCount) {
                    caseInfo.setAllocateCount(allocateCount);
                } else {
                    okList.add(caseInfo.getCaseId());
                    caseInfo.setAllocateCount(needCount);
                }
                resultMap.put(caseInfo.getCaseId(), caseInfo);
            }
            remain = finalRemain - needContinueList.stream().mapToInt(AllocateCaseInfo::getAllocateCount).sum();
            System.out.println("继续下次计算剩余:" + remain);
        }
        //分配结束,存在小数点的情况,继续按照个数进行分配
        List<AllocateCaseInfo> results = new ArrayList<>();
        for (Long key : resultMap.keySet()) {
            AllocateCaseInfo info = resultMap.get(key);
            results.add(info);
        }
        int allRemain = total - results.stream().mapToInt(AllocateCaseInfo::getAllocateCount).sum();
        while (allRemain > 0 && results.stream().anyMatch(th -> !th.getAllocateCount().equals(th.getNeedCount()))) {
            //对其按照权重进行排序
            results.sort(Comparator.comparing(AllocateCaseInfo::getWeight).reversed());
            //优先将最高权限的分配到最多,兼容小数点的问题,权重为1的话
            for (AllocateCaseInfo temp : results) {
                if (allRemain > 0 && temp.getAllocateCount() < temp.getNeedCount()) {
                    temp.setAllocateCount(temp.getAllocateCount() + 1);
                }
                allRemain = allRemain - 1;
            }
        }
        results = results.stream().sorted(Comparator.comparing(AllocateCaseInfo::getWeight).reversed()).collect(Collectors.toList());
        System.out.println("总数:" + total + "最终计算的结果:");
        for (AllocateCaseInfo info : results) {
            System.out.println("权重:" + info.getWeight() + ",需要的数量为" + info.getNeedCount() + ",结果:" + info.getAllocateCount());
        }
    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值