说我有一组数字'0','1','2',......,'9'。我想找到所有数字,其中只包含我集合中每个数字中的一个。
问题是:在我启动程序之前,我不知道我的程序集中包含多少个数字和数字。 (例如,该集可以包括数字'1','3'和'14'。)
我搜索了互联网,偶然发现了“动态编程”这个术语,这个术语显然是用来解决像我这样的问题,但我不明白这些例子。
有人可以给我一个如何解决这个问题的提示(可能是动态编程)吗?
编辑:当该集合包括诸如“14”的数字时,该集合的不同数量当然必须通过某种方式分开,例如,当集合包括数字'1','3'和'14'时,组合可以是1-3-14或3-14-1(=由' - '字符分隔的个别数字)。< / p>
编辑2:一个似乎有些类似的问题被描述为here:其中一个解决方案使用动态编程。
答案 0 :(得分:7)
对我来说,看起来你正在寻找一组给定元素的所有排列。
如果您使用C ++,则有一个标准函数next_permutation()
可以完全满足您的要求。您从排序的数组开始,然后重复调用next_permutation
。
示例如下:http://www.cplusplus.com/reference/algorithm/next_permutation/
答案 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;
}
它只是在一个循环中完成所有操作,以避免递归和函数调用开销,仍然使用堆栈数组“伪造”所需的嵌套for循环。
它表现相当不错,在我4岁的Athlon64 3800+上需要2'4“的用户时间(=&gt;实际计算时间)来生成36 ^ 6 = 2176782336组合,因此它每秒计算大约1750万个组合。 / p>
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$
“真实”时间相当高,因为我在PC上也做了其他事情。
答案 2 :(得分:1)
您正在寻找一组给定值的所有排列。
关于Java中“做”排列的一篇文章在这里:http://www.bearcave.com/random_hacks/permute.html
你想跳过前几节,直到你到达标题排列算法(当然)。
答案 3 :(得分:1)
这是我可以找到有用的排列的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("_"));
}
}
答案 4 :(得分:1)
有多少个号码,哪些号码不是两个问题。如果你知道哪些数字,你知道多少。
这些数字的名字不是很有趣。 1-3-14或0-1-2或Foo-Bar-Baz - 它始终是同一个问题,与0-1-2和数组的排列相同的问题,在哪里查找结果。
idx nums words
0 1 foo
1 3 bar
2 14 baz
最方便的解决方案是,编写一个通用的Iterable。然后,您可以使用简化的for循环来访问每个排列。
import java.util.*;
class PermutationIterator <T> implements Iterator <List <T>> {
private int current = 0;
private final long last;
private final List <T> lilio;
public PermutationIterator (final List <T> llo) {
lilio = llo;
long product = 1;
for (long p = 1; p <= llo.size (); ++p)
product *= p;
last = product;
}
public boolean hasNext () {
return current != last;
}
public List <T> next () {
++current;
return get (current - 1, lilio);
}
public void remove () {
++current;
}
private List <T> get (final int code, final List <T> li) {
int len = li.size ();
int pos = code % len;
if (len > 1) {
List <T> rest = get (code / len, li.subList (1, li.size ()));
List <T> a = rest.subList (0, pos);
List <T> res = new ArrayList <T> ();
res.addAll (a);
res.add (li.get (0));
res.addAll (rest.subList (pos, rest.size ()));
return res;
}
return li;
}
}
class PermutationIterable <T> implements Iterable <List <T>> {
private List <T> lilio;
public PermutationIterable (List <T> llo) {
lilio = llo;
}
public Iterator <List <T>> iterator () {
return new PermutationIterator <T> (lilio);
}
}
class PermutationIteratorTest {
public static void main (String[] args) {
List <Integer> la = Arrays.asList (new Integer [] {1, 3, 14});
PermutationIterable <Integer> pi = new PermutationIterable <Integer> (la);
for (List <Integer> lc: pi)
show (lc);
}
public static void show (List <Integer> lo) {
System.out.print ("(");
for (Object o: lo)
System.out.print (o + ", ");
System.out.println (")");
}
}
答案 5 :(得分:0)
与动态编程无关;除非你想在裤子外面穿内裤并在胸前涂上一个符号。
这样做的简单方法是维护一个0-9的整数数组,然后逐个遍历数字并递增数组[num]。一旦处理完所有数字,结果就是查看数组中的任何元素是非零还是一个。 (这表示重复的数字。)当然,取一个数字然后使用模数和除数逐位迭代是微不足道的。
答案 6 :(得分:0)
所以,假设您有数字1,2和3。
如果您期望六个数字123,132,213,231,312和321是正确的答案,那么您正在寻找的是一些代码来生成一组的所有排列,这将比对于有趣尺寸的问题几乎任何其他事情。你看O(n!)是最好的情况。
答案 7 :(得分:0)
你应该编写一个循环遍历列表的递归函数,并且每次使用更新的列表调用自身。这意味着它需要使用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)
}
}
答案 8 :(得分:0)
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]