Выведите все уникальные целочисленные разделы, которым задано целое число в качестве входных данных

Я решал упражнение по программированию и столкнулся с проблемой, для которой я не могу удовлетворительно найти решение. Проблема выглядит следующим образом:

Print all unique integer partitions given an integer as input.
Integer partition is a way of writing n as a sum of positive integers.

например: Input=4, тогда вывод должен быть Output=

  1 1 1 1
  1 1 2
  2 2
  1 3
  4

Как я должен думать о решении этой проблемы? Мне было интересно использовать рекурсию. Может ли кто-нибудь дать мне алгоритм для этого вопроса? Или намек на решение. любое объяснение таких проблем приветствуется. (Я новичок в мире программирования) Спасибо!!


person user2056245    schedule 18.07.2013    source источник


Ответы (4)


Я бы подошел к этому так:

Во-первых, обобщите проблему. Вы можете определить функцию

printPartitions(int target, int maxValue, string suffix)

со спецификацией:

Выведите все целочисленные разделы цели, за которыми следует суффикс, так что каждое значение в разделе не больше maxValue

Обратите внимание, что всегда существует по крайней мере 1 решение (при условии, что и target, и maxValue положительны), то есть все 1 с.


Вы можете использовать этот метод рекурсивно. Итак, давайте сначала подумаем о базовом случае:

printPartitions(0, maxValue, suffix)

следует просто напечатать suffix.


Если target не 0, у вас есть варианты: либо использовать maxValue, либо нет (если maxValue > target есть только один вариант: не использовать его). Если вы не используете его, вы должны уменьшить maxValue на 1.

То есть:

if (maxValue <= target)
    printPartitions(target-maxValue, maxValue, maxValue + suffix);
if (maxValue > 1)
    printPartitions(target, maxValue-1, suffix);

Объединение всего этого приводит к относительно простому методу (здесь закодировано на Java, и я немного изменил порядок операторов, чтобы получить тот же порядок, который вы описали):

void printPartitions(int target, int maxValue, String suffix) {
    if (target == 0)
        System.out.println(suffix);
    else {
        if (maxValue > 1)
            printPartitions(target, maxValue-1, suffix);
        if (maxValue <= target)
            printPartitions(target-maxValue, maxValue, maxValue + " " + suffix);
    }
}

Вы можете просто назвать это как

printPartitions(4, 4, "");

который выводит

1 1 1 1 
1 1 2 
2 2 
1 3 
4 
person Vincent van der Weele    schedule 18.07.2013

Это частично основано на подходе Хьюстера.

Во-первых, обратите внимание, что последние числа вывода 1,2,2,3,4. Если последнее число 2, 2-е последние числа 1,2. Это говорит мне о том, что было бы неплохо иметь рекурсивную функцию с циклом for, генерирующим строку сзади.

Сам код довольно прост:

  • Цикл от 1 до target, добавление переменной к суффиксу, вычитание его из target и рекурсия.
  • Также обратите внимание, что каждая сгенерированная строка сортируется (что неявно позволяет избежать дублирования вывода). Мы сортируем его, просто передавая последнее сгенерированное число и не зацикливаясь дальше этого числа.

Код:

private void printPartitions(int target, int max, String suffix)
{
    if (target == 0)
       System.out.println(suffix);
    else
    {
       for (int i = 1; i <= max && i <= target; i++)
          printPartitions(target - i, i, i + " " + suffix);
    }
}

Функция вызывающего абонента:

public void printPartitions(int target)
{
   printPartitions(target, target, "");
}
person Bernhard Barker    schedule 18.07.2013
comment
Да, таким образом вы избавляетесь от глубокой рекурсии, а код становится еще короче :) Хотя мне было проще объяснить другое :) - person Vincent van der Weele; 18.07.2013
comment
какова временная сложность вышеуказанного решения? - person coder_15; 09.04.2014

Процесс перечисления целочисленных разделов числа n является рекурсивным. Существует единственный раздел 0, пустой набор (). Существует единственный раздел 1, набор (1). Есть два раздела 2, наборы (1 1) и (2). Есть три раздела 3, наборы (1 1 1), (1 2) и (3). Есть пять разделов числа 4, множества (1 1 1 1), (1 1 2), (1 3), (2 2) и (4). Есть семь разделов числа 5, наборы (1 1 1 1 1), (1 1 1 2), (1 2 2), (1 1 3), (1 4), (2 3) и (5). И так далее. В каждом случае следующий больший набор разделов определяется путем добавления каждого целого числа x, меньшего или равного n, ко всем наборам, образованным разделением n – x, устраняя любые дубликаты.

Я даю код на нескольких языках в моем блоге. Например, вот мое решение на схеме:

(define (set-cons x xs)
  (if (member x xs) xs
    (cons x xs)))

(define (parts n)
  (if (zero? n) (list (list))
    (let ((xs (list)))
      (do ((x 1 (+ x 1))) ((= x n) (cons (list n) xs))
        (do ((yss (parts (- n x)) (cdr yss))) ((null? yss))
          (set! xs (set-cons (sort < (cons x (car yss))) xs)))))))

> (parts 0)
(())
> (parts 1)
((1))
> (parts 2)
((2) (1 1))
> (parts 3)
((3) (1 1 1) (1 2))
> (parts 4)
((4) (2 2) (1 1 2) (1 1 1 1) (1 3))
> (parts 5)
((5) (2 3) (1 1 3) (1 1 1 1 1) (1 1 1 2) (1 2 2) (1 4))
> (parts 6)
((6) (3 3) (2 2 2) (2 4) (1 1 4) (1 1 2 2) (1 1 1 1 2)
  ((1 1 1 1 1 1) (1 1 1 3) (1 2 3) (1 5))
person user448810    schedule 18.07.2013

вот алгоритм. дайте мне знать, что вы думаете. проверено на питоне3

def partition(A):
    table = [[[1]]] + [None]*(A-1)
    for i in range(1,A):
        table[i] = [[i+1]]
        for k in range(i):
            table[i].extend([[i-k]+l for l in table[k] if i-k >= l[0]])
    return table[-1]

def print_partition(A):
    for i in reversed(partition(A)): print(*i)
person rnbguy    schedule 18.07.2013
comment
Можно ли изменить это, чтобы включить только разделы с отдельными частями? - person Sidharth Samant; 04.03.2017
comment
что вы имеете в виду под отдельными частями? Примеры? - person rnbguy; 04.03.2017
comment
Разделы для 4: [4], [3,1], [2,2] и [1,1,1,1]. Но разделы с разными частями — это [4] и [3,1], потому что остальные содержат дубликаты. См. раздел о нечетных и отдельных частях -› en.m.wikipedia.org/ wiki/Partition_(number_theory) - person Sidharth Samant; 05.03.2017
comment
Да, конечно. просто измените i-k >= l[0] на i-k > l[0] :) - person rnbguy; 05.03.2017
comment
просто чтобы дать вам представление, мой код использует динамическое программирование. - person rnbguy; 05.03.2017