【问题标题】:Distribute i no. of items in n no. of boxes, where each box has different capacity level c1, c2分配我没有。 n 中的项目数。盒子的数量,其中每个盒子有不同的容量等级 c1, c2
【发布时间】:2023-04-03 04:29:01
【问题描述】:

我必须分发 i。不。项目入 n 号。每个盒子具有不同容量级别 c1、c2 ... cn 的盒子数量。我想按容量的比例分配物品。所以容量最高的盒子将包含最高的号码。的项目,反之亦然。容量可能不是按升序排列的。容量也可以为 0。此外,如果没有。的项目超过总容量,然后将所有盒子装满它们的最大容量。

这个问题已经有解决方案了吗?

因为我已经写了以下算法。但是效率不高。它还在以下输入处无限循环。由于 -2 差异永远不会解决。所以肯定还有其他用例会出现问题。

int[] arrCap = {1,1,0,1,1};
new Distributor(arrCap, 2).distribute();



import java.util.Arrays;
public class Distributor {

    /** Capacity of each box */
    private final int[] boxCapacity;
    /** Total no. of boxes */
    private final int NO_OF_BOXES;
    /** Total no. of items that are to be distributed into each box  */
    private final int NO_OF_ITEMS;
    /** Total capacity available. */
    private long totalCapacity;
    /** Fractionally ratio distributed items according to capacity */
    private float[] fractionalRatios;
    /** Ratio distributed items according to capacity */
    private int[] ratioDistributedCapacity;
    /** Sorted Rank of distributed items in ascending / descending order */
    private int[] rankIndex;
    /** The difference between the totalCapacity and total of ratioDistributedCapacity */
    private int difference;

    /**
     * Validates the total capacity and no. of items to be distributed.
     * Initializes the distributor with box capacity array, no of items.
     * Implicitly calculates no. of boxes as length of box capacity array.
     * @param boxCapacity Array of capacity of each box.
     * @param noOfItems No. of Items to be distributed.
     */
    public Distributor(int[] boxCapacity, int noOfItems) {
        calculateBoxes(boxCapacity);
        this.boxCapacity = boxCapacity;
        this.NO_OF_ITEMS = noOfItems;
        NO_OF_BOXES = boxCapacity.length;
        ratioDistributedCapacity = new int[NO_OF_BOXES];
        rankIndex = new int[NO_OF_BOXES];
    }

    /**
     * Calculates the ratio into which the items are to be distributed.
     * Actually assigns the items into each box according to the ratio.
     * @return Array of int[] containing ratio distributed items according to its capacity.
     */
    public int[] distribute() {
        // If NO_OF_ITEMS to be distributed is more than totalCapacity then distribute all the items upto full capacity
        if (NO_OF_ITEMS >= totalCapacity) {
            ratioDistributedCapacity = boxCapacity;
        } else {
            calculateRatioAndDistribute();
        }
        return ratioDistributedCapacity;
    }

    /**
     * Calculates the ratio & distributes the items according to the capacity.
     */
    private void calculateRatioAndDistribute() {
        fractionalRatios = new float[NO_OF_BOXES];
        for (int i=0; i<NO_OF_BOXES; i++) {
            fractionalRatios[i] = ((float) boxCapacity[i] * (float) NO_OF_ITEMS) / (float) totalCapacity;
            ratioDistributedCapacity[i] = Math.round(fractionalRatios[i]);
        }
        print(fractionalRatios);
        print(ratioDistributedCapacity);
        // keep redistributing the difference until its not 0
        while ((difference = rectifyAndGetDistributionResult()) != 0) {
            redistribute();
        }
        print(ratioDistributedCapacity);
    }

    /**
     * Redistributes the difference between the already allotted ratioDistributedCapacity array.
     * Also if the difference is 0 that means everything is already settled.
     * No more further need to do anything.
     * @param difference the difference that needs to be settled to equal the no. of items with total distributed items.
     */
    private void redistribute() {
        if (difference > 0) {
            // calculate distribution ranks in ascending order
            calculateDistributionRanks(true);   // orderDescending = true
            // eliminate the invalid ranks from rankIndex
            eliminateInvalidRanks();
            // In case all the ranks have become invalid. In this case the rankIndex will be empty.
            // So we need to re calculate the distribution ranks in opposite order.
            if (rankIndex.length == 0) {
                calculateDistributionRanks(false);  // orderDescending = false
            }
        } else if (difference < 0) {
            // calculate distribution ranks in descending order
            calculateDistributionRanks(false);  // orderDescending = false
            // eliminate the invalid ranks from rankIndex
            eliminateInvalidRanks();
            // In case all the ranks have become invalid. In this case the rankIndex will be empty.
            // So we need to re calculate the distribution ranks in opposite order.
            if (rankIndex.length == 0) {
                calculateDistributionRanks(true);   // orderDescending = true
            }
        }
        // add / substract 1 from the ratioDistributedCapacity of the element in order of the rankIndex
        // according to negative / positive difference until the difference becomes 0.
        final int len = rankIndex.length;
        for (int i=0; i<len; i++) {
            if (difference == 0) {
                break;
            } else if (difference > 0) {
                ratioDistributedCapacity[ rankIndex[i] ]++;
                difference--;
            } else if (difference < 0) {
                ratioDistributedCapacity[ rankIndex[i] ]--;
                difference++;
            }
        }
    }

