Распределение всего элемента по соотношению

Цель:

Скажем, у меня есть X рабочих на фруктовой плантации. На плантации выращивают яблоки, груши и виноград.

В конце дня бригадир оценивает каждого рабочего с коэффициентом. Сумма всех соотношений равна 100. Соотношение определяет, как распределить фрукты среди рабочих в конце дня.

Как мне распределить фрукты между всеми рабочими, чтобы каждый из них получил свою справедливую долю (в пределах определенной случайности для учета целочисленного деления). Делятся только целые плоды, поэтому получаются целые числа. И все плоды надо раздать.

Я делаю это с примерно 20 рабочими, так что прямо сейчас соотношение составляет около 0,05 на одного рабочего.

Что я пробовал (псевдокод):

for each worker:
  if applesGiven < appleStock:
    worker.give(ratio * applestock);
  if pearsGiven < pearStock:
    worker.give(ratio * pearStock);
  if grapesGiven < grapeStock:
    worker.give(ratio * grapeStock);

Я бы позволил определить точное количество [фруктов], которые они дали, с помощью boolean Roundup, которое было инициализировано случайным логическим значением и переключалось после каждого обработанного фрукта.

Что я пробовал (полный код):

public void balance() {
        boolean roundUp = random.nextBoolean();
        for (Employee e : employees) {
            double ratio = e.getRatio();
            if (applePlanned < appleNeeded) {
                int apple;
                if (roundUp) {
                    apple = (int) Math.ceil(ratio * appleNeeded);
                } else {
                    apple = (int) Math.floor(ratio * appleNeeded);
                }
                e.setrapple(apple);
                applePlanned += apple;
                roundUp = !roundUp;
            }

            if (pearPlanned < pearNeeded) {
                int pear;
                if (roundUp) {
                    pear = (int) Math.ceil(ratio * pearNeeded);
                } else {
                    pear = (int) Math.floor(ratio * pearNeeded);
                }
                e.setrpear(pear);
                pearPlanned += pear;
                roundUp = !roundUp;
            }

            if (grapePlanned < grapeNeeded) {
                int grape;
                if (roundUp) {
                    grape = (int) Math.ceil(ratio * grapeNeeded);
                } else {
                    grape = (int) Math.floor(ratio * grapeNeeded);
                }
                e.setrgrape(grape);
                grapePlanned += grape;
                roundUp = !roundUp;
            }
}

Проблемы, с которыми я столкнулся:

  • Распространяется только около 3/4 всех предметов.
  • Когда у меня есть четное количество фруктов, логическое значение получает одинаковое значение в начале каждого нового человека.

Спасибо за внимание!

Ответы на java, python или псевдокоде, пожалуйста, это то, что я умею читать.


person Difusio    schedule 21.09.2013    source источник
comment
Не видя точного кода, невозможно сказать, почему у вас возникают те или иные проблемы. Внезапно я бы сделал это так, чтобы все время округлять в меньшую сторону, но отслеживать общую ошибку округления для каждого работника (которая будет числом в интервале [0,3) ). Затем для оставшихся фруктов, для каждого работника, чья ошибка округления находится в [2,3], дайте им два случайных фрукта, а если он в [1,2), дайте им один случайный фрукт. Это оставит ошибку каждого рабочего в диапазоне [0,1); теперь просто выдавайте случайные фрукты, начиная с рабочего с наибольшей ошибкой.   -  person ajb    schedule 21.09.2013
comment
Я добавил код по вашему запросу. Почему ошибка округления в этом интервале?   -  person Difusio    schedule 21.09.2013
comment
Я имею в виду следующее: если вы подсчитаете, сколько яблок вы хотите дать, и округлите его в меньшую сторону, разница (ошибка, как я ее назвал) будет между 0 и 1. Поскольку вы делаете это для трех фруктов, общая сумма из трех ошибок будет от 0 до 3. Это трудно объяснить в коротком комментарии, но я не чувствовал, что это достаточно хорошо, чтобы публиковать в качестве ответа. Надеюсь это поможет.   -  person ajb    schedule 22.09.2013
comment
Спасибо за публикацию кода. Я не могу понять, почему будут распределены только 3/4 предметов; Я попробовал ваш код, и этого не происходит. Убедитесь, что коэффициенты сотрудников в сумме составляют 1,0. Что касается второго вопроса: вы имеете в виду четное количество различных видов фруктов? (Вместо 3, как вы нам показали.) Это было бы потому, что вы выполняете roundUp = !roundUp четное количество раз; решение состоит в том, чтобы добавить второе логическое значение roundUpStart, которое дополняется (roundUpStart = !roundUpStart) только один раз для каждого сотрудника, и инициализировать roundUp для каждого сотрудника.   -  person ajb    schedule 22.09.2013
comment
Хммм, это, безусловно, решает проблему 2. Я рассмотрю внешние факторы, почему я не раздаю все фрукты, а код делает это за вас. Спасибо.   -  person Difusio    schedule 23.09.2013


Ответы (2)


Используйте двойную математику, округлите в меньшую сторону, а затем случайным образом раздайте оставшиеся фрукты, взвешенные на основе соотношения. Обратите внимание: вы можете сделать это намного менее уродливым с помощью объектной ориентации и циклов, но это только начало.

public void distribute(int apple, int pear, int grape) {
    double total = apple + pear + grape;
    double appleRatio = apple/total;
    double pearRatio = pear/total;
    double grapeRatio = grape/total;

    // apple worker
    int appleWorkerApple = (int) (appleRatio*apple);
    int appleWorkerPear = (int) (appleRatio*pear);
    int appleWorkerGrape = (int) (appleRatio*grape);

    // pear worker
    int pearWorkerApple = (int) (pearRatio*apple);
    int pearWorkerPear = (int) (pearRatio*pear);
    int pearWorkerGrape = (int) (pearRatio*grape);

    // grape worker
    int grapeWorkerApple = (int) (grapeRatio*apple);
    int grapeWorkerPear = (int) (grapeRatio*pear);
    int grapeWorkerGrape = (int) (grapeRatio*grape);

    int appleRemain = apple - appleWorkerApple - pearWorkerApple - grapeWorkerApple;
    int pearRemain = pear - appleWorkerApple - pearWorkerApple - grapeWorkerApple;
    int grapeRemain = grape - appleWorkerApple - pearWorkerApple - grapeWorkerApple;

    Random r = new Random();
    while(appleRemain > 0 && pearRemain > 0 && grapeRemain > 0) {
        double target = r.nextDouble();
        switch(r.nextInt(3)) {
        case 0:
            if(appleRemain > 0) {
                appleRemain--
                if(target < appleRatio)
                    appleWorkerApple++;
                else if (target < appleRatio + grapeRatio)
                    pearWorkerApple++;
                else
                    grapeWorkerApple++;
            }
            break;
        case 1:
            if(grapeRemain > 0)
            // etc.
        }
    }
}
person durron597    schedule 21.09.2013
comment
О, это отличное начало. Мне просто не хватает соотношения яблок и фруктов. Почему ты это используешь? Соотношение идет на рабочего, скажем, у мастера 20 рабочих, и каждому из них он назначает процент от выработки. Итого до 100%. - person Difusio; 21.09.2013
comment
@Difusio Если бригадир оценивает рабочих в конце дня, то вам нужно выяснить, какой будет оценка, верно? В любом случае, когда я отвечал на этот вопрос, я думал, что у вас ровно три рабочих, по одному на каждый завод. Дайте мне знать, если вам нужно больше разъяснений. - person durron597; 21.09.2013
comment
Да, у меня есть X работников, которые делают всю работу вместе, так что никаких конкретных задач, они просто получают оценку или долю дохода за день. Прямо сейчас я пошел с: - Перетасовать список - Сортировать по соотношению - Раздать фрукты сверху вниз, если что-то осталось, начать снова сверху. - Для следующего фрукта снова начните с шага 1. Но это кажется немного предвзятым по отношению к ним с более высоким коэффициентом. - person Difusio; 23.09.2013

Это не особенно полезно, потому что я слишком много использовал Numpy, но я поделюсь, так как это актуально

import numpy
import random

# apple, bannana, grapes, guava, melon, pear
fruits = numpy.array([100, 150, 175, 200, 230, 247])

# Bill, Bob, Dan, Fred, Joe
ratios = numpy.array([21, 7, 32, 13, 27])

# Original fruit amount for each worker: 0
worker_fruits = numpy.zeros((5, 6), dtype=int)
worker_lucky  = numpy.zeros((5, 6), dtype=float)

# For each worker with his ratio
for worker, lucky, ratio in zip(worker_fruits, worker_lucky, ratios):
    # Give him fruits, storing partials as weighting
    to_give = (ratio * fruits) / 100
    lucky  += to_give % 1
    worker += to_give

# Calculate how much we have left over
spares = fruits - worker_fruits.sum(axis=0)

# Share it out in a weighted distribution
for fruit, lucky, numspare in zip(worker_fruits.transpose(), worker_lucky.transpose(), spares):
    if numspare:
        indexes = numpy.arange(len(fruit))
        add_to = numpy.random.choice(indexes, replace=False, size=numspare, p=lucky/numspare)
        fruit[add_to] += 1

# Our results!
worker_fruits
#>>> array([[21, 31, 36, 42, 49, 51],
#>>>        [ 7, 11, 12, 14, 16, 18],
#>>>        [32, 48, 56, 64, 74, 79],
#>>>        [13, 19, 23, 26, 29, 32],
#>>>        [27, 41, 48, 54, 62, 67]])

# Proof it's perfectly shared
fruits - worker_fruits.sum(axis=0)
#>>> array([0, 0, 0, 0, 0, 0])
person Veedrac    schedule 21.09.2013
comment
О боже, спасибо, теперь позвольте мне углубиться в numpy, чтобы увидеть, как преобразовать это в Java. Я дам тебе знать, если разберусь. - person Difusio; 21.09.2013
comment
Просто предупреждение: numpy.random.choice(indexes, replace=False, size=numspare, p=lucky/numspare) скрывает много сложностей. Я предлагаю подниматься от durron597, а не спускаться с моего. - person Veedrac; 21.09.2013