c#Bruteforce从指定点开始

时间:2013-01-25 15:19:38

标签: c# algorithm recursion brute-force

我需要找到一种方法来在指定的字符串而不是开头处启动此字符串生成算法。例如,不是从'aaaa'开始,而是从'baxi'开始,然后通过其余的字符串空间。

    private static String Charset = "abcdefghijklmnopqrstuvwxyz";

    /// <summary>
    /// Start Brute Force.
    /// </summary>
    /// <param name="length">Words length.</param>
    public static void StartBruteForce(int length)
    {
        StringBuilder sb = new StringBuilder(length);
        char currentChar = Charset[0];

        for (int i = 1; i <= length; i++)
        {
            sb.Append(currentChar);
        }

        int counter = 0;
        ChangeCharacters(0, sb, length, ref counter);
        Console.WriteLine(counter);
    }

    private static void ChangeCharacters(int pos, StringBuilder sb, int length, ref int counter)
    {
        for (int i = 0; i <= Charset.Length - 1; i++)
        {                
            sb[pos] = Charset[i];
            if (pos == length - 1)
            {
                counter++;
                Console.WriteLine(sb.ToString());
            }
            else
            {
                ChangeCharacters(pos + 1, sb, length, ref counter);
            }
        }
    }

2 个答案:

答案 0 :(得分:2)

你所拥有的是非常接近的,但看起来问题的根源是ChangeCharacters被写入总是从第一个可能的字符串开始,例如每个位置的字符始终从字母表的第一个字母开始(在您的示例中为'a')。您需要在起始字符串中的每个位置进行第一次传递,以使用已经存在的字母开始,随后的传递将以生成字母表的第一个字符开头。

因此,使用您已有的代码,您需要进行以下更改:

  1. 传递一个标志,表明这是第一次通过。
  2. 根据第一个通过标记选择循环的起点。
  3. 将第一次通过标记传递给递归调用。
  4. 在每个位置第一次通过后重置旗帜。
  5. 使用您的起始字符串初始化StringBuilder,而不是当前的默认起点。
  6. 为了清晰起见,还有其他一些值得改变的项目。这些都不是严格要求的正确性,但它们确实使代码更容易阅读和理解:

    1. 无需传递length,因为它始终与sb.Length相同。重复的信息最多会迫使读者跟踪更多信息,如果后来的代码更改破坏了关系,最坏的情况可能会导致错误。
    2. 使用从零开始的索引的语言中索引循环的标准习惯是“端点排他”形式,使用'小于'(或甚至'不等于')比较器,因为这可以避免溢出边界上的错误,例如i < length代替i <= length - 1。大多数代码读者本能地理解这个习语,而包含端点的形式通常会迫使读者考虑为什么需要反成语。

    3. 不要通过引用传递计数器,只需返回生成的字符串数。不改变外部状态的方法(通常称为“纯”或“功能”)通常更容易理解。 (但请注意,您传递的StringBuilder中也有状态。)

      • 作为进一步的改进,我甚至建议返回IEnumerable<string>,这不仅不需要跟踪计数,而且还允许调用者确定如何处理字符串,而不是使用决定(例如,呼叫Console.WriteLine并递增一个计数器)内幕你的方法。
    4. 将所有这些结合在一起,您的代码就变成了这个(带有注释的注释,指向正在进行的更改或建议):

      public static void StartBruteForce(string start)
      {
         /*change 5*/ StringBuilder sb = new StringBuilder(start);
         /*sugg 3*/ int counter = ChangeCharacters(0, /*change 1*/ true, sb);
         Console.WriteLine(counter);
      }
      
      private static int ChangeCharacters(int pos, /*change 1*/ bool firstPass , StringBuilder sb)
      {
          /*sugg 3*/ int counter = 0;
          for (int i = /*change 2*/ firstPass ? Charset.IndexOf(sb[pos]) : 0; /*sugg 2*/ i < Charset.Length; i++)
          {                
              sb[pos] = Charset[i];
              if (pos == /*sugg 1*/ sb.Length - 1)
              {
                  counter++;
                  Console.WriteLine(sb.ToString());
              }
              else
              {
                  /*sugg 3*/ counter += ChangeCharacters(pos + 1, /*change 3*/ firstPass, sb);
                  /*change 4*/ firstPass = false;
              }
          }
          /*sugg 3*/ return counter;
      }
      

答案 1 :(得分:0)

您的递归算法需要分解为步骤才能恢复。

请参阅this factorial algorithm分解步骤,以便恢复。


或者,您需要知道恢复点[在任何情况下您都需要]但在点击该点之前忽略所有值。

这是一个(更糟糕的)天真的实现,并且 CPU-wise它使恢复计算的整个想法无效 - 因为它实际上没有恢复,它只是不捕获/打印较早的值:

private static String Charset = "abcdefghijklmnopqrstuvwxyz";

/// <summary>
/// Start Brute Force.
/// </summary>
/// <param name="length">Words length.</param>
public static void StartBruteForce(int length)
{
    StringBuilder sb = new StringBuilder(length);
    char currentChar = Charset[0];

    for (int i = 1; i <= length; i++)
    {
        sb.Append(currentChar);
    }

    int counter = 0;

    var resumePoint = 60975;

    ChangeCharacters(0, sb, length, ref counter, resumePoint);

    Console.WriteLine(counter);
}

private static void ChangeCharacters(int pos, StringBuilder sb, int length, ref int counter, int resumePoint)
{
    for (int i = 0; i <= Charset.Length - 1; i++)
    {
        sb[pos] = Charset[i];
        if (pos == length - 1)
        {
            counter++;
            if (counter >= resumePoint)
            {
                Console.WriteLine(string.Format("{0} : {1}", counter, sb.ToString()));
            }
        }
        else
        {
            ChangeCharacters(pos + 1, sb, length, ref counter, resumePoint);
        }
    }
}