    /**
     * If the value of any ratioDistributedCapacity element exceeds its capacity or is less than 0,
     * revert it with its initial capacity value.
     */
    private void rectify() {
        for (int i=0; i<NO_OF_BOXES; i++) {
            ratioDistributedCapacity[i] = ((ratioDistributedCapacity[i] > boxCapacity[i]) || (ratioDistributedCapacity[i] < 0)) ? boxCapacity[i] : ratioDistributedCapacity[i];
        }
    }

    /**
     * Calculates the distribution ranks i.e. indexes of fractionalRatios array.
     * Sorts them into ascending or descending order.
     * @param orderDesc Sort order. true for descending and false for ascending.
     */
    private void calculateDistributionRanks(boolean orderDesc) {
        // Copy fractionalRatios array to another tmp array. Note:- Use fractionalRatios so ranking can be more accurate.
        float[] tmp = Arrays.copyOf(fractionalRatios, NO_OF_BOXES);
        // Sort the array in ascending order
        Arrays.sort(tmp);
        // re-initialize the rankIndex array
        rankIndex = new int[NO_OF_BOXES];
        for (int i=0; i<NO_OF_BOXES; i++) {
            innerLoop: for (int j=0; j<NO_OF_BOXES; j++) {
                if (tmp[i] == fractionalRatios[j]) {
                    // Store the array index of unsorted array if its value matches value of sorted array.
                    rankIndex[i] = j;
                    break innerLoop;
                }
            }
        }
        // reverse the rank array if orderDesc flag is true
        if (orderDesc) reverse();
        print(rankIndex);
    }

    /**
     * Remove the indexes from rank which are already full or equal to 0
     * or are not eligible for increment / decrement operation.
     */
    private void eliminateInvalidRanks() {
        final int len = rankIndex.length;
        int invalidRankCount = 0;
        final int markInvalidRank = -1;
        for (int i = 0; i < len; i++) {
            if (boxCapacity[rankIndex[i]] <= 0) {
                // mark this rank number as invalid, for removal
                rankIndex[i] = markInvalidRank;
                invalidRankCount++;
                continue;
            }
            if (difference > 0) {
                if ((ratioDistributedCapacity[rankIndex[i]] >= boxCapacity[rankIndex[i]])) {
                    // mark this rank number as invalid, for removal
                    rankIndex[i] = markInvalidRank;
                    invalidRankCount++;
                    continue;
                }
            } else if (difference < 0) {
                if (ratioDistributedCapacity[rankIndex[i]] <= 0) {
                    // mark this rank number as invalid, for removal
                    rankIndex[i] = markInvalidRank;
                    invalidRankCount++;
                    continue;
                }
            }
        }
        int[] tmp = new int[(len - invalidRankCount)];
        int j = 0;
        for (int i = 0; i < len; i++) {
            if (rankIndex[i] != markInvalidRank) {
                tmp[j++] = rankIndex[i];
            }
        }
        rankIndex = tmp;
        print(rankIndex);
    }

    /**
     * Rectifies the elements value inside ratioDistributedCapacity.
     * Calculates the total of already distributed items.
     * @return Difference between total distributed items and initial no. of items that were to be distributed.
     */
    private int rectifyAndGetDistributionResult() {
        rectify();
        int remaining = NO_OF_ITEMS;
        for (int tmp: ratioDistributedCapacity) {
            remaining -= tmp;
        }
        return remaining;
    }

    /**
     * Validates the capacity array and no. of items to be distributed.
     * @param arrCapacity Array of capacity of each box.
     */
    private void calculateBoxes(int[] arrCapacity) {
        for (int i: arrCapacity) {
            totalCapacity += i;
        }
    }

