生成不同字符串的非递归组合算法

Posted

技术标签:

【中文标题】生成不同字符串的非递归组合算法【英文标题】:Non-recursive combination algorithm to generate distinct character strings 【发布时间】:2013-12-23 06:28:58 【问题描述】:

这个问题困扰我太久了。我需要C 中的非递归算法来生成不明确的字符串。例如,如果给定字符串的长度为26 个字符,并且该字符串的长度为2,则存在26^2 非不同字符。

请注意,这些是不同的组合,aabbaaaba 不同。我搜索了 S.O.,大多数解决方案都会产生不明显的组合。另外,我不需要排列。

算法不能依赖库。我将把这段 C 代码翻译成标准 C 库不能工作的 cuda(至少效率不高)。

在向您展示我开始的内容之前,让我解释一下程序的一个方面。它在 GPU 上是多线程的,所以我用几个字符初始化开始字符串,在本例中为 aa。为了创建一个组合,我根据所需的长度添加一个或多个字符。

这是我尝试过的一种方法:

int main(void)

   //Declarations
   char final[12] = 0;
   char b[3] = "aa";
   char charSet[27] = "abcdefghijklmnopqrstuvwxyz"; 
   int max = 4; //Set for demonstration purposes
   int ul = 1;
   int k,i;

   //This program is multithreaded on a GPU. Each thread is initialized 
   //to a starting value for the string. In this case, it is aa

   //Set final with a starting prefix
   int pref = strlen(b);
   memcpy(final, b, pref+1);

   //Determine the number of non-distinct combinations
   for(int j = 0; j < length; j++) ul *= strlen(charSet);

   //Start concatenating characters to the current character string
   for(k = 0; k < ul; k++)
   
        final[pref+1] = charSet[k];
        //Do some work with the string

   
   ...

很明显,这个程序没有任何用处,如果我只是从charSet 中附加一个字符,请接受。

我的教授建议我尝试使用映射(这不是家庭作业;我向他询问了在没有递归的情况下生成不同组合的可能方法)。

他的建议与我上面开始的类似。使用计算出的组合数,他建议按照mod 10进行分解。然而,我意识到这行不通。

例如,假设我需要附加两个字符。这给了我 676 个使用上述字符集的组合。如果我是第 523 个组合,他演示的分解将产生

523 % 10 = 3
52 % 10 = 2
5 % 10 = 5

显然这不起作用。第一,它产生三个字符,第二,如果我的字符集大于 10 个字符,映射会忽略索引 9 以上的那些。

不过,我相信映射是解决方案的关键。

我探索的另一种方法用于循环:

//Psuedocode
c = charset;

for(i = 0; i <length(charset); i++)
    concat string

    for(j = 0; i <length(charset); i++)
          concat string

          for...

但是,这会硬编码我要计算的字符串的长度。我可以使用带有gotoif 语句来破坏它,但我想避免这种方法。

感谢任何建设性的意见。

【问题讨论】:

提示:如何在不递归的情况下生成数字 0-9999?除了您使用的是 base-26 之外,您的问题完全相同。 @R.. 不知道这是不是你的意思,但我会简单地做一个 for 循环并将一些整数变量增加到所需的最大值。 没错。所以你的问题是如何对字符数组中表示的 base-26 数字做同样的事情。首先在纸上计算如何使用以 10 为底的数字来处理数字。顺便说一句,使用前导零,所以 0 是 0000 或其他。 @R.. 和 1 是 0001。如果是这种情况,您是否实质上建议像在映射中那样为每个“字符”分配一个唯一编号? (假设我们仍在 base-10 中)。 好吧,如果您使用以 10 为底的数字,是的,您首先从 0000 到 0001,然后到 0002,依此类推,直到到达 0009。接下来会发生什么?跨度> 【参考方案1】:

给定一个字符串,查找序列中下一个可能的字符串:

    在字符串中查找不是字母表中最后一个字符的最后一个字符。

    用字母表中的下一个字符替换它。

    将该字符右侧的每个字符更改为字母表中的第一个字符。

从一个重复字母表第一个字符的字符串开始。当第 1 步失败时(因为字符串是字母表的最后一个字符),那么你就完成了。

例如:字母表是"ajxz"

aaaa开头。

第一次迭代:不是z 的最右边的字符是最后一个。将其更改为下一个字符:aaaj

第二次迭代。同上。 aaax

第三次迭代:再次。 aaaz

四次迭代:现在最右边的非z 字符是倒数第二个。推进它并将所有字符向右更改为aaaja

等等

【讨论】:

在决赛周和剥夺睡眠的日子里,是的。谢谢。【参考方案2】:

首先,感谢大家的意见;这很有帮助。由于我正在将此算法转换为 cuda,因此我需要它在 GPU 上尽可能高效。提出的方法确实有效,但不一定对 GPU 架构是最佳的。我使用模块化算法提出了一个不同的解决方案,该解决方案利用了我的字符集的基础。这是一个示例程序,主要在C 中混合C++ 用于输出,而且速度相当快。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <iostream>

using namespace std;
typedef unsigned long long ull;

int main(void)

   //Declarations
   int init = 2;
   char final[12] = 'a', 'a';

   char charSet[27] = "abcdefghijklmnopqrstuvwxyz"; 
   ull max = 2; //Modify as need be


   int base = strlen(charSet);
   int placeHolder; //Maps to character in charset (result of %)
   ull quotient;  //Quotient after division by base

   ull nComb = 1;
   char comb[max+1]; //Array to hold combinations

   int c = 0;
   ull i,j;


   //Compute the number of distinct combinations ((size of charset)^length)
   for(j = 0; j < max; j++) nComb *= strlen(charSet);


   //Begin computing combinations
   for(i = 0; i < nComb; i++)
       quotient = i;

      for(j = 0; j < max; j++) //No need to check whether the quotient is zero
             placeHolder = quotient % base;
             final[init+j] = charSet[placeHolder]; //Copy the indicated character
             quotient /= base; //Divide the number by its base to calculate the next character
      

      string str(final);
      c++;
      //Print combinations
      cout << final << "\n";
  
  cout << "\n\n" << c << " combinations calculated";
  getchar();

【讨论】:

以上是关于生成不同字符串的非递归组合算法的主要内容,如果未能解决你的问题,请参考以下文章

Heap's Algorithm - Python 中用于生成排列的非递归方法

《数据结构》遍历二叉树的非递归算法的疑问。

二叉树的非递归遍历

二叉树几种遍历算法的非递归实现

n阶乘的非递归算法

两点之间简单路径的非递归 DFS 算法