Найдите все комбинации данного набора чисел

скажите, что у меня есть ряд номера '0', '1', '2'..., '9'. Я хочу найти все числа, которые содержат точно одно из каждого из чисел в моем наборе.

Проблема: Прежде чем я запущу свою программу, я не знаю, сколько будут включать числа и который нумерует мой набор. (Например, набор мог включать номера '1', '3' и '14'.)

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

Кто-то может дать мне подсказку о том, как решить эту проблему (возможно с динамическим программированием)?

Править: Когда набор включает числа как '14', различные числа набора должны были бы, конечно, быть разделены некоторыми средствами, например, когда набор включает номера '1', '3', и '14', комбинации могли быть чем-то как 1-3-14 или 3-14-1 (= отдельные числа, разделенные '-'-символом).

РЕДАКТИРОВАНИЕ 2: Одна проблема, которая, кажется, несколько подобна, описана здесь: одно из решений использует динамическое программирование.

6
задан Community 23 May 2017 в 11:58
поделиться

8 ответов

Чтобы рассмотреть все комбинации, не зная заранее, сколько цифр должно быть на выходе, я однажды написал этот код:

#include <stdio.h>
#include <stdlib.h>

#define ARRSIZE(arr)    (sizeof(arr)/sizeof(*(arr)))

