【问题标题】:Efficient way of Solving Cryptarithms求解密码的有效方法
【发布时间】:2009-04-15 09:54:09
【问题描述】:

您好,我遇到了这个难题,它是著名的基于单词和数字的难题的子集,称为 Cryptarithms。假设你有一个表达式为

S E N D + M O R E = M O N E Y

现在有趣的部分是,每个字母表代表 0 到 9 的唯一数字。我想写一个通用求解器,但我最终为它写了一个蛮力解决方案。任何接受者,我该如何解决?

我认为可以使用谓词逻辑或集合论来解决。我对寻找基于 C# 或 Python 的解决方案特别感兴趣。有吗?

【问题讨论】:

    标签: c# python solver cryptarithmetic-puzzle


    【解决方案1】:

    在 PyCon 2009 上,Raymond Hettinger 谈到了 Python 中的 AI 编程,并涵盖了密码算法。

    整个演讲的视频可以在here观看,Python 2.6 解决方案的食谱可以在this link找到。

    【讨论】:

    • 整个谈话都很好。 (3, 5, 8) 加仑水罐求解器让我想起了虎胆龙威 3。
    【解决方案2】:

    这是一个小问题,暴力解决方案不是一个坏方法。假设每个字母必须代表一个唯一的数字(即我们不允许解 S = 9、M = 1、* = 0),我们看到要尝试的组合数是 n!,其中n 是密码中唯一字母的数量。要评估的理论最大组合数是10! = 3 628 800,这对于计算机来说确实是一个很小的数字。

    如果我们允许多个字母代表同一个数字,则尝试组合的数量将限制在 10^n 范围内,其中 n 是唯一的数量字母。假设只有大写英文字母,我们有 10^26 的理论最大组合数,因此对于理论上的最坏情况,我们可能需要一些启发式方法。不过,大多数实用的密码算法的唯一字母都少于 26 个,因此正常情况下可能会以小于 10 的 n 为界,这对于计算机来说也是相当合理的。

    【讨论】:

    • 蛮力可能对特定情况有好处,那么它何时必须适用于在限制内正确指定的任何输入呢?
    • 我就是这么回答的。对于任何一般输入,我们都可以找到暴力搜索的最坏情况。如果我们想要一个严格的解决方案,它是 10!,如果我们允许重复,它是 10^26。
    【解决方案3】:

    好吧,试着把它写成一个函数列表:

     SEND
     MORE
    ----+
    MONEY
    

    如果我记得我的初中数学,应该是:

    Y = (D+E) mod 10
    E = ((N+R) + (D+E)/10) mod 10
    ...
    

    【讨论】:

    • 你的基础是对的!就是这样,但我想知道如何以通用求解器的方式制作它..
    【解决方案4】:

    这是一种有效的蛮力方法,它递归地循环遍历所有可能性,但也注意到特定问题的结构以简化问题。

    每个方法的前几个参数代表每个分支的试验值,参数 v1、v2 等是尚未分配的值,可以在任何 命令。该方法是有效的,因为它有最多 8x7x5 可能的试验解决方案,而不是 10!/2 个可能的暴力解决方案

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    namespace ConsoleApplication1
    {
        class Program
        {
            static void MESDYNR(int m, int s, int e, int d, int y, int n, int r, int v1, int v2, int v3)
            {
                // Solve for O in hundreds position
                // "SEND" + "M?RE" = "M?NEY"
                int carry = (10 * n + d + 10 * r + e) / 100;
                int o = (10 + n - (e + carry))%10;
    
                if ((v1 == o) || (v2 == o) || (v3 == o)) 
                {
                    // check O is valid in thousands position
                    if (o == ((10 + (100 * e + 10 * n + d + 100 * o + 10 * r + e) / 1000 + m + s) % 10))
                    {
                        // "SEND" + "MORE" = "MONEY"
                        int send = 1000 * s + 100 * e + 10 * n + d;
                        int more = 1000 * m + 100 * o + 10 * r + e;
                        int money = 10000 * m + 1000 * o + 100 * n + 10 * e + y;
    
                        // Chck this solution
                        if ((send + more) == money)
                        {
                            Console.WriteLine(send + " + " + more + " = " + money);
                        }
                    }
                }
            }
    
            static void MSEDYN(int m, int s, int e, int d, int y, int n, int v1, int v2, int v3, int v4)
            {
                // Solve for R
                // "SEND" + "M*?E" = "M*NEY"
                int carry = (d + e) / 10;
                int r = (10 + e - (n + carry)) % 10;
    
                if (v1 == r) MESDYNR(m, s, e, d, y, n, r, v2, v3, v4);
                else if (v2 == r) MESDYNR(m, s, e, d, y, n, r, v1, v3, v4);
                else if (v3 == r) MESDYNR(m, s, e, d, y, n, r, v1, v2, v4);
                else if (v4 == r) MESDYNR(m, s, e, d, y, n, r, v1, v2, v3);
            }
    
            static void MSEDY(int m, int s, int e, int d, int y, int v1, int v2, int v3, int v4, int v5)
            {
                // Pick any value for N
                MSEDYN(m, s, e, d, y, v1, v2, v3, v4, v5);
                MSEDYN(m, s, e, d, y, v2, v1, v3, v4, v5);
                MSEDYN(m, s, e, d, y, v3, v1, v2, v4, v5);
                MSEDYN(m, s, e, d, y, v4, v1, v2, v3, v5);
                MSEDYN(m, s, e, d, y, v5, v1, v2, v3, v4);
            }
    
            static void MSED(int m, int s, int e, int d, int v1, int v2, int v3, int v4, int v5, int v6)
            {
                // Solve for Y
                // "SE*D" + "M**E" = "M**E?"
                int y = (e + d) % 10;
    
                if (v1 == y) MSEDY(m, s, e, d, y, v2, v3, v4, v5, v6);
                else if (v2 == y) MSEDY(m, s, e, d, y, v1, v3, v4, v5, v6);
                else if (v3 == y) MSEDY(m, s, e, d, y, v1, v2, v4, v5, v6);
                else if (v4 == y) MSEDY(m, s, e, d, y, v1, v2, v3, v5, v6);
                else if (v5 == y) MSEDY(m, s, e, d, y, v1, v2, v3, v4, v6);
                else if (v6 == y) MSEDY(m, s, e, d, y, v1, v2, v3, v4, v5);
            }
    
            static void MSE(int m, int s, int e, int v1, int v2, int v3, int v4, int v5, int v6, int v7)
            {
                // "SE**" + "M**E" = "M**E*"
                // Pick any value for D
                MSED(m, s, e, v1, v2, v3, v4, v5, v6, v7);
                MSED(m, s, e, v2, v1, v3, v4, v5, v6, v7);
                MSED(m, s, e, v3, v1, v2, v4, v5, v6, v7);
                MSED(m, s, e, v4, v1, v2, v3, v5, v6, v7);
                MSED(m, s, e, v5, v1, v2, v3, v4, v6, v7);
                MSED(m, s, e, v6, v1, v2, v3, v4, v5, v7);
                MSED(m, s, e, v7, v1, v2, v3, v4, v5, v6);
            }
    
    
            static void MS(int m, int s, int v1, int v2, int v3, int v4, int v5, int v6, int v7, int v8)
            {
                // "S***" + "M***" = "M****"
                // Pick any value for E
                MSE(m, s, v1, v2, v3, v4, v5, v6, v7, v8);
                MSE(m, s, v2, v1, v3, v4, v5, v6, v7, v8);
                MSE(m, s, v3, v1, v2, v4, v5, v6, v7, v8);
                MSE(m, s, v4, v1, v2, v3, v5, v6, v7, v8);
                MSE(m, s, v5, v1, v2, v3, v4, v6, v7, v8);
                MSE(m, s, v6, v1, v2, v3, v4, v5, v7, v8);
                MSE(m, s, v7, v1, v2, v3, v4, v5, v6, v8);
                MSE(m, s, v8, v1, v2, v3, v4, v5, v6, v7);
             }
    
            static void Main(string[] args)
            {
                // M must be 1
                // S must be 8 or 9
                DateTime Start = DateTime.Now;
                MS(1, 8, 2, 3, 4, 5, 6, 7, 9, 0);
                MS(1, 9, 2, 3, 4, 5, 6, 7, 8, 0);
                Console.WriteLine((DateTime.Now-Start).Milliseconds);
                return;
            }
        }
    }
    

    【讨论】:

      【解决方案5】:

      this 可能会有所帮助

      编辑:您发布的 wiki 链接上的答案也很有用!

      【讨论】:

        猜你喜欢
        • 2017-03-10
        • 1970-01-01
        • 1970-01-01
        • 2013-12-09
        • 1970-01-01
        • 1970-01-01
        • 2012-05-10
        • 1970-01-01
        • 2011-02-03
        相关资源
        最近更新 更多