【问题标题】:Extending C Brute Force Algorithm扩展 C 蛮力算法
【发布时间】:2019-07-14 21:53:04
【问题描述】:

我需要一个针对字母数字字符的蛮力算法。

我使用的代码只是将所有排列打印到标准输出。我尝试了几个小时,但未能以这样的方式重写代码,以至于我可以在需要时调用函数 brute_next() 来获取下一个代码字。

有人可以帮我重写这段代码吗?函数brute_next() 应该返回一个char* 或者获取一个char* 作为参数。我在 Mac 下使用 CLion 和 gcc。

代码是(source):

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

static const char alphabet[] =
"abcdefghijklmnopqrstuvwxyz"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"0123456789";

static const int alphabetSize = sizeof(alphabet) - 1;

void bruteImpl(char* str, int index, int maxDepth)
{
    for (int i = 0; i < alphabetSize; ++i)
    {
        str[index] = alphabet[i];

        if (index == maxDepth - 1) printf("%s\n", str);
        else bruteImpl(str, index + 1, maxDepth);
    }
}

void bruteSequential(int maxLen)
{
    char* buf = malloc(maxLen + 1);

    for (int i = 1; i <= maxLen; ++i)
    {
        memset(buf, 0, maxLen + 1);
        bruteImpl(buf, 0, i);
    }

    free(buf);
}

int main(void)
{
    bruteSequential(3);
    return 0;
}

这是我将递归转换为生成器的无效尝试。只是无法弄清楚排列算法是如何工作的。

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

static const char alphabet[] =
        "abcdefghijklmnopqrstuvwxyz"
        "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
        "0123456789"
        "$%&/()=.-_;!+*#";

static const int alphabetSize = sizeof(alphabet) - 1;

struct bruteconfig {
    int index;
    int i1;
    int i2;
    char* str;
    int maxDepth;
};

static struct bruteconfig* config;

void brute_init(int maxLen){
    free(config);
    config = malloc(sizeof(struct bruteconfig*));

    config->i1 = 1;
    config->i2 = 0;
    config->index = 0;
    config->maxDepth = maxLen;
}

void bruteImpl()
{
    if(config->i2 > alphabetSize)   // how to transform for to iterative?
        config->i2 = 0;

    config->str[config->index] = alphabet[config->i2];

    if (config->index == config->maxDepth - 1) {
        //printf("%s\n", config->str);
        return; // str filled with next perm
    }
    else {
        config->index++;
        //bruteImpl(config->str, config->maxDepth);
    }

    config->i2++;
}

char* bruteSequential()
{
    config->str = malloc(config->maxDepth + 1);

    if(config->i1 >= config->maxDepth)
        return NULL;

    memset(config->str, 0, config->maxDepth + 1); // clear buf
    bruteImpl(config->str, config->i1);       // fill with next perm

    return config->str;
    //free(buf);    // needs to be done by the caller
}

【问题讨论】:

  • 请在问题本身中包含任何相关代码,而不是作为外部链接。修剪任何不必要的内容来重现您遇到的问题。
  • 1:将递归函数转换为迭代函数。请注意,您的函数每次调用只会将索引增加一,因此它可能是for (int index = 0; index != maxDepth - 1; index++)。 2:识别“上下文” - 即。识别您在代码中的位置所需的所有变量。那将是您的函数中的所有局部变量。 3:创建一个struct brute_ctx_s { /* members */ } 结构,它将保存函数的所有上下文变量,并通过指向函数的指针传递上下文。让所有局部变量使用上下文。
  • @KamilCuk 谢谢!这正是我的尝试。但是,我只是无法弄清楚该算法是如何工作的。我试图将其转换为将局部变量保存在结构中的迭代算法,但输出完全错误......
  • 4:注意,如果你返回一个char* 变量,你必须管理它的内存。确定您是否知道此类变量的最大长度并希望使用静态分配或动态分配。 5. 所以这个函数可能看起来像char *brute_next(struct brute_ctx_s *ctx),你很可能还需要int brute_start(struct brute_ctx_s *ctx)bool brute_end(struct brute_ctx_s *ctx) 来启动和停止你生成的暴力序列。主观上,我会选择指针,例如。 int brute_next(struct brute_ctx_s *ctx, char **ret, size_t *size),但那是我。 6.size_t类型用于表示大小。
  • @KamilCuk 我会再次尝试发布我的尝试..

标签: c brute-force


【解决方案1】:

您正在尝试从递归切换到使用生成器:主要区别在于递归将工作状态隐式存储在调用堆栈中,而生成器需要显式存储其所有状态以供下次调用使用。

因此,首先您需要考虑在递归版本中为您隐式保存的状态:

  • 每个级别的递归调用都有自己的参数index 的值
  • 每个级别都有自己的局部变量i的值
  • ...就是这样。

