爪哇
我的MM(5,8)算法得分为177902 178006 182798 182697,最大深度为8 9,并且只需要几秒钟(在我的慢速计算机上)。
该算法找到的分数为21的MM(2,3)策略的示例输出如下所示:
{BC:{00:AA,01:AB:{01:CA},02:CB,10:AC:{00:BB,01:BA,10:CC}}}
我的算法没有什么令人兴奋的。没有发明。我只是遵循网上找到的食谱,并将它们压缩为Java代码。我所做的唯一优化就是尝试(以某种方式)优化代码行。它是这样的:
- 将所有可能代码的初始集合S0创建为当前集合S。
- 密码破译者找到了一个对S的(贪婪的)良好猜测。每个猜测都导致一个S的分区P,其中每个子集S'收集(从S)具有相同猜测答案的所有代码。一个好的猜测具有良好的分区,因为该分区提供了最多的猜测信息。
- 进行良好的猜测及其P。对P中的每个非空S'应用代码破解程序递归(步骤2)。
@MrBackend:我想写验证程序很困难。;-)
import java.util.TreeMap;
import java.util.Vector;
public class MM {
Vector<String> codeset = new Vector<String>();
String guess;
TreeMap<Integer, MM> strategy = new TreeMap<Integer, MM>();
public String toString() {
String list="";
for (Integer reply: strategy.keySet()) {
if (strategy.get(reply)!=null) list+=(list.length()>0?",":"")+(reply<10?"0":"")+reply+":"+strategy.get(reply);
}
if (list.length()>0) return guess+":{"+list+"}"; else return guess;
}
MM() { }
MM(int h, int c) {
for (int i = 0; i < Math.pow(c, h); i++) {
String code = "";
for (int j = 0, p=i; j < h; j++) {
code+="ABCDEFGH".charAt(p%c);
p/=c;
}
codeset.add(code);
}
}
int replyAccordingToDonaldKnuth(String secret, String guess) {
int black=0;
int totalHitsBlackAndWhite=0;
for (char n = 'A'; n <= 'H'; n++) {
int a=0, b=0;
for (int i = 0; i < secret.length(); i++) {
if (secret.charAt(i)==n) a++;
if ( guess.charAt(i)==n) b++;
}
totalHitsBlackAndWhite+=Math.min(a, b);
}
for (int i = 0; i < secret.length(); i++) {
if (secret.charAt(i) == guess.charAt(i)) black++;
}
return 10 * black + (totalHitsBlackAndWhite-black);
}
int reply(String secret, String guess) {
return replyAccordingToDonaldKnuth(secret, guess);
}
MM codebreaker(Vector<String> permuts) {
int fitness=0;
MM protostrategy=null;
for (int greedy = 0; greedy < Math.min(permuts.size(), 200); greedy++) {
MM tmp=partition(permuts, permuts.get(greedy));
int value=tmp.strategy.size();
if (fitness<=value) {
fitness=value;
protostrategy=tmp;
protostrategy.guess=permuts.get(greedy);
}
}
if (protostrategy!=null) {
for (Integer reply: protostrategy.strategy.keySet()) {
protostrategy.strategy.put(reply, codebreaker(protostrategy.strategy.get(reply).codeset));
}
}
return protostrategy;
}
MM partition(Vector<String> permuts, String code) {
MM protostrategy=new MM();
for (int c = 0; c < permuts.size(); c++) {
int reply=reply(permuts.get(c), code);
if (!protostrategy.strategy.containsKey(reply)) protostrategy.strategy.put(reply, new MM());
if (permuts.get(c)!=code) protostrategy.strategy.get(reply).codeset.add(permuts.get(c));
}
return protostrategy;
}
public static void main(String[] args) {
MM mm = new MM(5,8);
System.out.println("{"+mm.codebreaker(mm.codeset)+"}");
}
}
一些说明:
- 不需要一致性检查,因为集合S及其分区以(自动)一致的方式构建。
- 从S0(而不是S)中选择一个好的猜测是有道理的。但是我在当前代码中不遵循这种方法。
- 我贪婪的搜索被人为地修剪了200次。
- 我知道,“提供大多数信息以供猜测”不是很精确。简单的想法是选择子集数量最多的分区。
- 结果在很大程度上取决于您如何计算reply(..)。最后,我适应了唐纳德·克努斯(Donald Knuth)的表情。
的策略MM(5,8)
可以在这里找到。GitHub在显示行太长时存在一些问题,因此请单击Raw按钮。