【问题标题】:A more efficient approach to Verbal arithmetic / Alphametics?一种更有效的语言算术/字母表方法?
【发布时间】:2009-11-19 20:25:58
【问题描述】:

也许你们中的大多数人都知道发送 + 更多 = 金钱。好吧,我目前正在学习 java,其中一个练习是我必须解决 HES + THE = BEST。

现在,到目前为止,我可以/应该使用 if-for-while-do 循环,仅此而已。虽然我确信有不同的方法可以解决它,但这不是我正在进行的练习的重点。我必须能够以最有效的方式使用 if-for-while-do 循环。

我的问题?我似乎想不出一种有效的方法来解决它!我想出了这个,它解决了这个难题,但可能是最糟糕的方法:

public class Verbalarithmetics {

    public static void main (String args[]) {
        // Countint Variables
        int index_h = 0;
        int index_e = 0;
        int index_s = 0;
        int index_t = 0;
        int index_b = 0;

        // Start with h = 1 and increase until the else-if statement is true
        for(int h = 1; h <= 9; h++) { // h = 1, because first Symbol can't be zero
            index_h++;
                // Increase e so long until e equals h
                for(int e = 0; e <= 9; e++) {
                    index_e++;
                 if (e == h) {
                    continue;
                 }

                 // Increase s so long until s equals h or e
                 for(int s = 0; s <= 9; s++) {
                     index_s++;
                    if (s == h || s == e) {
                       continue;
                    }//end if

                    // Increase t so long until t equals h or e or s.
                    for(int t = 1; t <= 9; t++) { // t = 1, because 1st Symbol cant be zero
                        index_t++;
                      if(t == h || t == e || t == s) {
                         continue;
                      }// end if

                      // Increase b so long until b equals h, e, s or t.
                      for(int b = 1; b <= 9; b++) { // b = 1, weil das 1. Symbol nicht für eine 0 stehen darf
                          index_b++;
                          if (b == h || b == e || b == s || b == t) {
                              continue;
                          }// end if

                          // x = 100*h + 10*e + s
                          // y = 100*t + 10*h + e
                          // z = 1000*b + 100*e + 10*s + t
                          // Check if x+y=z, if true -> Print out Solution, else continue with the upper most loop
                          else 
                              if (100*h + 10*e + s + 100*t + 10*h + e == 1000*b + 100*e +10*s + t) {
                                  System.out.println("HES + THE = BEST => " + h + e + s + " + " + t + h + e + " = " + b + e + s + t);
                                  System.out.println("With H=" + h + ", E=" + e + ", S=" + s + ", T=" + t + ", und B=" + b + ".");
                                  System.out.println("It took " + index_h + 
                                          " Loop-Cycles to find 'h' !");
                                  System.out.println("It took " + index_e + 
                                          " Loop-Cycles to find 'e' !");
                                  System.out.println("It took " + index_s + 
                                          " Loop-Cycles to find 's' !");
                                  System.out.println("It took " + index_t + 
                                          " Loop-Cycles to find 't' !");
                                  System.out.println("It took " + index_b + 
                                          " Loop-Cycles to find 'b' !");
                                  System.out.println("This is a total of " + (index_h + index_e + index_s + index_t + index_b) + 
                                          " Loop-Cycles");
                          }// end else if
                      }//end for
                    }//end for
                 }//end for
              }//end for
        }//end for
    }   
}

总共需要大约 15000 个奇数循环来解决这个难题。这在我看来很多。请大家指点一下?