您有maxDepth 级别,编号为0..maxDepth-1,每个级别在字母表中都有自己的当前位置。注意index参数也只是这个集合中的位置,不需要单独存放。

现在,您需要在调用之间存储一些持久状态,它将是maxDepth 整数字母位置的数组。你能弄清楚如何编写一个函数来将该数组转换为字符串吗?你能想出如何以与递归代码相同的方式将状态提升到一个位置吗?


编辑您的状态应该类似于

struct PermutationState {
  /* stringLength == maxDepth */
  int stringLength;
  char *string;
  /* better to avoid globals */
  int alphaLength;
  const char *alphabet;
  /* this replaces i as the index into our alphabet */
  int *alphaPos;
};

我建议写一个类似的界面

struct PermutationState* start_permutation(int stringLength,
                                           int alphaLength,
                                           const char *alphabet)
{
  struct PermutationState *state = malloc(sizeof(*state));
  if (!state) return NULL;
  /* initialize scalar values first, for easier error-handling */
  state->stringLength = stringLength;
  state->string = NULL;
  state->alphaLength = alphaLength;
  state->alphabet = alphabet;
  state->alphaPos = NULL;

  /* now we can handle nested allocations */
  state->string = malloc(stringLength + 1);
  state->alphaPos = calloc(stringLength, sizeof(int));
  if (state->string && state->alphaPos) {
    /* both allocations succeeded, and alphaPos is already zeroed */
    memset(state->string, alphabet[0], stringLength);
    state->string[stringLength] = 0;
    return state;
  }

  /* one or both of the nested allocations failed */
  end_permutation(state);
  return NULL;
}

void end_permutation(struct PermutationState *state)
{
    free(state->string);
    free(state->alphaPos);
    free(state);
}

最后你希望实现这个功能:

char *next_permutation(struct PermutationState *state)
{
    /* TODO */
}

由于start_permutation 已经为您设置了state-&gt;alphaPos = [0, 0, ... 0]state-&gt;string = "aaa...a",您可能希望将alphaPos 前进一位,然后返回当前字符串。

注意。我假设您不需要复制字母表,这意味着调用者有责任保证其生命周期。如有必要,您也可以轻松复制它。

【讨论】:

  • 重写为迭代算法并保存局部变量也是我的想法,但它给了我错误的输出,我只是无法弄清楚置换算法是如何工作的,但我会再试一次并发布我的尝试...谢谢
  • 我尝试了另一次尝试并更新了我的帖子,但我不知道该怎么做,它不起作用。
  • 您有一个index 值和两个(出于某种原因)i 值。我说你需要maxDepthi 的副本,这意味着你需要一个数组,而不是固定数量的命名变量!
  • 非常感谢您的回答!!我会尝试实现你给我的结构,另一个用户刚刚发布了一个非常小的工作代码,但我认为这对学习很有好处......
【解决方案2】:

我只是不知道排列算法是如何工作的

这很简单:从一个单词到下一个单词,从最右边的位置开始,将该字符更改为字母表中的下一个;如果没有下一个字符,则将位置重置为字母表中的第一个字符并继续向左更改位置;如果没有剩余位置,则需要加长码字。这是一个示例实现:

char *brute_next()
{
    for (; ; )
    {
        static char *buf;               // buffer for codeword
        static int maxDepth;            // length of codeword
        int i, index = maxDepth-1;      // alphabet and buffer index, resp.
        while (0 <= index)              // as long as current length suffices:
        {   // next char at buf[index] is next in alphabet or first:
            i = buf[index] ? strchr(alphabet, buf[index]) - alphabet + 1 : 0;
            if (buf[index] = alphabet[i]) return buf;

            buf[index--] = alphabet[0]; // reset to 'a', continue to the left
        }
        index = maxDepth++;             // now need to lengthen the codeword
        buf = realloc(buf, maxDepth+1); // string length + terminator
        if (!buf) exit(1);
        buf[index] = buf[maxDepth] = '\0';
    }
}

【讨论】:

  • 非常感谢!!它就像魅力一样。哦!然后我是对的,我查看了字母“abc”的输出并得出了这个结论,但是重写代码来存档它太令人困惑了。仅当我调用 brute_next() x 次后才需要释放返回的指针,对吗?还是每次通话后?仍在研究 while 循环的工作原理。
  • 你是对的,你只需要释放返回的指针一次。但请注意,此示例代码有一个限制:没有规定要从头开始重新启动一系列 brute_next() 调用。为此,可以将参数添加到导致bufmaxDepth 被清除的函数。 - 关于原始代码的重写:这在像 Python 这样具有生成器函数的语言中会很容易;在 C 语言中,一种直接的方法可能是使用(不可移植的)协程。
猜你喜欢
  • 2011-11-08
  • 2015-06-22
  • 1970-01-01
  • 2015-01-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多