int main()
{
    const char values[]= {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'};
    char * buffer=NULL;
    int * stack=NULL;
    int combinationLength=-1;
    int valuesNumber=-1;
    int curPos=0;
    fprintf(stderr, "%s", "Length of a combination: ");
    if(scanf("%d", &combinationLength)!=1 || combinationLength<1)
    {
        fputs("Invalid value.\n",stderr);
        return 1;
    }
    fprintf(stderr, "%s (%lu max): ", "Possible digit values",(long unsigned)ARRSIZE(values));
    if(scanf("%d", &valuesNumber)!=1 || valuesNumber<1 || (size_t)valuesNumber>ARRSIZE(values))
    {
        fputs("Invalid value.\n", stderr);
        return 1;
    }
    buffer=(char *)malloc(combinationLength);
    stack=(int *)malloc(combinationLength*sizeof(*stack));
    if(buffer==NULL || stack==NULL)
    {
        fputs("Cannot allocate memory.\n", stderr);
        free(buffer);
        free(stack);
        return 2;
    }
    /* Combinations generator */
    for(;;)
    {
        /* If we reached the last digit symbol... */
        if(stack[curPos]==valuesNumber)
        {
            /* ...get back to the previous position, if we finished exit */
            if(--curPos==-1)
                break;
            /* Repeat this check */
            continue;
        }
        buffer[curPos]=values[stack[curPos]];
        /* If we are in the most inner fake-cycle write the combination */
        if(curPos==combinationLength-1)
            puts(buffer);
        stack[curPos]++;
        /* If we aren't on the last position, start working on the next one */
        if(curPos<combinationLength-1)
        {
            curPos++;
            stack[curPos]=0;
        }
    }
    /* Cleanup */
    free(buffer);
    free(stack);
    return 0;    
}

Он делает все за один цикл, чтобы избежать рекурсии и накладных вызовов функций, но все равно, если "подделать" нужную вложенность для циклов с использованием массива стека.
. Он достаточно хорошо работает, на моем 4-х летнем Athlon64 3800+ требуется 2' 4" пользовательского времени (=> реальное время вычислений) для генерации 36^6=2176782336 комбинаций, так что он вычисляет около 17.5 миллионов комбинаций в секунду.

matteo@teoubuntu:~/cpp$ gcc -Wall -Wextra -ansi -pedantic -O3 combinations.c -o combinations.x
matteo@teoubuntu:~/cpp$ time ./combinations.x > /media/Dati/combinations.txt
Length of a combination: 6
Possible digit values (36 max): 36

real    13m6.685s
user    2m3.900s
sys 0m53.930s
matteo@teoubuntu:~/cpp$ head /media/Dati/combinations.txt
000000
000001
000002
000003
000004
000005
000006
000007
000008
000009
matteo@teoubuntu:~/cpp$ tail /media/Dati/combinations.txt
zzzzzq
zzzzzr
zzzzzs
zzzzzt
zzzzzu
zzzzzv
zzzzzw
zzzzzx
zzzzzy
zzzzzz
matteo@teoubuntu:~/cpp$ ls -lh /media/Dati/combinations.txt 
-rwxrwxrwx 1 root root 15G 2010-01-02 14:16 /media/Dati/combinations.txt
matteo@teoubuntu:~/cpp$ 

Реальное время достаточно высоко, так как я тем временем занимался и кое-чем другим на ПК

.
2
ответ дан 9 December 2019 в 20:44
поделиться
import Data.List (inits, tails)

place :: a -> [a] -> [[a]]
place element list = zipWith (\front back -> front ++ element:back)
                             (inits list)
                             (tails list)

perm :: [a] -> [[a]]
perm = foldr (\element rest -> concat (map (place element) rest)) [[]]

test = perm [1, 3, 14]
0
ответ дан 9 December 2019 в 20:44
поделиться

Мне кажется, что вы ищете все перестановки заданного набора элементов.

Если Вы используете C++, то существует стандартная функция next_permutation(), которая делает именно то, что Вы ищете. Вы начинаете с отсортированного массива и затем повторно вызываете next_permutation.

Пример здесь: http://www.cplusplus.com/reference/algorithm/next_permutation/

7
ответ дан 9 December 2019 в 20:44
поделиться

Вы ищете все перестановки заданного набора значений.

Одна статья о "выполнении" перестановок в Java находится здесь: http://www.bearcave.com/random_hacks/permute.html

Вы хотите пропустить первые пару разделов, пока не перейдете к заголовку Алгоритмы перестановки (конечно).

1
ответ дан 9 December 2019 в 20:44
поделиться

Вот моя реализация перестановки на C# 3.0, которую вы можете найти полезную

public static class PermutationExpressions
    {
        public static IEnumerable<IEnumerable<T>> Permutations<T>(this IEnumerable<T> list)
        {
            return list.Permutations((uint)list.Count());
        }

        public static IEnumerable<IEnumerable<T>> Permutations<T>(this IList<T> list)
        {
            return list.Permutations((uint)list.Count);
        }

        private static IEnumerable<IEnumerable<T>> Permutations<T>(this IEnumerable<T> list, uint n)
        {
            if (n < 2) yield return list;
            else
            {
                var ie = list.GetEnumerator();
                for (var i = 0; i < n; i++)
                {
                    ie.MoveNext();
                    var item = ie.Current;

                    var i1 = i;
                    var sub_list = list.Where((excluded, j) => j != i1).ToList();

                    var sub_permutations = sub_list.Permutations(n - 1);

                    foreach (var sub_permutation in sub_permutations)
                    {
                        yield return
                            Enumerable.Repeat(item, 1)
                                .Concat(sub_permutation);
                    }
                }
            }
        }
        }

[TestFixture]
    public class TestPermutations
    {
        [Test]
        public void Permutation_Returns_Permutations()
        {
            var permutations = PermutationExpressions.Permutations(new[] { "a", "b", "c" }.AsEnumerable());
            foreach (var permutation in permutations)
            {
                Console.WriteLine(string.Join("", permutation.ToArray()));
            }
            Assert.AreEqual("abc_acb_bac_bca_cab_cba", permutations.Select(perm => perm.joinToString("")).joinToString("_"));
        }
    }
1
ответ дан 9 December 2019 в 20:44
поделиться

Ничего общего с динамическим программированием, если только вы не хотите носить трусы за пределами брюк и рисовать символ на груди.

Простой способ сделать это - поддерживать массив из 0-9 целых чисел, затем прогонять числа один за другим и инкрементировать массив [число]. В результате, после обработки всех цифр, вы увидите, является ли какой-либо элемент массива ненулевым или единичным. (Это указывает на повторяющуюся цифру.) Конечно, тривиально взять число, а затем пройти через цифру за цифрой, используя модуль и делитель.

.
0
ответ дан 9 December 2019 в 20:44
поделиться

Итак, допустим, у вас есть числа 1, 2 и 3.

Если вы ожидаете, что шесть чисел 123, 132, 213, 231, 312 и 321 будут правильным ответом, то вам нужен какой-нибудь код для генерации всех перестановок множества, который будет быстрее, чем почти все остальное для задач интересного размера. Впрочем, вы смотрите на O(n!) как на лучший вариант.

.
0
ответ дан 9 December 2019 в 20:44
поделиться

Нужно написать рекурсивную функцию, которая циклически перебирает список и каждый раз вызывает себя с обновленным списком. Это означает, что для перехода к следующей итерации необходимо создать копию списка с элементами N-1. Для получения результата необходимо добавлять текущий выбранный номер в каждой итерации.

string Permutations(List numbers, string prefix)
{
   foreach (current_number in numbers)
   {
      new_prefix = prefix+"-"+number;
      new_list=make_copy_except(numbers,  current_number)
      if (new_list.Length==0)
           print new_prefix
      else
           Permutations(new_list, new_prefix)
   }
}
0
ответ дан 9 December 2019 в 20:44
поделиться
Другие вопросы по тегам:

Похожие вопросы: