生成任意长度的任意字母的所有组合

发布于 2024-08-23 10:09:56 字数 199 浏览 7 评论 0原文

假设我有一个任意大小的数组,其中包含单个字符。我想计算这些字符的所有可能组合,直到任意长度。

假设我的数组是 [1, 2, 3]。用户指定的长度为2。则可能的组合为[11, 22, 33, 12, 13, 23, 21, 31, 32]。

我很难找到一个合适的算法,该算法允许任意长度,而不仅仅是排列数组。哦,虽然速度并不是绝对关键,但它也应该相当快。

Say I have an array of arbitrary size holding single characters. I want to compute all possible combinations of those characters up to an arbitrary length.

So lets say my array is [1, 2, 3]. The user-specified length is 2. Then the possible combinations are [11, 22, 33, 12, 13, 23, 21, 31, 32].

I'm having real trouble finding a suitable algorithm that allows arbitrary lengths and not just permutates the array. Oh and while speed is not absolutely critical, it should be reasonably fast too.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

花伊自在美 2024-08-30 10:09:56

只需进行加进位即可。

假设你的数组包含 4 个符号,并且你想要长度为 3 的符号。

从 000 开始(即单词上的每个符号 = 字母表[0])

然后加起来:

000
001
002
003
010
011
...

该算法(给定这些索引)只是增加最低的数字。如果它达到了字母表中的符号数量,请增加先前的数字(遵循相同的规则)并将当前数字设置为 0。

C++ 代码:

int N_LETTERS = 4;
char alphabet[] = {'a', 'b', 'c', 'd'};

std::vector<std::string> get_all_words(int length)
{
  std::vector<int> index(length, 0);
  std::vector<std::string> words;

  while(true)
  {
    std::string word(length);
    for (int i = 0; i < length; ++i)
      word[i] = alphabet[index[i]];
    words.push_back(word);

    for (int i = length-1; ; --i)
    { 
      if (i < 0) return words;
      index[i]++;
      if (index[i] == N_LETTERS)
        index[i] = 0;
      else
        break;
    }
  }
}

代码未经测试,但应该可以解决问题。

Just do an add with carry.

Say your array contained 4 symbols and you want ones of length 3.

Start with 000 (i.e. each symbol on your word = alphabet[0])

Then add up:

000
001
002
003
010
011
...

The algorithm (given these indices) is just to increase the lowest number. If it reaches the number of symbols in your alphabet, increase the previous number (following the same rule) and set the current to 0.

C++ code:

int N_LETTERS = 4;
char alphabet[] = {'a', 'b', 'c', 'd'};

std::vector<std::string> get_all_words(int length)
{
  std::vector<int> index(length, 0);
  std::vector<std::string> words;

  while(true)
  {
    std::string word(length);
    for (int i = 0; i < length; ++i)
      word[i] = alphabet[index[i]];
    words.push_back(word);

    for (int i = length-1; ; --i)
    { 
      if (i < 0) return words;
      index[i]++;
      if (index[i] == N_LETTERS)
        index[i] = 0;
      else
        break;
    }
  }
}

Code is untested, but should do the trick.

方觉久 2024-08-30 10:09:56

Knuth 在计算机编程的艺术卷中深入介绍了组合和排列1. 这是我几年前编写的他的算法之一的实现(不要讨厌它的风格,它古老的代码):

#include <algorithm>
#include <vector>
#include <functional>
#include <iostream>
using namespace std;

template<class BidirectionalIterator, class Function, class Size>
Function _permute(BidirectionalIterator first, BidirectionalIterator last, Size k, Function f, Size n, Size level)
{
    // This algorithm is adapted from Donald Knuth, 
    //      "The Art of Computer Programming, vol. 1, p. 45, Method 1"
    // Thanks, Donald.
    for( Size x = 0; x < (n-level); ++x )   // rotate every possible value in to this level's slot
    {
        if( (level+1) < k ) 
            // if not at max level, recurse down to twirl higher levels first
            f = _permute(first,last,k,f,n,level+1);
        else
        {
            // we are at highest level, this is a unique permutation
            BidirectionalIterator permEnd = first;
            advance(permEnd, k);
            f(first,permEnd);
        }
        // rotate next element in to this level's position & continue
        BidirectionalIterator rotbegin(first);
        advance(rotbegin,level);
        BidirectionalIterator rotmid(rotbegin);
        rotmid++;
        rotate(rotbegin,rotmid,last);
    }
    return f;
}

template<class BidirectionalIterator, class Function, class Size>
Function for_each_permutation(BidirectionalIterator first, BidirectionalIterator last, Size k, Function fn)
{
    return _permute<BidirectionalIterator,Function,Size>(first, last, k, fn, distance(first,last), 0);
}   





template<class Elem>
struct DumpPermutation : public std::binary_function<bool, Elem* , Elem*>
{
    bool operator()(Elem* begin, Elem* end) const
    {
        cout << "[";
        copy(begin, end, ostream_iterator<Elem>(cout, " "));
        cout << "]" << endl;
        return true;
    }
};

int main()
{

    int ary[] = {1, 2, 3};
    const size_t arySize = sizeof(ary)/sizeof(ary[0]);

    for_each_permutation(&ary[0], &ary[arySize], 2, DumpPermutation<int>());

    return 0;
}

这个程序的输出是:

[1 2 ]
[1 3 ]
[2 3 ]
[2 1 ]
[3 1 ]
[3 2 ]

如果你希望你的组合包含重复的元素,如 [11] [ 22] 和 [33],您可以使用上面的算法生成组合列表,然后通过执行以下操作将新元素附加到生成的列表:

for( size_t i = 0; i < arySize; ++i )
{
    cout << "[";
    for( int j = 0; j < k; ++j )
        cout << ary[i] << " ";
    cout << "]" << endl;
}

...程序输出现在变为:

[1 2 ]
[1 3 ]
[2 3 ]
[2 1 ]
[3 1 ]
[3 2 ]
[1 1 ]
[2 2 ]
[3 3 ]

