【问题标题】:How to get count of next combinations for given set?如何获得给定集合的下一个组合的计数?
【发布时间】:2009-06-13 10:55:58
【问题描述】:
  • 我编辑了原始文本以节省潜在读者的时间和健康。也许有人会真正使用它。

我知道这是基本的东西。可能非常非常基本。
如何获得给定集合的所有可能组合。 例如
字符串集 = "abc";
我希望得到:
a b c 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 社区很棒 - 谢谢!

【问题讨论】:

  • 如果我理解正确的话,你需要一个无限循环来生成越来越长的字符集组合。这是正确的吗?
  • 您想要所有排列的幂集,对吗? en.wikipedia.org/wiki/Power_set

标签: c++ string combinatorics


【解决方案1】:

这里有一些 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 );
  }
}

【讨论】:

  • 你拯救了我的一天 :) 谢谢老兄!
  • 您的代码有错误。将第 8 行替换为: int found = set.find(result[loc]); if(found!=string::npos && found
【解决方案2】:

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 是用于检查的线程数。

【讨论】:

  • 感谢您的代码。您能否解释一下这些参数的目的是什么?另外 - 第一次检查不是无效的吗?不应该是 if(len
  • 哦,是的,很好的发现。 s 是原始字符串(在您的示例中为 abc)。 len 是您要生成的长度(例如 len=2 将生成 aa、ab、ac...)。前缀应作为空字符串“”传入。例如组合(“abc”,2,“”)应该调用:组合(“abc”,1,“a”)组合(“abc”,1,“b”)组合(“abc”,1,“c”)
  • sventek - 这是一个用于破解 md5 的多线程应用程序,没什么特别的 - 散列函数可以是任何东西。你是对的,它正在散列消耗时间的东西。但是为该过程提供的数据应该是有效的,对吧?我将创建主进程并添加子进程或线程,但我不确定这是正确的方法。我宁愿将有限的组合传递给新流程。为什么?尝试为例如创建所有组合6种组合...
  • 不确定您的意思是“为该过程提供的数据应该是有效的”。您不必一次创建所有组合。相反,您的父线程仅在工人使用密码时生成密码。
【解决方案3】:

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

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

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

【讨论】:

  • 正常顺序 - 就像给定的集合一样。我的意思是首先“填充”1个字符的所有组合,然后是两个,然后是三个......等等。它是无限的,但这不是问题。
  • * 让我换一种说法。长度 = 1 的字符串的所有组合。然后长度 = 2... 等等。
【解决方案4】:

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

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

可能有点混乱,但请考虑一下;)

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 函数中放入你喜欢的任何条件(可能是集合的大小?)来终止递归

【讨论】:

    【解决方案5】:

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

    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++;
        }
    }
    

    【讨论】:

      【解决方案6】:

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

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

      以“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;
              }
          }
      }
      

      【讨论】:

      • 谢谢!我标记的代码也是如此,但会返回准确的新元素,这对我来说很重要。我应该能够通过当前设置并获得下 n 个组合。无论如何,感谢您的努力。
      • 没关系。修改它来做这些事情不会太难,但我写这个不是为了你的解决方案——我写它是因为我认为这是一种很好的做法,我发布它是因为我认为你可能会有兴趣查看解决原始问题的其他方法:-)
      【解决方案7】:

      一个 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
      

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

      【讨论】:

        【解决方案8】:

        你想要的叫做排列。

        在java中检查Permutation implementation

        【讨论】:

        • 恕我直言(谢谢回答),排列不只是:给定长度的集合字符的所有组合吗?这个例子就是这样做的......
        猜你喜欢
        • 1970-01-01
        • 2021-02-11
        • 2020-05-22
        • 1970-01-01
        • 1970-01-01
        • 2021-03-21
        • 1970-01-01
        • 2017-06-30
        • 1970-01-01
        相关资源
        最近更新 更多