Как равномерно разделить элементы на группы в заданных пропорциях?

Рейтинг: -1Ответов: 2Опубликовано: 19.06.2023

Имеется массив/перечислимый объект некоторых элементов. Есть три группы, на которые нужно разделить этот массив элементов в заданных пропорциях А, B, C (A+B+C=1). То есть если всего элементов N, то в первой группе окажется N*A элементов, во второй N*B элементов, в третьей - N*С. Причем элементы попали в эти группы наиболее равномерным (насколько это возможно) образом, то есть вероятности попадания элемента в любую из групп одинаковы.

Необходимо написать алгоритм этого разделения. Желательно на Java.

Условное дано:

double A; //доля первой группы
double B; //доля второй группы
double C; //доля третьей группы
int[] items; // элементы

В своих изысканиях я придумал следующий алгоритм. По заданной доле и количеству элементов вычисляется, сколько элементов будет в группе. По этому количеству определяется шаг, с которым будет проходиться список элементов. Алгоритм с конца проходит список извлекая из него элементы через указанный шаг, и складывая его в накапливаемую группу. Функция возвращает выделенную группу и применяется к оставшимся элементам уже с другим значением доли.

private static List<Integer> take(List<Integer> items, int count) {
        double step = (double) items.size() / count;
        int size = items.size();
        List<Integer> res = new LinkedList<>();
        for (int i = 0; i < count; i++) {
            int index = (int) Math.round(size - 1 - i * step);
            Integer item = items.remove(index);
            res.add(0, item);
        }
        return res;
    }

Алгоритм в целом работает, но качество его равномерности математически оценить сложно. Визуально получается так:

  1. Для пяти элементов
A = 55,00% B = 35,00% C = 10,00%

 0  1  2  3  4 
 C  A  A  B  A 
---------------
    A  A     A 
          B    
 C 
  1. Для десяти элементов
A = 55,00% B = 35,00% C = 10,00%

 0  1  2  3  4  5  6  7  8  9 
 B  A  A  C  A  B  A  A  B  A 
------------------------------
    A  A     A     A  A     A 
 B              B        B    
          C                   
  1. Для десяти элементов
 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 
 B  A  C  A  A  B  A  B  A  B  A  B  A  C  A  A  B  A  B  A 
------------------------------------------------------------
    A     A  A     A     A     A     A     A  A     A     A 
 B              B     B     B     B              B     B    
       C                                C             

Прошу сообщество попытаться оценить качество такого решения и предложить варианты улучшения или альтернативы. Возможно, такая задача уже решена математиками или некоторыми программистами.

Ответы

▲ 0

Ладно, будем считать что целочисленные количества элементов вы уже получили.

Теперь используйте трехмерный алгоритм Брезенхэма (вот какая-то реализация), только вместо вывода трехмерных точек делайте вывод того элемента, чья координата увеличивается (т.е. в момент point[1] += y_inc; выводите B и т.п.)

▲ 0

Если я правильно понимаю, то проблема в том, что в первую группу попадает 55% элементов, во вторую 35 %, в 3 10%, а должно быть примерно 33% в каждой группе. Есть две идеи :

  1. Хранить 3 группы чисел в 3х разных массивах с размером 33 % от общего числа элементов.
  2. Предусмотреть для рандомного распределения элементов условие что если группа А больше установленного размера, то элемент рандомно добавляется либо в В, либо в С. И так для каждой группы.