Алгоритм разбиения числа

дано положительное целое число X, как можно разбить его на N частей, каждая между A и B здесь A <= B также положительные целые числа? То есть писать

X = X_1 + X_2 + ... + X_N

здесь A <= X_i <= B и приказом X_is, не имеет значения?

3 ответов


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

результаты этого метода обычно представляют собой список максимальных и минимальных значений с 1 или 2 значениями между ними. Из-за этого там есть небольшая оптимизация (используя abs), который будет препятствовать итератору постоянно пытаться найти минимальные значения, отсчитывающие от max и vice наоборот.

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


сценарий:

# iterative approach in-case the number of partitians is particularly large
def splitter(value, partitians, min_range, max_range, part_values):
    # lower bound used to determine if the solution is within reach
    lower_bound = 0
    # upper bound used to determine if the solution is within reach
    upper_bound = 0
    # upper_range used as upper limit for the iterator
    upper_range = 0
    # lower range used as lower limit for the iterator
    lower_range = 0
    # interval will be + or -
    interval = 0

    while value > 0:
        partitians -= 1
        lower_bound = min_range*(partitians)
        upper_bound = max_range*(partitians)
        # if the value is more likely at the upper bound start from there
        if abs(lower_bound - value) < abs(upper_bound - value):
            upper_range = max_range
            lower_range = min_range-1
            interval = -1
        # if the value is more likely at the lower bound start from there
        else:
            upper_range = min_range
            lower_range = max_range+1
            interval = 1

        for i in range(upper_range, lower_range, interval):
            # make sure what we are doing won't break solution
            if lower_bound <= value-i and upper_bound >= value-i:
                part_values.append(i)
                value -= i
                break

    return part_values


def partitioner(value, partitians, min_range, max_range):
    if min_range*partitians <= value and max_range*partitians >= value:
        return splitter(value, partitians, min_range, max_range, [])
    else:
        print ("this is impossible to solve")

def main():
    print(partitioner(9800, 1000, 2, 100))

основная идея этого скрипта заключается в том, что значение должно находиться между min*parts и max*parts, для каждого шага решения, если мы всегда достигаем этой цели, мы будем в конечном итоге в min < value < max на parts == 1, так что если мы постоянно забираем из значения, и держать его в пределах этого min < value < max диапазон мы всегда найдем результат, если он доступен.

для примера этого кода он в основном всегда будет отнимать либо max или min в зависимости от того, какая граница value ближе, пока некоторые не min или max значение остается как остаток.


если вы хотите знать количество способов для этого вы можете использовать производящие функции.

по существу, вы заинтересованы в целые разделы. Целочисленный раздел X - это способ писать X как сумма натуральных чисел. Пусть p(n) количество целочисленных разделы n. Например, если n=5 затем p(n)=7 соответствующие разделы:

5
4,1
3,2
3,1,1
2,2,1
2,1,1,1
1,1,1,1,1

производящая функция для p(n) is

sum_{n >= 0} p(n) z^n = Prod_{i >= 1} ( 1 / (1 - z^i) )

что это значит для вас? путем расширять правую сторону и принимать коэффициент z^n вы можете взыскать p(n). Не беспокойтесь, что продукт бесконечен, так как вы будете принимать только конечное количество членов для вычисления p(n). На самом деле, если это все, что вы хотите, то просто усеките продукт и остановитесь на i=n.

почему это работает? запомнить это

1 / (1 - z^i) = 1 + z^i + z^{2i} + z^{3i} + ...

Итак, коэффициент z^n это количество способов написать

n = 1 * a_1 + 2*a_2 + 3 * a_3 +...

где теперь я думаю о a_i как количество раз i в раздел n.

как это обобщить? легко, как выясняется. Из приведенного выше описания, если вы хотите, чтобы части раздела были только в заданном наборе A, то вместо принимая продукт за все i >= 1, возьмите продукт только i in A. Пусть p_A(n) - число целочисленных разделов n чьи части приходят из набора A. Тогда

sum_{n >= 0} p_A(n) z^n = Prod_{i in A} ( 1 / (1 - z^i) )

опять же, принимая коэффициент z^n в этом расширении решает вашу проблему. Но мы можем пойти дальше и отслеживать количество деталей раздела. Для этого добавьте в другое место q чтобы отслеживать, сколько частей мы используем. Пусть p_A(n,k) будет количество целочисленных разделов n на k части, где части приходят из набора A. Тогда

sum_{n >= 0} sum_{k >= 0} p_A(n,k) q^k z^n = Prod_{i in A} ( 1 / (1 - q*z^i) )

с коэффициентом q^k z^n дает количество целочисленных разделов n на k части, где части приходят из набора A.

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


простая реализация, которую вы можете сделать, заключается в том, что среднее значение X_i должен быть между A и B, так что мы можем просто разделить X by N а затем сделайте некоторые небольшие корректировки, чтобы равномерно распределить остаток, чтобы получить допустимый раздел.

вот один из способов сделать это:

X_i = ceil  (X / N)  if i <= X mod N,
      floor (X / N)  otherwise.

это дает действительное решение, если A <= floor (X / N) и ceil (X / N) <= B. Иначе нет решения. См. доказательства под.


sum(X_i) == X

доказательство:

используйте алгоритм деления для записи X = q*N + r С 0 <= r < N.

  • если r == 0, потом ceil (X / N) == floor (X / N) == q таким образом, алгоритм устанавливает все X_i = q. Их сумма q*N == X.

  • если r > 0, потом floor (X / N) == q и ceil (X / N) == q+1. Алгоритм устанавливает X_i = q+1 на 1 <= i <= r (т. е. r экз.), и X_i = q оставшиеся N - r штук. Поэтому сумма (q+1)*r + (N-r)*q == q*r + r + N*q - r*q == q*N + r == X.


если floor (X / N) < A или ceil (X / N) > B, то решения нет.

доказательство:

  • если floor (X / N) < A, потом floor (X / N) * N < A * N, и с floor(X / N) * N <= X, это означает, что X < A*N, поэтому, даже используя только самые маленькие кусочки, сумма будет больше, чем X.

  • аналогично, если ceil (X / N) > B, потом ceil (X / N) * N > B * N, и с ceil(X / N) * N >= X, это означает, что X > B*N, так что даже через только самые большие части возможны, сумма будет меньше, чем X.