Math 用C+制作密码运算器+; 我计划一个C++程序,它用3个字符串来表示一个密码问题。例如,给定2、2和4,程序会找到每个字母的数字替换,这样数学表达式 TWO + TWO ------ FOUR

Math 用C+制作密码运算器+; 我计划一个C++程序,它用3个字符串来表示一个密码问题。例如,给定2、2和4,程序会找到每个字母的数字替换,这样数学表达式 TWO + TWO ------ FOUR,math,artificial-intelligence,cryptarithmetic-puzzle,Math,Artificial Intelligence,Cryptarithmetic Puzzle,是真的,假设输入是正确的。解决这个问题的一种方法当然是强制执行,用嵌套循环为每个字母指定每个可能的替换,反复尝试求和,等等,直到最终找到答案 我的想法是,尽管这是非常低效的,但底层的循环检查可能是可行的(甚至是必要的)方法——在执行一系列的演绎以限制每个变量的域之后。我发现这有点难以想象,但首先假设这样的一般/填充结构是否合理(每个X代表一个不一定不同的数字,每个C代表一个进位数字,在这种情况下,它将是0或1)?: 考虑到这一点,还有一些规划思路: -虽然在这个问题中不会给出前导零,但我可能应该

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

我的想法是,尽管这是非常低效的,但底层的循环检查可能是可行的(甚至是必要的)方法——在执行一系列的演绎以限制每个变量的域之后。我发现这有点难以想象,但首先假设这样的一般/填充结构是否合理(每个X代表一个不一定不同的数字,每个C代表一个进位数字,在这种情况下,它将是0或1)?:

考虑到这一点,还有一些规划思路:

-虽然在这个问题中不会给出前导零,但我可能应该在适当的地方添加足够多的前导零,以使操作数达到平衡/匹配

-我想我应该从每个字母的一组可能值0-9开始,可能作为向量存储在“域”表中,并在进行演绎时从中删除值。例如,如果我看到一些字母像这样排列

 A
 C
--
 A
,我可以看出C是零,这将从其域中删除所有其他值。我可以想出很多推论,但将它们推广到各种小情况并将其转化为代码乍一看似乎有点棘手

-假设我有一系列很好的推论,这些推论贯穿整个过程,并从domains表中引导出许多值,我想我仍然会循环所有内容,并希望状态空间足够小,能够在合理的时间内生成解决方案。但感觉应该不止这些也许有一些聪明的方程式可以建立,或者类似的东西


小费是感激的

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


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

我用随机爬山算法解决了这个问题。基本思想是选择一个随机的数字到字母的赋值,通过计算等式两边之间的差来给赋值打分,然后改变赋值(交换两个数字)并重新计算分数,保留那些提高分数的更改,丢弃那些没有提高分数的更改。这就是爬山,因为你只接受一个方向的改变。爬山的问题是,它有时会卡在一个局部最大值上,所以你经常会放弃当前的尝试,重新开始;这是算法的随机化部分。该算法非常快:它能在几分之一秒内解决我给出的每一个密码运算。

密码运算问题是经典的。基本上,您需要做的是让您的程序根据输入生成约束,这样您就可以使用给定的示例得到如下结果:

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的搜索空间

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

(请注意,除了最低有效列du之外,您不能做出像
A+C=A
->
C==0
这样的假设。)
Range(O, W, T) == {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
Range(Carry1, Carry2, F) == {0, 1}