Генерация n-значных чисел, упорядоченных по сумме отдельных цифр (без рекурсии) - PullRequest
2 голосов
/ 11 ноября 2009

Я хочу сгенерировать все возможные значения n-значного числа в следующем порядке, где последовательность определяется суммой отдельных цифр.

Например, с n = 3:

111     sum = 3
112     sum = 4
121
211
122     sum = 5
212
221
113
131
311
114     sum = 6
141
411
:::
999     sum = 27

Порядок в группе сумм не важен.

Любая помощь, идеи будут оценены

Ответы [ 4 ]

6 голосов
/ 11 ноября 2009

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

Но если язык поддерживает , то рекурсивные решения гораздо элегантнее.

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

Но вы должны понимать, что глубина стека для обработки n чисел только увеличивается относительно log 10 n. Другими словами, вы получаете только дополнительный кадр стека на цифру (всего 10 кадров стека для обработки всего диапазона 32-разрядных целых чисел).

В сторону: к тому времени, когда вы доберетесь до этой точки, ваш алгоритм будет работать так долго, кадры стека станут наименьшей из ваших проблем: -)

Вот рекурсивное решение Python:

def recur (numdigits,sum,pref="",prefsum=0):
    if numdigits == 0:
        if prefsum == sum:
            print "%s, sum=%d"%(pref,prefsum)
    else:
        for i in range (1,10):
            recur (numdigits-1,sum,"%s%d"%(pref,i),prefsum+i)

def do (n):
    for i in range (1,n*9+1):
        recur (n,i)

do (2)
do (3)

какие выходы (для 2 и 3):

11, sum=2          111, sum=3
12, sum=3          112, sum=4
21, sum=3          121, sum=4
13, sum=4          211, sum=4
22, sum=4          113, sum=5
31, sum=4          122, sum=5
14, sum=5          131, sum=5
23, sum=5          212, sum=5
32, sum=5          221, sum=5
41, sum=5          311, sum=5
15, sum=6          114, sum=6
 :    :             :     :
89, sum=17         989, sum=26
98, sum=17         998, sum=26
99, sum=18         999, sum=27

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

Запустите следующую программу и используйте sort и awk в UNIX, чтобы получить желаемый заказ. Например:

go | sort | awk '{print $2}'

Обратите внимание, что для сортировки используются внешние инструменты, но вы также можете легко сортировать код C (если позволяет память).

#include <stdio.h>

int main (void) {
    int i, sum, carry, size;
    int *pDigit;

    // Choose your desired size.

    size = 2;

    // Allocate and initialise digits.

    if ((pDigit = malloc (size * sizeof (int))) == NULL) {
        fprintf (stderr, "No memory\n");
        return 1;
    )

    for (i = 0; i < size; i++)
        pDigit[i] = 1;

    // Loop until overflow.

    carry = 0;
    while (carry != 1) {
        // Work out sum, then output it with number.
        // Line is sssssssssssssssssss ddddd
        //   where sss...sss is the fixed-width sum, zero padded on left (for sort)
        //   and ddd...ddd is the actual number.

        sum = 0;
        for (i = 0; i < size; i++)
            sum += pDigit[i];

        printf ("%020d ", sum);
        for (i = 0; i < size; i++)
            printf ("%d", pDigit[i]);
        printf ("\n");

        // Advance to next number.

        carry = 1;
        for (i = 0; i < size; i++) {
            pDigit[size-i-1] = pDigit[size-i-1] + carry;
            if (pDigit[size-i-1] == 10)
                pDigit[size-i-1] = 1;
            else
                carry = 0;
        }
    }

    return 0;
}
4 голосов
/ 11 ноября 2009

Можете ли вы использовать std :: next_permutation ?

Функция next_permutation () пытается преобразовать заданный диапазон элементов [начало, конец) в следующий лексикографически большая перестановка элементов. Если это удастся, это возвращает true, иначе возвращает ложь.

Если функция строгого слабого порядка предоставляется объект cmp, он используется в вместо оператора <при сравнении элементы. </p>

См. Это: предыдущий ответ SO

0 голосов
/ 11 ноября 2009

Вы можете попытаться свести свою проблему к двум сегментам:

Два разбиения ведра просты: начните со всех минус один в ведре A и один в ведре B, затем поместите один из A в B, пока A не будет содержать только один.

В таком случае три разбиения по группам являются справедливыми: начните со всех минус двух в сегменте A и по одному в B и C. Уменьшите A на единицу и соберите все два разделения по 3 в B и C, повторяйте, пока A не содержит только один.

0 голосов
/ 11 ноября 2009

Если не имеет значения, какой шаблон вы используете, пока существует шаблон (из вашего поста не совсем ясно, имеется ли в виду конкретный шаблон), тогда при n = 3 начните с 111 и увеличивайте пока не достигнешь 999.

Кстати, термин для того, что вы просите, не совсем "перестановки".

...