我试图计算所有可能的3个字母排列,使用26个字母(仅相当于26 * 25 * 24 = 15,600)。字母的顺序很重要,我不想重复字母。 (我希望按字典顺序生成排列,但这不是必需的)
到目前为止,我试图嵌套for
循环,但我最终迭代了每个组合。所以有重复的字母,我不想要,如果我想要超过3个字母,for
循环可能变得难以管理。
我可以翻阅这些字母,直到我收到一封未使用过的字母,但它不是字典顺序,并且 比使用next_permutation
慢得多(我不能使用这个std
方法因为我正在计算26个字母的所有子集。
有更有效的方法吗?
为了明确低效率,next_permutation
即时迭代前6位数字。但是,使用此方法获取所有三个字母排列需要几秒钟,并且next_permutation
仍然很快变得无效,我必须计算2 ^ n个子集。
以下是嵌套for
循环的内容:
char key[] = {'a','b','c','d','e','f','g','h','i','j','k',
'l','m','n','o','p','r','s','t','u','v','w','x','y','z'};
bool used[25];
ZeroMemory( used, sizeof(bool)*25 );
for( int i = 0; i < 25; i++ )
{
while( used[i] == true )
i++;
if( i >= 25 )
break;
used[i] = true;
for( int j = 0; j < 25; j++ )
{
while( used[j] == true )
j++;
if( j >= 25 )
break;
used[j] = true;
for( int k = 0; k < 25; k++ )
{
while( used[k] == true )
k++;
if( k >= 25 )
break;
used[k] = true;
cout << key[i] << key[j] << key[k] << endl;
used[k] = false;
}
used[j] = false;
}
used[i] = false;
}
答案 0 :(得分:5)
创建一个表示组合开头的根,因此它没有值。
计算所有可能的孩子(26个字母,26个孩子......)
为每个根孩子计算可能的孩子(所以:剩余的字母)
使用递归的有限深度搜索来查找您的组合。
答案 1 :(得分:5)
如果我只想要一个“简单”的解决方案,我会尝试这个解决方案。我不确定这是多少资源密集,所以我建议你开始尝试一小组字母。
a = {a...z}
b = {a...z}
c = {a...z}
for each(a)
{
for each(b)
{
for each(c)
{
echo a + b + c;
}
}
}
答案 2 :(得分:3)
对于特定的和小的,n,手动循环就像你一样是最简单的方法。但是,您的代码可以高度简化:
for(char a='a'; a<='z'; ++a) {
for(char b='a'; b<='z'; ++b) {
if (b==a) continue;
for(char c='a'; c<='z'; ++c) {
if (c==a) continue;
if (c==b) continue;
std::cout << a << b << c << '\n';
}
}
}
对于变量N,显然我们需要一个不同的策略。事实证明,它需要一个令人难以置信的不同的策略。这是基于DaMachk的答案,即使用递归生成后续字母
template<class func_type>
void generate(std::string& word, int length, const func_type& func) {
for(char i='a'; i<='z'; ++i) {
bool used = false;
for(char c : word) {
if (c==i) {
used = true;
break;
}
}
if (used) continue;
word.push_back(i);
if (length==1) func(word);
else generate(word, length-1, func);
word.pop_back();
}
}
template<class func_type>
void generate(int length, const func_type& func) {
std::string word;
generate(word, length, func);
}
我还制作了一个展开版本,结果非常复杂,但速度要快得多。我有两个辅助函数:我有一个函数来“查找下一个字母”(称为next_unused
),它会将索引处的字母增加到下一个未使用的字母,如果不能,则返回false
。第三个函数reset_range
“将”从给定索引到字符串末尾的一系列字母重置为它可以使用的第一个未使用的字母。首先,我们使用reset_range
来查找第一个字符串。为了找到后续字符串,我们在最后一个字母上调用next_unused
,如果失败,则倒数第二个字母,如果第三个字母到最后一个字母失败,等等。当我们找到一封信时,我们可以正确地增加,然后,我们将其右侧的所有字母“重置”为最小的未使用值。如果我们一直到第一个字母并且不能增加,那么我们已经到了最后,我们就停止了。代码令人恐惧,但这是我能想到的最好的。
bool next_unused(char& dest, char begin, bool* used) {
used[dest] = false;
dest = 0;
if (begin > 'Z') return false;
while(used[begin]) {
if (++begin > 'Z')
return false;
}
dest = begin;
used[begin] = true;
return true;
}
void reset_range(std::string& word, int begin, bool* used) {
int count = word.size()-begin;
for(int i=0; i<count; ++i)
assert(next_unused(word[i+begin], 'A'+i, used));
}
template<class func_type>
void doit(int n, func_type func) {
bool used['Z'+1] = {};
std::string word(n, '\0');
reset_range(word, 0, used);
for(;;) {
func(word);
//find next word
int index = word.size()-1;
while(next_unused(word[index], word[index]+1, used) == false) {
if (--index < 0)
return; //no more permutations
}
reset_range(word, index+1, used);
}
}
Here it is at work。
And here it is running in a quarter of the time as the simple one
答案 3 :(得分:0)
我在powershell中做了类似的事情。生成9个符号的所有可能组合。经过一些试验和错误,这就是我想出来的。
$S1=New-Object System.Collections.ArrayList
$S1.Add("a")
$S1.Add("b")
$S1.Add("c")
$S1.Add("d")
$S1.Add("e")
$S1.Add("f")
$S1.Add("g")
$S1.Add("h")
$S1.Add("i")
$S1 | % {$a = $_
$S2 = $S1.Clone()
$S2.Remove($_)
$S2 | % {$b = $_
$S3 = $S2.Clone()
$S3.Remove($_)
$S3 | % {$c = $_
$S4 = $S2.Clone()
$S4.Remove($_)
$S4 | % {$d = $_
$S5 = $S4.Clone()
$S5.Remove($_)
$S5 | % {$e = $_
$S6 = $S5.Clone()
$S6.Remove($_)
$S6 | % {$f = $_
$S7 = $S6.Clone()
$S7.Remove($_)
$S7 | % {$g = $_
$S8 = $S7.Clone()
$S8.Remove($_)
$S8 | % {$h = $_
$S9 = $S8.Clone()
$S9.Remove($_)
$S9 | % {$i = $_
($a+$b+$c+$d+$e+$f+$g+$h+$i)
}
}
}
}
}
}
}
}
}