我不知道这方面的算法名称(尽管可能存在),但我在评论中提到的方法避免了处理重复项,并且是我想象中最有效的方法。
引用:
似乎可以通过反向思考来改进问题:每个字母必须放入四个桶之一,而桶的空间有限,因此递归地尝试将每个字母放入每个具有其所需空间的桶中。这样,您只会生成组合而不是排列。
以下是C#实现。它可以在不到30秒的时间内生成1000万个组合,其中2/3的时间仅用于构建字符串输出:
void Main()
{
var letters = Enumerable.Range(0, 26).Select(i => (char)('a' + i)).ToList();
var buckets = new[]{new Bucket(6), new Bucket(6), new Bucket(7), new Bucket(7)};
var combos = Combos(letters, 0, buckets).Take(100);
foreach (var combo in combos)
{
Console.WriteLine(combo);
}
}
public class Bucket : List<char>
{
public int MaxLoad {get; private set;}
public Bucket(int capacity) : base(capacity)
{
MaxLoad = capacity;
}
}
IEnumerable<string> Combos(IList<char> letters, int currentIndex, Bucket[] buckets)
{
if(currentIndex == letters.Count){
yield return string.Join("|", buckets.Select(b => string.Join(",", b)));
yield break;
}
var currentLetter = letters[currentIndex];
foreach (var bucket in buckets)
{
if(bucket.Count < bucket.Capacity)
{
bucket.Add(currentLetter);
foreach (var possibility in Combos(letters, currentIndex + 1, buckets))
{
yield return possibility;
}
bucket.Remove(currentLetter);
}
}
}
样例输出:
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
a,b,c,d,e,f|g,h,i,j,k,l|m,n,o,p,q,r,t|s,u,v,w,x,y,z
a,b,c,d,e,f|g,h,i,j,k,l|m,n,o,p,q,r,u|s,t,v,w,x,y,z
a,b,c,d,e,f|g,h,i,j,k,l|m,n,o,p,q,r,v|s,t,u,w,x,y,z
a,b,c,d,e,f|g,h,i,j,k,l|m,n,o,p,q,r,w|s,t,u,v,x,y,z
a,b,c,d,e,f|g,h,i,j,k,l|m,n,o,p,q,r,x|s,t,u,v,w,y,z
a,b,c,d,e,f|g,h,i,j,k,l|m,n,o,p,q,r,y|s,t,u,v,w,x,z
a,b,c,d,e,f|g,h,i,j,k,l|m,n,o,p,q,r,z|s,t,u,v,w,x,y
a,b,c,d,e,f|g,h,i,j,k,l|m,n,o,p,q,s,t|r,u,v,w,x,y,z
a,b,c,d,e,f|g,h,i,j,k,l|m,n,o,p,q,s,u|r,t,v,w,x,y,z
a,b,c,d,e,f|g,h,i,j,k,l|m,n,o,p,q,s,v|r,t,u,w,x,y,z
a,b,c,d,e,f|g,h,i,j,k,l|m,n,o,p,q,s,w|r,t,u,v,x,y,z
a,b,c,d,e,f|g,h,i,j,k,l|m,n,o,p,q,s,x|r,t,u,v,w,y,z
a,b,c,d,e,f|g,h,i,j,k,l|m,n,o,p,q,s,y|r,t,u,v,w,x,z
a,b,c,d,e,f|g,h,i,j,k,l|m,n,o,p,q,s,z|r,t,u,v,w,x,y
a,b,c,d,e,f|g,h,i,j,k,l|m,n,o,p,q,t,u|r,s,v,w,x,y,z
a,b,c,d,e,f|g,h,i,j,k,l|m,n,o,p,q,t,v|r,s,u,w,x,y,z
a,b,c,d,e,f|g,h,i,j,k,l|m,n,o,p,q,t,w|r,s,u,v,x,y,z
a,b,c,d,e,f|g,h,i,j,k,l|m,n,o,p,q,t,x|r,s,u,v,w,y,z
a,b,c,d,e,f|g,h,i,j,k,l|m,n,o,p,q,t,y|r,s,u,v,w,x,z
a,b,c,d,e,f|g,h,i,j,k,l|m,n,o,p,q,t,z|r,s,u,v,w,x,y
a,b,c,d,e,f|g,h,i,j,k,l|m,n,o,p,q,u,v|r,s,t,w,x,y,z
a,b,c,d,e,f|g,h,i,j,k,l|m,n,o,p,q,u,w|r,s,t,v,x,y,z
a,b,c,d,e,f|g,h,i,j,k,l|m,n,o,p,q,u,x|r,s,t,v,w,y,z
a,b,c,d,e,f|g,h,i,j,k,l|m,n,o,p,q,u,y|r,s,t,v,w,x,z
a,b,c,d,e,f|g,h,i,j,k,l|m,n,o,p,q,u,z|r,s,t,v,w,x,y
...
我给出的方法有一个很好的特点,就是你可以在结果生成时进行处理--你不需要等待整个列表被生成,也不需要同时在内存中拥有所有的组合。
但请注意,你最终会得到非常多的组合--可能会超过计算机可以在任何合理的时间内生成的数量,无论算法效率如何。例如,如果Vincent的估计为10^12,使用上述代码大约需要一年的时间。你也许可以将其优化到一个月左右。并行化可能会在一台真正强大的计算机上将其缩短到一周。