скажите, что у меня есть ряд номера '0', '1', '2'..., '9'. Я хочу найти все числа, которые содержат точно одно из каждого из чисел в моем наборе.
Проблема: Прежде чем я запущу свою программу, я не знаю, сколько будут включать числа и который нумерует мой набор. (Например, набор мог включать номера '1', '3' и '14'.)
Я искал Интернет и наткнулся на термин 'динамическое программирование', которое, по-видимому, является чем-то для использования для решения проблем как мои, но я не понял примеров.
Кто-то может дать мне подсказку о том, как решить эту проблему (возможно с динамическим программированием)?
Править: Когда набор включает числа как '14', различные числа набора должны были бы, конечно, быть разделены некоторыми средствами, например, когда набор включает номера '1', '3', и '14', комбинации могли быть чем-то как 1-3-14 или 3-14-1 (= отдельные числа, разделенные '-'-символом).
РЕДАКТИРОВАНИЕ 2: Одна проблема, которая, кажется, несколько подобна, описана здесь: одно из решений использует динамическое программирование.
Чтобы рассмотреть все комбинации, не зная заранее, сколько цифр должно быть на выходе, я однажды написал этот код:
#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$
Реальное время достаточно высоко, так как я тем временем занимался и кое-чем другим на ПК
.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]
Мне кажется, что вы ищете все перестановки заданного набора элементов.
Если Вы используете C++, то существует стандартная функция next_permutation()
, которая делает именно то, что Вы ищете. Вы начинаете с отсортированного массива и затем повторно вызываете next_permutation
.
Пример здесь: http://www.cplusplus.com/reference/algorithm/next_permutation/
Вы ищете все перестановки заданного набора значений.
Одна статья о "выполнении" перестановок в Java находится здесь: http://www.bearcave.com/random_hacks/permute.html
Вы хотите пропустить первые пару разделов, пока не перейдете к заголовку Алгоритмы перестановки (конечно).
Вот моя реализация перестановки на 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("_"));
}
}
Ничего общего с динамическим программированием, если только вы не хотите носить трусы за пределами брюк и рисовать символ на груди.
Простой способ сделать это - поддерживать массив из 0-9 целых чисел, затем прогонять числа один за другим и инкрементировать массив [число]. В результате, после обработки всех цифр, вы увидите, является ли какой-либо элемент массива ненулевым или единичным. (Это указывает на повторяющуюся цифру.) Конечно, тривиально взять число, а затем пройти через цифру за цифрой, используя модуль и делитель.
.Итак, допустим, у вас есть числа 1, 2 и 3.
Если вы ожидаете, что шесть чисел 123, 132, 213, 231, 312 и 321 будут правильным ответом, то вам нужен какой-нибудь код для генерации всех перестановок множества, который будет быстрее, чем почти все остальное для задач интересного размера. Впрочем, вы смотрите на O(n!) как на лучший вариант.
.Нужно написать рекурсивную функцию, которая циклически перебирает список и каждый раз вызывает себя с обновленным списком. Это означает, что для перехода к следующей итерации необходимо создать копию списка с элементами 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)
}
}