エンジニアの競馬情報収集ブログ

最近は5割が競馬の話、3割がLoL/LJLの話、2割が技術的な話な感じ。たまにその他の話。

AOJ 2165: Strange String Manipulation

問題

問題文日本語訳

線形合同法擬似乱数を発生させる手法で、擬似乱数Rを以下のように発生させる

R(0) = S
R(i) = ((A・R(i-1) + C) mod M  (for i >= i)

S, A, C, M はパラメータであり、この問題においては、0 <= S, A, C <= 15, m = 256 とする。

入力I (Iは0以上M-1以下の整数) に対して、Rを利用して以下の式でOを得る

O(i) = (I(i) + R(i)) mod M (for i >= 1)

あなたの仕事は、S, A, C を求めるプログラムを書くことである。 O(i)のエントロピーを最小化するようなS, A, Cを求めよ。 エントロピーHは以下の式で求められる(式略)

 入力

入力は複数のデータセットからなる。それぞれのデータセットは以下のように与えられる

N
I(1) I(2) ... I(N)

Nは256以下で、それぞれのIは空白で区切られる。

出力

それぞれのデータセットに対して、S, A, C を空白区切りで1行で出力せよ。

  • 入出力や式については問題原文を参照してください。

解放

S, A, C を 0から15まで変化させてみてすべて試すだけである。 O(S * A * C * N) 程度になるが、数字が小さいので余裕で間に合う

ただし、エントロピーの大小を比較する時、Doubleの誤差を考慮しないと Wrong Answer になるようなので注意が必要。

ソースコード

int main(int argc, const char * argv[]){
    while(true){
        int n, I[256], m = 256;
        
        cin >> n;
        if(!n) break;
        
        REP(i,n) cin >> I[i];
        
        int as = 0, aa = 0, ac = 0;
        double ah = 10000.0;
        REP(s,16) REP(a,16) REP(c,16){
            int cnt[256];
            REP(i, 256) cnt[i] = 0;
            
            int r = s;
            REP(i,n){
                r = (a * r + c) % m;
                int o = (I[i] + r) % m;
                cnt[o]++;
            }
            
            double h = 0.0;
            REP(i,m){
                if(cnt[i]){
                    h -= (double)cnt[i] / n * log((double)cnt[i] / (double)n);
                }
            }
            
            if(h + ESP< ah){
                ah = h;
                as = s;
                aa = a;
                ac = c;
            }
        }
        cout << as << " " <<  aa << " " << ac << endl;
    }
}
  • if(h + ESP< ah) と EPS の誤差を考慮しなければいけない