开发一个中文验证码识别模块并不复杂,但要找到最佳的参数、系数和权重以获得最佳结果,这个过程会耗费一定的CPU时间。选择了一种简单的方法,没有使用通用算法或神经网络,而是使用了常见的for循环,这种方法虽然效率不高,但足以满足需求。最终,实现了大约75%的准确率,这对来说已经足够了。
最优解决方案涉及到相当复杂的嵌套循环,这并不是最优雅的解决方案。例如,以下是C#代码的一个示例:
int lowMaskLevel = 0;
while (lowMaskLevel <= 90) {
int topMaskLevel = lowMaskLevel;
while (topMaskLevel <= 255) {
int positiveFinalKoef = 0;
while (positiveFinalKoef <= 100) {
int negativeFinalKoef = 0;
while (negativeFinalKoef <= 100) {
int finePositiveKoef = 0;
while (finePositiveKoef <= 30) {
int fineNegativeKoef = 0;
while (fineNegativeKoef <= 20) {
int hightLightKoef = 1;
while (hightLightKoef <= 3) {
// ...
var value = TestParameters(lowMaskLevel, topMaskLevel, positiveFinalKoef, negativeFinalKoef, finePositiveKoef, fineNegativeKoef, hightLightKoef);
if (value >= bestValue) {
bestLowMaskLevel = lowMaskLevel;
bestTopMaskLevel = topMaskLevel;
bestPositiveFinalKoef = positiveFinalKoef;
bestNegativeFinalKoef = negativeFinalKoef;
bestFinePositiveKoef = finePositiveKoef;
bestFineNegativeKoef = fineNegativeKoef;
bestHightLightKoef = hightLightKoef;
// ...
}
hightLightKoef++;
}
fineNegativeKoef++;
}
finePositiveKoef++;
}
negativeFinalKoef++;
}
positiveFinalKoef++;
}
topMaskLevel++;
}
lowMaskLevel++;
}
这种方法的维护难度相当高,因为参数、规则和限制的数量可能会发生变化。因此,放弃了这种方法,转而采用了一种更易于管理的解决方案,即通过一个辅助类来处理迭代,使事情变得清晰和可维护。
现在,可以像定义常规方法参数一样定义迭代器参数,看起来更整洁、更易于支持或进行任何更改。以下是C#代码的一个示例:
int bestValue = 0;
object[] bestArguments = null;
Iterator.Execute(() => TestParameters(
Iterator.Range(0, 90),
Iterator.Range(args => (int)args[0], args => 255),
Iterator.Range(0, 100, 10),
Iterator.Range(0, 100),
Iterator.Range(0, 30),
Iterator.Range(0, 20),
Iterator.Range(0, 3)
), invocationResult => {
if (invocationResult.Value >= bestValue) {
bestValue = invocationResult.Value;
bestArguments = invocationResult.Arguments;
}
});
要使用它,需要添加对WestWayConsulting.Tools.Iterator.dll程序集的引用。请注意,这个程序集引用了Immutable Collections,因此它是用.NET Framework 4.5编译的。该工具有一个Facade类Iterator,用于定义和执行迭代。
要执行迭代,需要调用静态Execute方法,该方法有两个重载版本——一个用于调用返回值的方法,另一个用于调用动作方法。Execute方法接受以下参数:
当在迭代器中定义方法时,仍然可以使用任何类型的参数,就像通常做的那样,并将它们与迭代器定义混合使用。因此,以下语句将有效:
Iterator.Execute(() => MyMethod(
1,
DateTime.Now,
Iterator.Range(0, 100, 10),
Iterator.RangeParallel(0, 100),
new WebClient()
));
请注意,不能在主方法调用之外定义迭代器。以下语句将抛出InvalidOperationException异常:
Iterator.Execute(() => MyMethod(
new DateTime(2013, 1, Iterator.Range(0, 100, 10))
));
在当前版本中,提供了以下迭代器:
每个定义都有带有动态参数的重载。通过使用它们,可以使用复杂的规则定义迭代参数。定义动态参数的函数接受来自上述参数的值数组。
Iterator.Execute(() => MyMethod(
1,
// arg[0] DateTime.Now,
// arg[1] Iterator.Range(args => (int)args[0], args => 255),
// arg[2] Iterator.RandomParallel(args => (int)args[2], args => DateTime.Now.Seconds + 255),
// arg[3] new WebClient()
));
工作原理:
Execute方法解析Lambda表达式中的顶级参数,并生成一个参数处理程序的链接链。每个处理程序都知道要执行的下一个处理程序。
Iterator.Execute(() => TestParameters(
Iterator.Range(0, 90), // _iterator {arg[0]}
Iterator.Range(args => (int)args[0], args => 255), // _iterator {arg[0], arg[1]}
DateTime.Now, // _method as argument {arg[0], arg[1], arg[2], arg[3]}
));