3
  • 我编辑了原始文本以节省潜在读者的时间和健康。也许有人会真正使用它。

我知道这是基本的东西。可能非常非常基本。
如何获得给定集合的所有可能组合。例如
字符串集=“abc”;
我希望得到:
abc aa ab ac aaa aab aac aba abb abc aca acb acc baa bab ...
并且列表还在继续(如果没有设置长度限制)。

我正在为此寻找一个非常干净的代码 - 我发现的所有代码都很脏并且无法正常工作。我可以对我编写的代码说同样的话。

我需要这样的代码,因为我正在编写在多个线程上工作的蛮力(md5)实现。模式是有父进程为线程提供它们自己的组合块,所以它们可以自己处理这些。
示例:第一个线程获得 100 个排列的包,第二个获得下一个 100 个等。
让我知道是否应该在任何地方发布最终程序。

编辑#2 再次感谢你们。
多亏了你,我已经完成了用 MPICH2 实现的 Slave/Master Brute-Force 应用程序(是的,可以在 linux 和 windows 下工作,例如网络),因为这一天快结束了,我已经浪费了很多时间(和太阳)我将继续我的下一个任务...... :)
你向我展示了 StackOverflow 社区很棒 - 谢谢!

4

8 回答 8

7

这是一些 C++ 代码,它生成给定长度的幂集的排列。

该函数getPowPerms接受一组字符(作为字符串向量)和最大长度,并返回一个置换字符串向量:

#include <iostream>
using std::cout;
#include <string>
using std::string;
#include <vector>
using std::vector;

vector<string> getPowPerms( const vector<string>& set, unsigned length ) {
  if( length == 0 ) return vector<string>();
  if( length == 1 ) return set;

  vector<string> substrs = getPowPerms(set,length-1);
  vector<string> result = substrs;
  for( unsigned i = 0; i < substrs.size(); ++i ) {
    for( unsigned j = 0; j < set.size(); ++j ) {
      result.push_back( set[j] + substrs[i] );
    }
  }

  return result;
}

int main() {
  const int MAX_SIZE = 3;
  string str = "abc";

  vector<string> set;     // use vector for ease-of-access            
  for( unsigned i = 0; i < str.size(); ++i ) set.push_back( str.substr(i,1) );

  vector<string> perms = getPowPerms( set, MAX_SIZE );
  for( unsigned i = 0; i < perms.size(); ++i ) cout << perms[i] << '\n';
}

运行时,此示例打印

a b c aa ba ca ab bb cb ... acc bcc ccc

更新:我不确定这是否有用,但这里有一个名为“生成器”的函数next,它在给定当前项目的情况下创建列表中的下一个项目。

也许您可以生成前N个项目并将它们发送到某个地方,然后生成接下来的N个项目并将它们发送到其他地方。

string next( const string& cur, const string& set ) {
  string result = cur;
  bool carry = true;
  int loc = cur.size() - 1;
  char last = *set.rbegin(), first = *set.begin();
  while( loc >= 0 && carry ) {
    if( result[loc] != last ) {             // increment              
      int found = set.find(result[loc]); 
      if( found != string::npos && found < set.size()-1 ) {
        result[loc] = set.at(found+1); 
      }
      carry = false;
    } else {                                // reset and carry        
      result[loc] = first;
    }
    --loc;
  }
  if( carry ) {                             // overflow               
    result.insert( result.begin(), first );
  }
  return result;
}

int main() {
  string set = "abc";
  string cur = "a";
  for( int i = 0; i < 20; ++i ) {
    cout << cur << '\n';        // displays a b c aa ab ac ba bb bc ...
    cur = next( cur, set );
  }
}
于 2009-06-13T12:36:25.853 回答
5

C++ 有一个函数 next_permutation(),但我认为这不是你想要的。

您应该能够使用递归函数很容易地做到这一点。例如

void combinations(string s, int len, string prefix) {
  if (len<1) {
    cout << prefix << endl;
  } else {
    for (int i=0;i<s.size();i++) {
      combinations(s, len-1, prefix + s[i])
    }
  }
}

编辑:对于线程部分,我假设您正在使用密码暴力破解?

如果是这样,我猜密码测试部分是您想要加速而不是密码生成的部分。

