用C ++编写一个cryptaritmetic求解器

时间:2013-06-21 02:49:44

标签: math artificial-intelligence cryptarithmetic-puzzle

我正在计划一个C ++程序,该程序需要3个表示密码算术难题的字符串。例如,给定TWO,TWO和FOUR,程序将找到每个字母的数字替换,以便数学表达式

   TWO 
+  TWO 
------
  FOUR

是正确的,假设输入是正确的。解决这个问题的一种方法当然是强制它,用嵌套循环为每个字母分配每个可能的替换,重复尝试总和等,直到最终找到答案。

我的想法是,虽然这是非常低效的,但是在执行一系列推算以限制每个变量的域之后,潜在的循环检查可能是可行的(甚至是必要的)方式。我觉得很难想象,但是首先假设像这样的通用/填充结构是合理的(每个X代表一个不一定不同的数字,每个C都是一个进位数,在这种情况下,会是0还是1)? :

   CCC.....CCC
   XXX.....XXXX
+  XXX.....XXXX
----------------
  CXXX.....XXXX 

考虑到这一点,还有一些计划性的想法:

- 虽然问题中没有给出前导零,但我可能应该在适当的地方添加足够多的内容,以便将操作数排除在外。

- 我想我应该从每个字母的一组可能值0-9开始,可能在'域'表中存储为矢量,并在扣除时从中消除值。例如,如果我看到一些像这样排列的字母

 A
 C
--
 A

,我可以说C是零,这消除了其域中的所有其他值。我可以想到相当多的推论,但是将它们推广到各种小情况并将它放入代码中乍一看似乎有些棘手。

- 假设我有一系列很好的推论贯穿整个事情,并从域表中引出了很多值,我想我仍然只是循环遍历所有内容并希望状态空间足够小以生成解决方案在合理的时间内。但感觉必须要有更多的东西! - 可能是一些聪明的方程式设置或沿着那些线。

提示非常感谢!

3 个答案:

答案 0 :(得分:1)

您可以从右到左迭代此问题,即您执行实际操作的方式。从最右边的列开始。对于您遇到的每个数字,您可以检查是否已存在该数字的分配。如果存在,则使用其值并继续。如果没有,则在所有可能的数字上输入一个循环(如果你想要一个双射映射,可能省略已经使用的数字),并递归地继续每个可能的赋值。到达总和行时,再次检查是否已分配给定数字的变量。如果不是,则指定当前总和的最后一位数,然后继续下一个更高价值的列,随身携带。如果已经有作业,并且它同意结果的最后一位,则以相同的方式继续。如果有任务并且不同意,那么您将中止当前分支,并返回到您可以选择其他数字的最近循环。

这种方法的好处应该是许多变量是由总和确定的,而不是预先猜到的。特别是对于仅出现在总和行中的字母,这可能是一个巨大的胜利。此外,您可能能够尽早发现错误,从而避免在某些情况下选择字母,而目前您做出的选择已经不一致。缺点可能是程序的递归结构稍微复杂一些。但是一旦你做对了,你也会学到很多关于将思想转化为代码的知识。

答案 1 :(得分:1)

我使用随机爬山算法在blog解决了这个问题。基本思路是选择数字随机分配给字母,通过计算等式两边之间的差异来“分数”分配,然后改变分配(交换两位数)并重新计算分数,保持那些改进的改进分数并丢弃那些没有的变化。这是爬山,因为你只接受一个方向的变化。爬山的问题在于它有时会陷入局部最大值,所以你经常抛出当前的尝试并重新开始;这是算法的随机化部分。算法非常快:它解决了我在几分之一秒内给出的每个密码。

答案 2 :(得分:0)

密码算术问题是经典的constraint satisfaction problems。基本上,您需要做的是让您的程序根据输入生成约束,这样您最终会得到类似下面的内容,使用您给出的示例:

O + O = 2O = R + 10Carry1
W + W + Carry1 = 2W + Carry1 = U + 10Carry2
T + T + Carry2 = 2T + Carry2 = O + 10Carry3 = O + 10F

广义伪代码:

for i in range of shorter input, or either input if they're the same length:
    shorterInput[i] + longerInput2[i] + Carry[i] = result[i] + 10*Carry[i+1] // Carry[0] == 0

for the rest of the longer input, if one is longer:
    longerInput[i] + Carry[i] = result[i] + 10*Carry[i+1]

基于问题定义的其他约束:

Range(digits) == {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
Range(auxiliary_carries) == {0, 1}

所以对你的例子来说:

Range(O, W, T) == {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
Range(Carry1, Carry2, F) == {0, 1}

生成约束以限制搜索空间后,您可以使用链接文章中描述的CSP解析技术来遍历搜索空间并确定您的解决方案(当然,如果存在)。 (本地)一致性的概念在这里非常重要,利用它可以大大减少CSP的搜索空间。

作为一个简单的例子,请注意,密码算术通常不使用前导零,这意味着如果结果比两个输入都长,则最后一个数字,即最后一个进位数字必须为1(所以在您的示例中,它意味着{{ 1}})。然后可以向后传播此约束,因为这意味着F == 1;换句话说,2T + Carry2 == O + 10的最小值必须为5,因为T最多可以是1和2(4)+ 1 == 9。还有其他增强搜索的方法(最小冲突算法等),但我宁愿不把这个答案变成一个成熟的CSP课程,所以我将进一步调查你。

(请注意,您不能进行Carry2 - > A+C=A之类的假设,但由于C == 0可能为9且进位数字为这意味着C通常会限制在域C,但是,你并没有完全关闭。)