c# 暴力破解从指定点开始
本文关键字:开始 破解 | 更新日期: 2023-09-27 17:55:55
我需要找到一种方法在指定的字符串而不是开头启动此字符串生成算法。例如,不是从"aa"开始,而是从"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);
}
}
}
你得到的非常接近,但看起来问题的根源是ChangeCharacters
总是从第一个可能的字符串开始,例如,每个位置的字符总是从字母表的第一个字母开始(在你的例子中'a'
)。您需要在起始字符串中的每个位置进行第一次传递,以便从已存在的字母开始,然后后续传递将从生成字母表的第一个字符开始。
因此,使用已有的代码,需要进行以下更改:
- 沿线传递一个标志,指示这是第一次传递。
- 根据第一遍标志选择循环的起点。
- 将第一个传递标志向下传递递归调用。
- 在每个位置进行第一次通过后重置标志。
- 使用起始字符串(而不是当前默认起始点)初始化
StringBuilder
。
还有其他一些项目值得更改。这些都不是正确性所严格要求的,但它们确实使代码更易于阅读和理解:
- 没有必要
- 传递
length
,因为它总是和sb.Length
一样。重复的信息充其量会迫使读者跟踪更多信息,最坏的情况是,如果以后的代码更改破坏了关系,则可能导致错误。 在使用从零开始的索引的语言中,索引循环的标准习惯用法是"端点独占"形式,使用"小于"(甚至"不等于")比较器,因为这可以避免边界处的溢出错误,例如
i < length
而不是i <= length - 1
.大多数代码阅读器本能地理解这个习惯用法,而包含端点的形式通常会迫使读者考虑为什么需要反习语。与其通过引用传递计数器,不如简单地返回生成的字符串计数。不改变外部状态的方法,通常称为"纯"或"功能",通常更容易理解。(但是请注意,在传递的
StringBuilder
中也有状态。- 作为进一步的改进,我什至建议返回一个
IEnumerable<string>
,这不仅消除了跟踪计数的需要,而且还让调用者确定如何处理字符串,而不是做出该决定(例如调用Console.WriteLine
和增加计数器)在你的方法内部。
- 作为进一步的改进,我什至建议返回一个
将所有这些放在一起,您的代码就变成了这样(用注释注释指出哪些更改或建议正在发挥作用):
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;
}
您的递归算法需要分解为多个步骤,以便可以恢复。
请参阅此阶乘算法分为几个步骤,以便可以恢复。
或者,您需要知道恢复点[在任何情况下都需要],但在达到该点之前忽略所有值。
这是一个(比)更糟糕的幼稚实现,从 CPU 上讲,它使恢复计算的整个想法无效——因为它实际上并没有恢复,它只是没有捕获/打印早期的值:
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);
}
}
}