Java 为什么该算法不符合其规范?如何尝试证明其正确性?

Java 为什么该算法不符合其规范?如何尝试证明其正确性?,java,computer-science,correctness,proof-of-correctness,Java,Computer Science,Correctness,Proof Of Correctness,编写返回字符串的方法getLargeAndSmallLetters 包含以下所有大写和小写ASCII字母的 表单(用空格分隔的大小写字母,字母 以逗号分隔的组,末尾没有逗号): A,B,C,D,E,…,Z 算法规范不包含任何关于实现细节的细节,即StringBuilder结构的使用、算法必须满足的效率指南或任何关于实现可读性的最佳实践 编译并返回所需输出的算法的我的版本 public static String getLargeAndSmallLetters() { StringBuil

编写返回字符串的方法getLargeAndSmallLetters 包含以下所有大写和小写ASCII字母的 表单(用空格分隔的大小写字母,字母 以逗号分隔的组,末尾没有逗号):

A,B,C,D,E,…,Z

算法规范不包含任何关于实现细节的细节,即
StringBuilder
结构的使用、算法必须满足的效率指南或任何关于实现可读性的最佳实践

编译并返回所需输出的算法的我的版本

public static String getLargeAndSmallLetters() {
    StringBuilder builder = new StringBuilder();
    for (int i = 64; i < 96; ++i) { // WRONG: hard coded
        if (Character.isLetter(i)) {
            String upper = Character.toString((char)i);
            String lower = Character.toString((char)(i+32));
            builder.append(upper + " " + lower + ", "); // WRONG: in loop
        }
    }
    String result = builder.toString();
    result = result.substring(0, result.length() - 2);
    return result;
 }
为什么这种算法“学术上”更正确?这也可以通过简单的字符串连接来解决

我首先想了解为什么我的解决方案不是最优的(但在IMO中是正确的)。有C#背景,只能粗略猜测我的代码不是最佳代码的原因:

  • for
    循环经过了太多的迭代。我用
    if(Character.isleter(I))
    以一种“丑陋”但正确的方式纠正了这个问题
  • 它没有那么可读
  • 它将
    int
    s转换为
    char
    s。是否存在影响性能的装箱/拆箱情况
  • 在Java中实现
    String
    的方式是,我是不是通过在
    for
    循环中使用字符串连接和
    +
    操作符在字符串池中创建了更不可变的
    String
    s?使用
    StringBuilder
    会更有效吗?因为它在内部更有效地重用
    ”、“
    字符串”
    s
现在我还想做的是提供一个“学术论证”,证明我的答案的正确性。以下是一些想法:

  • 编译器优化了我的“错误”,并为“错误”和“正确”答案生成了相同的字节码
  • JIT编译器对“正确”和“错误”答案实现执行相同的机器代码
  • 提供算法的数学模型
  • 选项3似乎是证明我的答案正确性的最直接的方法

    对于选项1和2,我认为需要将For循环更正为
    For(inti=65;I<91;++I)
    ,并删除
    if(Character.isleter(I))
    语句

    如果您对我的想法有任何建议、补充或更正,我们将不胜感激

    编译器优化了我的“错误”,并为“错误”和“正确”答案生成了相同的字节码

    不,编译器通过优化做的很少。它可能会将一些字符串连接更改为
    StringBuilder
    附录
    ,但仅此而已;否则,它只是忠实地复制您在代码中以字节码形式编写的内容。基本上所有的智能都发生在JIT中

    您可以很容易地看到它们不会产生相同的字节码:使用
    javap-c
    对它们进行反编译

    JIT编译器对“正确”和“错误”答案实现执行相同的机器代码

    JIT最终会执行代码。对于这样的东西,它可能只执行一次,因此不值得JIT'ing

    但我强烈怀疑JIT是否会为这两种方法生成相同的代码,因为这两种方法完全不同

    提供算法正确性的数学证明

    在Java中,形式证明很难。有很多微妙的语义,你必须考虑到在证明;或者,至少,证明这些语义不参与代码的执行

    你真正能做的就是断言它在功能上是等价的。例如,不是证明它是正确的,而是证明它不是错误的

    为此编写测试非常容易,因为您知道正确的输出:

    A a, B b, C c, ...
    
    所以只要检查你的代码是否产生了这些,你就完成了。当然,也可以检查代码是否生成与模型答案相同的输出


    你的答案实际上与模型答案并不完全相同。你把
    从末尾砍掉;模型答案并非如此。

    您正在打一场失败的战斗。如果你的“指导者”懒得去检查你的代码是否产生了正确的输出,他就懒得去读你算法正确性的证明。如果你从中学到了一件事,那就是你经常要忍受愚人的折磨,所以要做好这件事

    这就是说,你的代码的正确性可以通过以下方式得到证明:(a)它不接受任何输入(b)它不受副作用的影响(更现实地说,不是那些不影响你的讲解员的解决方案的副作用),然后通过显示(c)你的算法的实际执行跟踪得到正确的输出得出结论(正确的是,它产生的内容与您的讲师的内容相同)


    有鉴于此,没有充分的理由选择一种实现而不是另一种。如果您更喜欢性能、简洁或优雅的模型答案,那么有一种比您或您的讲师发现的更好、更简单的解决方案:

    public static String getLargeAndSmallLetters() {
        return "A a, B b, C c, D d, E e, F f, G g, H h, I i, J j, K k, L l, M m, N n, O o, P p, Q q, R r, S s, T t, U u, V v, W w, X x, Y y, Z z";
    }
    
    您可以使其更具可读性,如下所示:

    public static String getLargeAndSmallLetters() {
        return String.format("%s%s",
            "A a, B b, C c, D d, E e, F f, G g, H h, I i, J j, K k, L l, M m, ",
            "N n, O o, P p, Q q, R r, S s, T t, U u, V v, W w, X x, Y y, Z z");
    }
    

    注意:
    for
    -在
    char
    s中循环是很糟糕的,你不应该这样做。我不能说我有同事在他的代码中尝试过,但如果那天到来,我会感谢同行的代码审查。

    对不起,这个网站是关于代码的。我阅读所有这些并不是为了告诉你你是对还是错。请与你联系r professor.@duffymo这是一个关于算法的话题,但在我看来仍然有太多的文本。可能相关:
    sb.append(“x”+y”);
    要执行
    “x”+y
    Java需要生成cod
    public static String getLargeAndSmallLetters() {
        return String.format("%s%s",
            "A a, B b, C c, D d, E e, F f, G g, H h, I i, J j, K k, L l, M m, ",
            "N n, O o, P p, Q q, R r, S s, T t, U u, V v, W w, X x, Y y, Z z");
    }