Project Euler 185

Number MindをMCMCで解いてみた。
方針は与えられた数sに対して、評価関数c(s)を真の正解の数との二乗誤差とおく。今状態sから状態tへの遷移確率を
 p(s->t) = \min (1.0 , \exp (-\beta (c(t) - c(s)))
とおいて遷移していく、これを繰り返していくと
 p(s) = \frac{\exp (-\beta c(s))}{\sum_t \exp (-\beta c(t))}
に従ってsをサンプリングすることができ、c(s)が低いものほど頻繁に出やすくなるため、そのうち求める解であるc(s)=0となるsが求められる。

import java.util.*;

public class P185 {
  static final String digits[] = { "5616185650518293", "3847439647293047",
      "5855462940810587", "9742855507068353", "4296849643607543",
      "3174248439465858", "4513559094146117", "7890971548908067",
      "8157356344118483", "2615250744386899", "8690095851526254",
      "6375711915077050", "6913859173121360", "6442889055042768",
      "2321386104303845", "2326509471271448", "5251583379644322",
      "1748270476758276", "4895722652190306", "3041631117224635",
      "1841236454324589", "2659862637316867", };
  static final int correct[] = { 2, 1, 3, 3, 3, 1, 2, 3, 1, 2, 3, 1, 1, 2, 0,
      2, 2, 3, 1, 3, 3, 2 };
  static int cost(int seq[]){
    int cs[] = new int[correct.length];
    for(int i = 0 ; i < correct.length ; i++){
      for(int j = 0 ; j < seq.length ; j++){
        if(seq[j] == digits[i].charAt(j) - '0'){
          cs[i]++;
        }
      }
    }
    int ret = 0;
    for(int i = 0 ; i < cs.length ; i++)
      ret += (cs[i] - correct[i]) * (cs[i] - correct[i]);
    return ret;
  }
  public static void main(String[] args) {
    Random rand = new Random();
    int seq[] = new int[16];
    for(int i = 0 ; i < 16 ; i++)
      seq[i] = rand.nextInt(10);
    int c = cost(seq);
    while(c > 0){
      int ni = rand.nextInt(seq.length);
      int nv = rand.nextInt(10);
      int cv = seq[ni];
      seq[ni] = nv;
      int nc = cost(seq);
      double accept = Math.min(1.0 , Math.exp(- 2.0 * (nc - c)));
      if(rand.nextDouble() < accept){
        c = nc;
      }else{
        seq[ni] = cv;
      }
    }
    System.out.println(Arrays.toString(seq));
  }
}