    /**
     * Prints the array elements and the total of the elements within it.
     * @param x
     */
    private void print(int[] x) {
        final int len = x.length;
        final StringBuilder sb = new StringBuilder("");
        for (int i=0; i<len; i++) {
            sb.append(x[i]).append(", ");
        }
        System.out.println(sb.toString());
    }

    /**
     * Prints the array elements and the total of the elements within it.
     * @param x
     */
    private void print(float[] x) {
        final int len = x.length;
        final StringBuilder sb = new StringBuilder("");
        for (int i=0; i<len; i++) {
            sb.append(x[i]).append(", ");
        }
        System.out.println(sb.toString());
    }

    private void reverse() {
        final int len = rankIndex.length;
        for (int i=0; i < (len/2); i++) {
            rankIndex[i] += rankIndex[len - 1 - i]; //  a = a+b
            rankIndex[len - 1 - i] = rankIndex[i] - rankIndex[len - 1 - i]; //  b = a-b
            rankIndex[i] -= rankIndex[len - 1 - i]; //  a = a-b
        }
    }
}

【问题讨论】:

    标签: java algorithm


    【解决方案1】:

    我想知道你是否做简单的数学运算来找出比率可以轻松解决问题。 所以Ni是你必须分发的物品数量,B所有盒子容量的总和(即c1+c2+...+cn

    所以R = Ni/B 将是你的口粮。 R*cn 将是您想要放入框中的项目数 n

    示例: 您总共有 8 个项目。和 2 盒容量 4、12(N1 = 16)。 R = 8/(4+12) = 1/2 对于框将有R*4 = 2 第二个盒子会有R*12 = 6

    当然,您必须处理舍入问题,框中将有 +/-1 个项目。

    PS 为了解决舍入问题,您将执行以下操作。 你创建一个变量sumSoFar = 0 //initial value

    box1 包含R*c1 然后你添加sumSoFar+=Math.round(R*c1)

    box2 包含Math.round(R*c2) 然后你添加sumSoFar+=Math.round(R*c2)

    对于你放N1-sumSoFar 的最后一个框,你分配所有值。

    代码如下:

    static int[] distribute(int[] boxes, int items) {
        int[] result = new int[boxes.length];
        int sumSoFar = 0;
        int totalCapacity = 0;
        for (int box : boxes) {
            totalCapacity += box;
        }
        float R = (float) items / totalCapacity;
    
        for (int i = 0; i < boxes.length - 1; i++) {
            int box = boxes[i];
            result[i] = Math.round(R * box);
            sumSoFar += result[i];
        }
        result[boxes.length - 1] = items - sumSoFar;
        return result;
    }
    

    调用:

    System.out.println(Arrays.toString(distribute(new int[]{1, 2}, 10)));
    System.out.println(Arrays.toString(distribute(new int[]{4, 12}, 8)));
    

    结果:

    [3, 7]
    [2, 6]
    

    【讨论】:

    • 我已经分发了这些物品。我无法调整舍入问题。
    • @iaL - 这是正确且简单的解决方案。您在“调整舍入问题”时遇到什么麻烦?
    【解决方案2】:

    想到了两种方法

    1. 最佳舍入

      将问题视为最佳舍入问题。由于您希望物品“按其容量的比例”分布在盒子中,因此对于每个盒子计算它们的份额,即“capacity / sum of all capacities”。然后将该份额乘以项目数。这通常会给你每个盒子的一小部分项目。我假设你的物品是不可分割的。现在您只需要确定如何“优化四舍五入”这些值。 Here 是一个讨论如何做到这一点的 SO 问题。您还可以搜索“整数约束下的最佳舍入”以找到有关该主题的几篇论文。

    2. 公平分工

      使用fair division 处理问题。该链接涵盖了许多方法(大多数是近似值)。但是,关键部分将是您的每个盒子如何为每个项目分配一个值,以便算法知道如何打包项目。您可以使用与其容量成比例的指标。

    【讨论】:

    • 谢谢。我会过去告诉你的。
    • @iaL 好的,请记住,无论 cmets 在其他地方说什么,都没有一种“正确且简单的解决方案”方法可以对所有情况进行最佳舍入。您必须为您的数据集测试一些。有一些“预算”方法,您可以保留预算,以一种或另一种方式舍入多少,并在下一个数字上进行补偿。有一些随机方法,您可以尝试将平均引入的错误降至最低。 Here 是一篇对这个问题进行了很好讨论的论文。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-06-18
    • 1970-01-01
    • 1970-01-01
    • 2014-05-10
    相关资源
    最近更新 更多