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

Есть ли у кого-нибудь хороший алгоритм для получения упорядоченного списка целых чисел, например:
[1, 3, 6, 7, 8, 10, 11, 13, 14, 17, 19, 23, 25, 27, 28]

в заданное количество упорядоченных подсписок одинакового размера, то есть для 4 это будет:
[1, 3, 6] [7, 8, 10, 11] [13, 14, 17, 19] [23, 25, 27, 28]

Требование состоит в том, чтобы все подсписки были упорядочены и как можно более близки по размеру.

Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
4
0
5 035
6
Перейти к ответу Данный вопрос помечен как решенный

Ответы 6

Вот мое собственное рекурсивное решение, вдохновленное сортировкой слиянием и обходом дерева по ширине:

private static void splitOrderedDurationsIntoIntervals(Integer[] durations, List<Integer[]> intervals, int numberOfInterals) {
    int middle = durations.length / 2;
    Integer[] lowerHalf = Arrays.copyOfRange(durations, 0, middle);
    Integer[] upperHalf = Arrays.copyOfRange(durations, middle, durations.length);
    if (lowerHalf.length > upperHalf.length) {
        intervals.add(lowerHalf);
        intervals.add(upperHalf);
    } else {
        intervals.add(upperHalf);
        intervals.add(lowerHalf);
    }
    if (intervals.size() < numberOfIntervals) {
        int largestElementLength = intervals.get(0).length;
        if (largestElementLength > 1) {
            Integer[] duration = intervals.remove(0);
            splitOrderedDurationsIntoIntervals(duration,  intervals);
        }
    }
}

Я надеялся, что у кого-нибудь есть предложение по итеративному решению.

Ответ принят как подходящий

Равномерное разделение списков означает, что у вас будет два размера списков - размер S и S + 1.

С N подсписками и X элементами в оригинале вы получите:

floor (X / N) - количество элементов в меньших подсписках (S), а X% N - количество больших подсписок (S + 1).

Затем выполните итерацию по исходному массиву и (глядя на ваш пример) сначала создайте небольшие списки.

Что-то вроде этого может быть:

 private static List<Integer[]> splitOrderedDurationsIntoIntervals(Integer[] durations, int numberOfIntervals) {

    int sizeOfSmallSublists = durations.length / numberOfIntervals;
    int sizeOfLargeSublists = sizeOfSmallSublists + 1;
    int numberOfLargeSublists = durations.length % numberOfIntervals;
    int numberOfSmallSublists = numberOfIntervals - numberOfLargeSublists;

    List<Integer[]> sublists = new ArrayList(numberOfIntervals);
    int numberOfElementsHandled = 0;
    for (int i = 0; i < numberOfIntervals; i++) {
        int size = i < numberOfSmallSublists ? sizeOfSmallSublists : sizeOfLargeSublists;
        Integer[] sublist = new Integer[size];
        System.arraycopy(durations, numberOfElementsHandled, sublist, 0, size);
        sublists.add(sublist);
        numberOfElementsHandled += size;
    }
    return sublists;
}

Я предполагаю, что в java есть какая-то функция subsequence?

Svante 26.11.2008 13:09

Вот решение для Python. Вы можете перевести его на Java, вам нужен способ получить часть списка, а затем вернуть его. Однако вы не можете использовать подход генератора, но вы можете добавить каждый подсписок в новый список.

псевдокод ...

private static void splitOrderedDurationsIntoIntervals(Integer[] durations, List<Integer[]> intervals, int numberOfInterals) {

    int num_per_interval = Math.floor(durations.length / numberOfInterals);
    int i;
    int idx;

    // make sure you have somewhere to put the results
    for (i = 0; i < numberOfInterals; i++) intervals[i] = new Integer[];

    // run once through the list and put them in the right sub-list
    for (i = 0; i < durations.length; i++)
    {
        idx = Math.floor(i / num_per_interval);
        intervals[idx].add(durations[i]);
    }
}

Этот код нужно немного привести в порядок, но я уверен, что вы уловили суть. Также я подозреваю, что список интервалов неравномерного размера будет в конце, а не в начале. Если вы действительно хотите, чтобы это было так, вы, вероятно, можете сделать это, изменив порядок цикла.

Это должен быть ответ в более повторяющейся форме.

public static void splitList(List<Integer> startList, List<List<Integer>> resultList, 
        int subListNumber) {
    final int subListSize = startList.size() / subListNumber;
    int index = 0;
    int stopIndex = subListSize;
    for (int i = subListNumber; i > 0; i--) {
        resultList.add(new ArrayList<Integer>(startList.subList(index, stopIndex)));
        index = stopIndex;
        stopIndex =
            (index + subListSize > startList.size()) ? startList.size() : index + subListSize;
    }
}

Вы могли бы подумать о чем-то вроде этого:


public static int[][] divide(int[] initialList, int sublistCount)
    {
        if (initialList == null)
            throw new NullPointerException("initialList");
        if (sublistCount < 1)
            throw new IllegalArgumentException("sublistCount must be greater than 0.");

        // without remainder, length / # lists will always be the minimum 
        // number of items in a given subset
        int min = initialList.length / sublistCount;
        // without remainer, this algorithm determines the maximum number 
        // of items in a given subset.  example: in a 15-item sample, 
        // with 4 subsets, we get a min of 3 (15 / 4 = 3r3), and 
        // 15 + 3 - 1 = 17.  17 / 4 = 4r1.
        // in a 16-item sample, min = 4, and 16 + 4 - 1 = 19. 19 / 4 = 4r3.
        // The -1 is required in samples in which the max and min are the same.
        int max = (initialList.length + min - 1) / sublistCount;
        // this is the meat and potatoes of the algorithm.  here we determine
        // how many lists have the min count and the max count.  we start out 
        // with all at max and work our way down.
        int sublistsHandledByMax = sublistCount;
        int sublistsHandledByMin = 0;
        while ((sublistsHandledByMax * max) + (sublistsHandledByMin * min)
                    != initialList.length)
        {
            sublistsHandledByMax--;
            sublistsHandledByMin++;
        }

        // now we copy the items into their new sublists.
        int[][] items = new int[sublistCount][];
        int currentInputIndex = 0;
        for (int listIndex = 0; listIndex < sublistCount; listIndex++)
        {
            if (listIndex < sublistsHandledByMin)
                items[listIndex] = new int[min];
            else
                items[listIndex] = new int[max];

            // there's probably a better way to do array copies now.
            // it's been a while since I did Java :)
            System.arraycopy(initialList, currentInputIndex, items[listIndex], 0, items[listIndex].length);
            currentInputIndex += items[listIndex].length;
        }

        return items;
    }

Это не совсем отполировано - я попал в бесконечный цикл (я думаю), когда попытался передать массив из 18 элементов с 10 подсписками. Я думаю, что алгоритм не работает, когда min == 1.

Это должно быть довольно быстро. Удачи :)

Другие вопросы по теме