Код для разделения кусков предоставляется этим фрагментом кода:

def chunks(lst, n):     #n here is 4
    """Yield successive n-sized chunks from lst."""
    for i in range(0, len(lst), n):
        yield lst[i:i + n]

В качестве входных данных у меня есть список со следующими целыми числами:

[11, 45, 74, 24, 27, 55, 37, 97, 15, 36, 54, 7, 41, 77, 28, 36, 22, 214, 110, 40, 41, 14, 6, 35, 6, 7, 62, 2, 34, 1, 30, 5, 4, 8, 9, 7, 5, 7, 0, 0, 3, 0, 0, 1, 2]

Я хочу сгенерировать 4 куска. На выходе я получаю следующее:

[[11, 45, 74, 24, 27, 55, 37, 97, 15, 36, 54, 7], 
[41, 77, 28, 36, 22, 214, 110, 40, 41, 14, 6, 35], 
[6, 7, 62, 2, 34, 1, 30, 5, 4, 8, 9, 7], 
[5, 7, 0, 0, 3, 0, 0, 1, 2]]

Моя проблема в том, что второй список в выводе имеет больший вес, чем другие; распределение чисел не совсем справедливое.
Может ли кто-нибудь дать мне представление о том, как получить справедливое распределение чисел по кускам, включая целые числа?

Я сделал пример вручную:

Ввод: [11,20,2,4,8,13,16,0,1,0,3,6]

Вывод: [[20,1,0,0],[16,6],[13,8],[11,4,3,2]]

2
menbar 13 Авг 2020 в 10:26

1 ответ

Лучший ответ

Сначала мы можем попробовать разделить массив на 2 части так, чтобы их сумма была почти равна.
Затем, когда у нас есть 2 набора, мы можем в дальнейшем применить тот же процесс к каждому из них, чтобы получить 2 * 2 = 4 набора равной суммы.

Алгоритм разделения массива на 2 части примерно равной суммы выглядит следующим образом:

  • Инициализируйте 2 пустых набора для хранения нашего ответа.
  • Отсортируйте массив в обратном порядке.
  • Сохраняя сумму двух наборов, переберите все элементы из массива и добавьте их в набор, который имеет меньшую сумму.
  • Обратите внимание, что это всего лишь приблизительный алгоритм. Если мы хотим найти точно оптимальный ответ, мы можем смоделировать эту проблему как проблему суммы подмножества , и выясним, можем ли мы разделить массив на 2 части, где одна из множества имеет сумму sum/2 или sum/2 - 1 или sum/2 - 2 ... 0 (пробуя каждую из них в таком порядке). Это значительно медленнее по сравнению с нашим приблизительным решением.
def divide_almost_equally_into_2(arr):
    set1 = []
    set2 = []
    sum1 = sum2 = arr_idx = 0
    while arr_idx < len(arr):
        if sum1 < sum2:
            set1.append(arr[arr_idx])
            sum1 += arr[arr_idx]
        else:
            set2.append(arr[arr_idx])
            sum2 += arr[arr_idx]
        arr_idx += 1
    return set1, set2


def divide_almost_equally_into_4(arr):
    arr.sort(reverse=True)
    set1, set2 = divide_almost_equally_into_2(arr)
    set11, set12 = divide_almost_equally_into_2(set1)
    set21, set22 = divide_almost_equally_into_2(set2)
    return [set11, set12, set21, set22]


def main():
    arr = [11,20,2,4,8,13,16,0,1,0,3,6]
    set1, set2, set3, set4 = divide_almost_equally_into_4(arr)
    print(f"{arr}   {sum(arr)}\n")
    print(f"{set1}   {sum(set1)}")
    print(f"{set2}   {sum(set2)}")
    print(f"{set3}   {sum(set3)}")
    print(f"{set4}   {sum(set4)}")


main()

Выход:

[20, 16, 13, 11, 8, 6, 4, 3, 2, 1, 0, 0]   84

[13, 8]   21
[16, 3, 2]   21
[11, 6, 4]   21
[20, 1, 0, 0]   21

РЕДАКТИРОВАТЬ:

Чтобы обобщить тот же алгоритм на количество разбиений n, мы можем использовать кучу:

  • Создайте минимальную кучу размера «n», где каждый элемент представляет собой кортеж формы (current_sum_of_set_i, i).
  • Итак, изначально куча будет содержать элементы (0, 0), (0, 1) ... (0, n-1).
  • Теперь выполните итерацию по массиву с обратной сортировкой и назначьте каждый элемент набору, присутствующему в верхней части кучи.
  • Обновите элемент кучи набора с новой суммой добавленных к нему элементов.
import heapq


def divide_almost_equally(arr, num_chunks):
    arr = sorted(arr, reverse=True)
    heap = [(0, idx) for idx in range(num_chunks)]
    heapq.heapify(heap)
    sets = {}
    for i in range(num_chunks):
        sets[i] = []
    arr_idx = 0
    while arr_idx < len(arr):
        set_sum, set_idx = heapq.heappop(heap)
        sets[set_idx].append(arr[arr_idx])
        set_sum += arr[arr_idx]
        heapq.heappush(heap, (set_sum, set_idx))
        arr_idx += 1
    return sets.values()


def main():
    arr = [11,20,2,4,8,13,16,0,1,0,3,6]
    set1, set2, set3, set4 = divide_almost_equally(arr, 4)
    print(f"{sorted(arr, reverse=True)}   {sum(arr)}\n")
    print(f"{set1}   {sum(set1)}")
    print(f"{set2}   {sum(set2)}")
    print(f"{set3}   {sum(set3)}")
    print(f"{set4}   {sum(set4)}")


main()

Выход:

[20, 16, 13, 11, 8, 6, 4, 3, 2, 1, 0, 0]   84

[20, 1]   21
[16, 4, 0, 0]   20
[13, 6, 3]   22
[11, 8, 2]   21
1
Anmol Singh Jaggi 13 Авг 2020 в 12:07