因此,您可以简单地创建一个生成所有组合的父进程,然后将每个第k个密码提供给线程k mod N(其中N是线程数)进行检查。

于 2009-06-13T11:47:10.730 回答
0

尽管您在 C++ 中提出质疑,但另一个版本的排列在 Python 的标准库中。

http://docs.python.org/library/itertools.html#itertools.permutations

但是你的列表包含一个每个字符的不定式序列,所以我认为应该首先定义如何排序的方法,并清楚地说明你的算法。

于 2009-06-13T11:12:28.130 回答
0

我不能给你代码但是你需要的是一个递归算法这里是一些伪代码

这个想法很简单,将集合中的每个字符串与每个其他字符串连接,然后排列字符串。将所有较小的字符串添加到您的集合中,然后对新集合再次执行相同的操作。坚持到你累了:)

可能有点令人困惑,但请考虑一下;)

set = { "a", "b", "c"}

build_combinations(set)
{
  new_set={}
  for( Element in set ){
    new_set.add(Element);
    for( other_element in set )
      new_element = concatinate(Element, other_element);
      new_set.add(new_element);
  }

  new_set = permute_all_elements(new_set);

 return build_combinations(new_set);
}

这显然会导致堆栈溢出,因为没有终止条件:) 所以将您喜欢的任何条件(可能是集合的大小?)放入 build_combinations 函数中以终止递归

于 2009-06-13T11:39:11.670 回答
0

这是一种奇怪且通常不理想的方法,但是嘿,它有效,并且不使用递归:-)

void permutations(char c[], int l) // l is the length of c
{
    int length = 1;
    while (length < 5)
    {
        for (int j = 0; j < int(pow(double(l), double(length))); j++) // for each word of a particular length
        {
            for (int i = 0; i < length; i++) // for each character in a word
            {
                cout << c[(j / int(pow(double(l), double(length - i - 1))) % l)];
            }
            cout << endl;
        }
        length++;
    }
}
于 2009-06-13T12:33:40.070 回答
0

我知道你已经得到了一个非常好的答案(实际上是多个答案),但我对这个问题有点思考,我想出了一个非常简洁的算法,我不妨分享一下。

基本上,您可以通过从符号列表开始,然后将每个符号附加到其他符号以形成两个符号词,然后将每个符号附加到每个词来做到这一点。那样可能没有多大意义,所以它看起来像这样:

以“a”、“b”和“c”作为符号开始,并将它们添加到列表中:

a
b
c

将“a”、“b”和“c”附加到列表中的每个单词。然后列表如下所示:

a
b
c
aa
ab
ac
ba
bb
bc
ca
cb
cc

然后将“a”、“b”和“c”附加到列表中的每个新单词,这样列表将如下所示:

a
b
c
aa
ab
ac
ba
bb
bc
ca
cb
cc
aaa
aab
aac
aba
abb
... and so on

您可以通过使用迭代器轻松地做到这一点,并让迭代器从头开始。

此代码打印出添加到列表中的每个单词。

void permutations(string symbols)
{
    list<string> l;
    // add each symbol to the list
    for (int i = 0; i < symbols.length(); i++)
    {
        l.push_back(symbols.substr(i, 1));
        cout << symbols.substr(i, 1) << endl;
    }
    // infinite loop that looks at each word in the list
    for (list<string>::iterator it = l.begin(); it != l.end(); it++)
    {
        // append each symbol to the current word and add it to the end of the list
        for (int i = 0; i < symbols.length(); i++)
        {
            string s(*it);
            s.push_back(symbols[i]);
            l.push_back(s);
            cout << s << endl;
        }
    }
}
于 2009-06-14T04:22:02.460 回答
0

一个 Python 示例:

import itertools
import string

characters = string.ascii_lowercase 
max_length = 3
count = 1
while count < max_length+1:
    for current_tuple in itertools.product(characters, repeat=count):
        current_string = "".join(current_tuple)
        print current_string
    count += 1

输出正是您期望得到的: abc aa ab ac aaa aab aac aba abb abc aca acb acc baa bab ...(该示例使用整个 ASCII 小写字符集,更改“characters = ['a',' b','c']" 以减小输出的大小)

于 2009-06-14T11:45:13.830 回答
-1

你想要的叫做排列。

检查这个在java中的置换实现

于 2009-06-13T11:04:20.380 回答