Java 为什么该算法不符合其规范?如何尝试证明其正确性?
编写返回字符串的方法getLargeAndSmallLetters 包含以下所有大写和小写ASCII字母的 表单(用空格分隔的大小写字母,字母 以逗号分隔的组,末尾没有逗号): A,B,C,D,E,…,Z 算法规范不包含任何关于实现细节的细节,即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
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))
- 它没有那么可读
- 它将
s转换为int
s。是否存在影响性能的装箱/拆箱情况李>char
- 在Java中实现
的方式是,我是不是通过在String
循环中使用字符串连接和for
操作符在字符串池中创建了更不可变的+
s?使用String
会更有效吗?因为它在内部更有效地重用StringBuilder
和“
”、“
s字符串”
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");
}