【问题讨论】:

    标签: java performance math cryptarithmetic-puzzle alphametic-question


    【解决方案1】:

    这里的大问题是:您能否(您想)从逻辑上推导出某些约束并将它们应用到您的算法中,还是您想暴力破解它?假设是前者,其中一些是非常明显的:

    • B = 1
    • T 不能为 0(因为它在 THE 中是第一个),因此 S 和 E 都不能为 0。
    • T = E + S % 10

    因此,您有 S、E、H 循环,最多为您提供 9 * 8 * 8 个组合,即 576。此外,H + T 必须大于或等于 9,您将减少此更进一步。

    更新这是一个快速而丑陋的解决方案。它仅基于上面列出的 3 个约束。

    public class Puzzle {
      public static void main(String[] args) {
        for (int S = 1; S<10; S++) {
          for (int E = 1; E<10; E++) {
            if (S==E) continue; // all letters stand for different digits
            for (int H = 1; H<10; H++) {
              if (H==E || H==S) continue; // all letters stand for different digits
              checkAndPrint(S, E, H);
            }
          } // for
        } // for
      } // main
    
      private static boolean checkAndPrint(int S, int E, int H) {
        int T = (S + E) % 10;
        int S1 = (E + H) + (S + E) / 10; // calculates value for 'S' in 'BEST' (possibly + 10)
        if (S1 % 10 != S) return false;
        int E1 = H + T + S1 / 10; // calculates value for 'E' in 'BEST' (possibly + 10)
        if (E1 % 10 != E) return false;
        System.out.println(H + "" + E + "" + S + " + " + T + "" + H + "" + E + " = 1" + E + "" + S + "" + T);
        return true;
      }
    }
    

    【讨论】:

    • 啊,现在我明白了 Brian Schroth 写了什么。我一直在试图找出他的答案。
      确实我得出了至少B = 1,T = 1和H = 1的结论(意味着不能为0),因为其中的每一个字母在每个“单词”中都是第一位的。好吧,T = E + S % 10 我没有得出结论,虽然很有趣。
      我要试试这个。谢谢各位大侠指点!
    • 如果你能在逻辑上推导出来,最简单的解决方案是在 1 次迭代中 - 将每件事设置为正确的值,因为整个解决方案都可以在逻辑上推导出来,然后就结束了!我认为,如果您要以算法方式进行操作,则需要以算法方式执行所有逻辑。所以推导出 B = 1 没问题,但是你应该包含计算它为 1 的代码行,而不是仅仅将它定义为 1。当然,这是一个自我挑战,所以你可以为所欲为!
    • Brian - 这样的谜题不一定只有一个解决方案。尤其是这个,有 6 个。虽然您可以手动找出它们,但它确实涉及实际的迭代和检查 - 您的 CPU 可能会做得更快:-)
    【解决方案2】:

    也许您可能想查看此存储库:它是使用 JavaFX 解决语言算术问题的解决方案。 Firefly Algorithm for Verbal-arithmetics problem [GitHub]

    【讨论】:

      【解决方案3】:

      不要遍历字母的所有值,而是遍历 S、E 和 T 的可能值。S + E % 10 应该是 T。一旦你有了一组可能的 S、E、T 解,找到通过可能的 E+H+(0 或 1,取决于 S+E 是否大于 9)=S 解决方案的循环......等等,等等。

      【讨论】:

        【解决方案4】:

        我不是专家,但可能值得研究管理约束的语言,例如 Prolog。这里有一个非常相似的问题:

        Compute a list of distinct odd numbers (if one exists), such that their sum is equal to a given number

        Prolog 是一种不同类型的语言,但如果您这样做是为了自己的教育,那么它肯定会锻炼您的大脑:-)

        将有可能编写字母表的通用方法 - 而不仅仅是这里相当简单的方法。

        另一种方法(不能保证给出结果)是使用优化技术,例如遗传算法。猜测一些解,并计算它们与正确解的接近程度,然后调整它们。通过这种方法,您可能会得到部分解决方案。

        【讨论】:

          【解决方案5】:

          如果标准方法是brute force 它,效率就会消失,正如这里所建议的那样。仅使用循环的最有效方法可能涉及计算详尽的可能性集,存储它们,然后遍历每一个以查看它是否有效。

          【讨论】:

          • 嗯,我想也许对这个 HES+THE=BEST 口头算术有一些不同的、更有效的控制流语句方法。因为在我看来,解决方案的 15000 和奇数循环周期很多。也许我错了,我不知道。效率稍后会出现,我只是想知道是否可能有另一种方法..
          【解决方案6】:

          嗯,你可以在你的方法中以优化的形式做很多事情。

          首先,获取“BEST”的最大值。 假设“HES”具有最高可能值 987,那么“THE”将是 X98,因此“THE”的最高值为 698,即 987+698=1685。

          如果“THE”的值最高,THE 为 987,HES 为 876 -> 876+987=1863,高于 1685,因此 1863 是“best”的上限。因此,您可以让您的程序将“B”的上限调整为 1(在这种情况下,这已经为您提供了第一个数字..)。 BEST 的下限很简单,因为它是 1023。

          然后你做这样的事情:

          for(i=102;i<=987;i++)
          {
              for(j=1023-i;j<=(1863-i < 987 ? 1863-i:987);j++)
              {
                  //check if the solution matches and doesn't have duplicate digits
              }
          }
          

          这样,您可以通过内部 for 循环中的值立即丢弃许多不可能的组合。 我敢打赌,还有类似的方法可以更多地限制可能的解决方案的空间。

          这样程序就不那么复杂了。

          【讨论】:

          • 您的解决方案为外循环提供了 885 次迭代,为内循环提供了 66 到 1827 次迭代;这绝对不是最理想的方法。此外,虽然您描述的逻辑确实简单明了,但两个“for”循环却不是;基于这种方法的“检查”功能也可能不是很简单。
          • 哦,我知道这绝不是最终解决方案,如果我要解决这个问题,我会寻找其他方法来限制迭代。另外我会使用我选择的语言的 Min() 函数,我只是添加了(授予丑陋的)内联条件,因为他的意思是他不能使用函数。好吧,我的整个帖子只是为了推动可能的解决方案。如果我只是发布一个完整的解决方案,这个谜题不会很有趣。 NoCanDo 显然是在寻求“指针”而不是最终解决方案:)
          【解决方案7】:

          这类问题是查询优化的典型代表。 Java 不适合。

          如果您的状态数少于数百亿,请暴力破解。与创建优化查询引擎相比,运行蛮力搜索所需的时间多。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2020-07-25
            • 1970-01-01
            • 2012-07-18
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多