Knuth covers combinations and permutations in some depth in The Art of Computer Programming, vol 1. Here is an implementation of one of his algorithms I wrote some years ago (don't hate on the style, its ancient code):

#include <algorithm>
#include <vector>
#include <functional>
#include <iostream>
using namespace std;

template<class BidirectionalIterator, class Function, class Size>
Function _permute(BidirectionalIterator first, BidirectionalIterator last, Size k, Function f, Size n, Size level)
{
    // This algorithm is adapted from Donald Knuth, 
    //      "The Art of Computer Programming, vol. 1, p. 45, Method 1"
    // Thanks, Donald.
    for( Size x = 0; x < (n-level); ++x )   // rotate every possible value in to this level's slot
    {
        if( (level+1) < k ) 
            // if not at max level, recurse down to twirl higher levels first
            f = _permute(first,last,k,f,n,level+1);
        else
        {
            // we are at highest level, this is a unique permutation
            BidirectionalIterator permEnd = first;
            advance(permEnd, k);
            f(first,permEnd);
        }
        // rotate next element in to this level's position & continue
        BidirectionalIterator rotbegin(first);
        advance(rotbegin,level);
        BidirectionalIterator rotmid(rotbegin);
        rotmid++;
        rotate(rotbegin,rotmid,last);
    }
    return f;
}

template<class BidirectionalIterator, class Function, class Size>
Function for_each_permutation(BidirectionalIterator first, BidirectionalIterator last, Size k, Function fn)
{
    return _permute<BidirectionalIterator,Function,Size>(first, last, k, fn, distance(first,last), 0);
}   





template<class Elem>
struct DumpPermutation : public std::binary_function<bool, Elem* , Elem*>
{
    bool operator()(Elem* begin, Elem* end) const
    {
        cout << "[";
        copy(begin, end, ostream_iterator<Elem>(cout, " "));
        cout << "]" << endl;
        return true;
    }
};

int main()
{

    int ary[] = {1, 2, 3};
    const size_t arySize = sizeof(ary)/sizeof(ary[0]);

    for_each_permutation(&ary[0], &ary[arySize], 2, DumpPermutation<int>());

    return 0;
}

Output of this program is:

[1 2 ]
[1 3 ]
[2 3 ]
[2 1 ]
[3 1 ]
[3 2 ]

If you want your combinations to include repeated elements like [11] [22] and [33], you can generate your list of combinations using the algorithm above, and then append to the generated list new elements, by doing something like this:

for( size_t i = 0; i < arySize; ++i )
{
    cout << "[";
    for( int j = 0; j < k; ++j )
        cout << ary[i] << " ";
    cout << "]" << endl;
}

...and the program output now becomes:

[1 2 ]
[1 3 ]
[2 3 ]
[2 1 ]
[3 1 ]
[3 2 ]
[1 1 ]
[2 2 ]
[3 3 ]
眼睛会笑 2024-08-30 10:09:56

一种方法是使用一个简单的计数器,您在内部将其解释为基数 N,其中 N 是数组中的项目数。然后,从 N 基数计数器中提取每个数字,并将其用作数组的索引。因此,如果您的数组是 [1,2] 并且用户指定的长度是 2,那么

Counter = 0, indexes are 0, 0
Counter = 1, indexes are 0, 1
Counter = 2, indexes are 1, 0
Counter = 3, indexes are 1, 1

这里的技巧就是您的基数 10 到基数 N 的转换代码,这并不是非常困难。

One way to do it would be with a simple counter that you internally interpret as base N, where N is the number of items in the array. You then extract each digit from the base N counter and use it as an index into your array. So if your array is [1,2] and the user specified length is 2, you have

Counter = 0, indexes are 0, 0
Counter = 1, indexes are 0, 1
Counter = 2, indexes are 1, 0
Counter = 3, indexes are 1, 1

The trick here will be your base-10 to base-N conversion code, which isn't terribly difficult.

ゝ偶尔ゞ 2024-08-30 10:09:56

如果您事先知道长度,那么您所需要的只是一些 for 循环。比如说,for length = 3

for ( i = 0; i < N; i++ )
   for ( j = 0; j < N; j++ )
      for ( k = 0; k < N; k++ )
         you now have ( i, j, k ), or a_i, a_j, a_k

现在概括一下,只需递归地执行,递归的每一步都使用一个 for 循环:

recurse( int[] a, int[] result, int index)
    if ( index == N ) base case, process result
    else
        for ( i = 0; i < N; i++ ) {
           result[index] = a[i]
           recurse( a, result, index + 1 )
        }

当然,如果您只是想要所有组合,您可以认为每个步骤作为基于 N 的数字,从 1k^N - 1,其中 k 是长度。

基本上你会得到,以 N 为基数(对于 k = 4):

0000 // take the first element four times
0001 // take the first element three times, then the second element
0002 
...
000(N-1) // take the first element three times, then take the N-th element
1000 // take the second element, then the first element three times
1001 
..
(N-1)(N-1)(N-1)(N-1) // take the last element four times

If you know the length before hand, all you need is some for loops. Say, for length = 3:

for ( i = 0; i < N; i++ )
   for ( j = 0; j < N; j++ )
      for ( k = 0; k < N; k++ )
         you now have ( i, j, k ), or a_i, a_j, a_k

Now to generalize it, just do it recursively, each step of the recursion with one of the for loops:

recurse( int[] a, int[] result, int index)
    if ( index == N ) base case, process result
    else
        for ( i = 0; i < N; i++ ) {
           result[index] = a[i]
           recurse( a, result, index + 1 )
        }

Of course, if you simply want all combinations, you can just think of each step as an N-based number, from 1 to k^N - 1, where k is the length.

Basically you would get, in base N (for k = 4):

0000 // take the first element four times
0001 // take the first element three times, then the second element
0002 
...
000(N-1) // take the first element three times, then take the N-th element
1000 // take the second element, then the first element three times
1001 
..
(N-1)(N-1)(N-1)(N-1) // take the last element four times
北陌 2024-08-30 10:09:56

使用彼得的算法效果很好;但是,如果您的字母集太大或字符串太长,则尝试将所有排列放入一个数组并返回该数组将不起作用。数组的大小将是字母表的大小加上字符串的长度。

我在 Perl 中创建了这个来解决这个问题:

package Combiner;
#package used to grab all possible combinations of a set of letters. Gets one every call, allowing reduced memory usage and faster processing.
use strict;
use warnings;

#initiate to use nextWord
#arguments are an array reference for the list of letters and the number of characters to be in the generated strings.
sub new {
    my ($class, $phoneList,$length) = @_;
    my $self = bless {
        phoneList => $phoneList,
        length => $length,
        N_LETTERS => scalar @$phoneList,
    }, $class;
    $self->init;
    $self;
}

sub init {
    my ($self) = shift;
    $self->{lindex} = [(0) x $self->{length}];
    $self->{end} = 0;
    $self;
}

#returns all possible combinations of N phonemes, one at a time. 
sub nextWord {
    my $self = shift;
    return 0 if $self->{end} == 1;
    my $word = [('-') x $self->{length}];

    $word[$_] = ${$self->{phoneList}}[${$self->{lindex}}[$_]]
        for(0..$self->{length}-1);

    #treat the string like addition; loop through 000, 001, 002, 010, 020, etc.
    for(my $i = $self->{length}-1;;$i--){
         if($i < 0){
            $self->{end} = 1;
            return $word;
         }
         ${$self->{lindex}}[$i]++;
         if (${$self->{lindex}}[$i] == $self->{N_LETTERS}){
            ${$self->{lindex}}[$i] = 0;
         }
         else{
            return $word;
         }
    }
}

像这样调用它: my $c = Combiner->new(['a','b','c','d'],20) ;。然后调用nextWord来抓取下一个单词;如果 nextWord 返回 0,则表示已完成。

Using Peter's algorithm works great; however, if your letter set is too large or your string size too long, attempting to put all of the permutations in an array and returning the array won't work. The size of the array will be the size of the alphabet raised to the length of the string.

I created this in perl to take care of the problem:

package Combiner;
#package used to grab all possible combinations of a set of letters. Gets one every call, allowing reduced memory usage and faster processing.
use strict;
use warnings;

#initiate to use nextWord
#arguments are an array reference for the list of letters and the number of characters to be in the generated strings.
sub new {
    my ($class, $phoneList,$length) = @_;
    my $self = bless {
        phoneList => $phoneList,
        length => $length,
        N_LETTERS => scalar @$phoneList,
    }, $class;
    $self->init;
    $self;
}

sub init {
    my ($self) = shift;
    $self->{lindex} = [(0) x $self->{length}];
    $self->{end} = 0;
    $self;
}

#returns all possible combinations of N phonemes, one at a time. 
sub nextWord {
    my $self = shift;
    return 0 if $self->{end} == 1;
    my $word = [('-') x $self->{length}];

    $word[$_] = ${$self->{phoneList}}[${$self->{lindex}}[$_]]
        for(0..$self->{length}-1);

    #treat the string like addition; loop through 000, 001, 002, 010, 020, etc.
    for(my $i = $self->{length}-1;;$i--){
         if($i < 0){
            $self->{end} = 1;
            return $word;
         }
         ${$self->{lindex}}[$i]++;
         if (${$self->{lindex}}[$i] == $self->{N_LETTERS}){
            ${$self->{lindex}}[$i] = 0;
         }
         else{
            return $word;
         }
    }
}

Call it like this: my $c = Combiner->new(['a','b','c','d'],20);. Then call nextWord to grab the next word; if nextWord returns 0, it means it's done.

独享拥抱 2024-08-30 10:09:56

这是我在 Haskell 中的实现

g :: [a] -> [[a]] -> [[a]]
g alphabet = concat . map (\xs -> [ xs ++ [s] | s <- alphabet])

allwords :: [a] -> [[a]]
allwords alphabet = concat $ iterate (g alphabet) [[]]

将此脚本加载到 GHCi 中。假设我们想要在字母表 {'a','b','c'} 中查找长度小于或等于 2 的所有字符串。以下 GHCi 会话可以做到这一点:

*Main> take 13 $ allwords ['a','b','c']
["","a","b","c","aa","ab","ac","ba","bb","bc","ca","cb","cc"]

或者,如果您只想长度等于的字符串2:

*Main> filter (\xs -> length xs == 2) $ take 13 $ allwords ['a','b','c']
["aa","ab","ac","ba","bb","bc","ca","cb","cc"]

小心allwords ['a','b','c'],因为它是一个无限列表!

Here's my implementation in Haskell:

g :: [a] -> [[a]] -> [[a]]
g alphabet = concat . map (\xs -> [ xs ++ [s] | s <- alphabet])

allwords :: [a] -> [[a]]
allwords alphabet = concat $ iterate (g alphabet) [[]]

Load this script into GHCi. Suppose that we want to find all strings of length less than or equal to 2 over the alphabet {'a','b','c'}. The following GHCi session does that:

*Main> take 13 $ allwords ['a','b','c']
["","a","b","c","aa","ab","ac","ba","bb","bc","ca","cb","cc"]

Or, if you want just the strings of length equal to 2:

*Main> filter (\xs -> length xs == 2) $ take 13 $ allwords ['a','b','c']
["aa","ab","ac","ba","bb","bc","ca","cb","cc"]

Be careful with allwords ['a','b','c'] for it is an infinite list!

谁许谁一生繁华 2024-08-30 10:09:56

这是我写的。可能对你有帮助...

#include<stdio.h>
#include <unistd.h>
void main()
{
FILE *file;
int i=0,f,l1,l2,l3=0;
char set[]="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890!@#$%&*.!@#$%^&*()";
int size=sizeof(set)-1;
char per[]="000";
//check urs all entered details here//
printf("Setlength=%d Comination are genrating\n",size);

// writing permutation here for length of 3//
for(l1=0;l1<size;l1++)
//first for loop which control left most char printed in file//
{ 
per[0]=set[l1];
// second for loop which control all intermediate char printed in file//
for(l2=0;l2<size;l2++)
{
per[1]=set[l2];
//third for loop which control right most char printed in file//
for(l3=0;l3<size;l3++)
{
per[2]=set[l3];
//apend file (add text to a file or create a file if it does not exist.//
file = fopen("file.txt","a+");
//writes array per to file named file.txt// 
fprintf(file,"%s\n",per); 
///Writing to file is completed//
fclose(file); 
i++;
printf("Genrating Combination  %d\r",i);
fflush(stdout);``
usleep(1);
}
}
}
printf("\n%d combination has been genrate out of entered data of length %d \n",i,size);
puts("No combination is left :) ");
puts("Press any butoon to exit");
getchar();
}

This is written by me. may be helpful for u...

#include<stdio.h>
#include <unistd.h>
void main()
{
FILE *file;
int i=0,f,l1,l2,l3=0;
char set[]="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890!@#$%&*.!@#$%^&*()";
int size=sizeof(set)-1;
char per[]="000";
//check urs all entered details here//
printf("Setlength=%d Comination are genrating\n",size);

// writing permutation here for length of 3//
for(l1=0;l1<size;l1++)
//first for loop which control left most char printed in file//
{ 
per[0]=set[l1];
// second for loop which control all intermediate char printed in file//
for(l2=0;l2<size;l2++)
{
per[1]=set[l2];
//third for loop which control right most char printed in file//
for(l3=0;l3<size;l3++)
{
per[2]=set[l3];
//apend file (add text to a file or create a file if it does not exist.//
file = fopen("file.txt","a+");
//writes array per to file named file.txt// 
fprintf(file,"%s\n",per); 
///Writing to file is completed//
fclose(file); 
i++;
printf("Genrating Combination  %d\r",i);
fflush(stdout);``
usleep(1);
}
}
}
printf("\n%d combination has been genrate out of entered data of length %d \n",i,size);
puts("No combination is left :) ");
puts("Press any butoon to exit");
getchar();
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文