2012-03-18 18 views
16

.NET Framework 1.1から.NET Framework 4へのデコードに使用されるメソッドを移行しています。 Randomの実装が変更されていることに気付きました。だから、同じ種があれば、Random.NextBytesは別の結果を返します。.NETのRandom()への実装変更

したがって、次のコードを実行するとします。 .NET Framework 1.1の下で

byte[] bytes = new byte[4]; 
System.Random random = new System.Random(50); 
random.NextBytes(bytes); 

for(int i=0; i< bytes.Length; i++) 
{ 
    Console.WriteLine("bytes[" + i + "] = " + bytes[i]); 
} 

それが返されます。

bytes[0] = 216 
bytes[1] = 124 
bytes[2] = 183 
bytes[3] = 58 

を.NETフレームワーク4の下にそれが返されます。

bytes[0] = 154 
bytes[1] = 49 
bytes[2] = 183 
bytes[3] = 48 

この問題を解決する最良の方法は何ですか?

+10

問題はなんですか?あなたのプログラムは特定の乱数生成に依存していますか? – Stilgar

+1

私はStilgarと一緒です。ランダムは、擬似ランダム結果を生成することを意味します。特定のバイトが必要な場合は、そのバイトをコードに配置しないのはなぜですか? @ Moo-juiceが、後のフレームワークにアップグレードする場合、アップグレード後に同じ実装を使用しています...? – sheikhjabootie

+0

@ムージュース。クライアントの接続を許可する前に、バージョンが互換性があることを確認してください。ゲームは何十年もの間、この種のバージョン管理を行ってきました。 – Stilgar

答えて

17

Reflectorを使用すると、1.1 mscorlibからRandomクラスをコピーできます。

public class Random1_1 
{ 
    // Fields 
    private int inext; 
    private int inextp; 
    private const int MBIG = 0x7fffffff; 
    private const int MSEED = 0x9a4ec86; 
    private const int MZ = 0x0; 
    private int[] SeedArray; 

    // Methods 
    public Random1_1() 
     : this(Environment.TickCount) 
    { 
    } 

    public Random1_1(int Seed) 
    { 
     this.SeedArray = new int[0x38]; 
     int num2 = 0x9a4ec86 - Math.Abs(Seed); 
     this.SeedArray[0x37] = num2; 
     int num3 = 0x1; 
     for (int i = 0x1; i < 0x37; i++) 
     { 
      int index = (0x15 * i) % 0x37; 
      this.SeedArray[index] = num3; 
      num3 = num2 - num3; 
      if (num3 < 0x0) 
      { 
       num3 += 0x7fffffff; 
      } 
      num2 = this.SeedArray[index]; 
     } 
     for (int j = 0x1; j < 0x5; j++) 
     { 
      for (int k = 0x1; k < 0x38; k++) 
      { 
       this.SeedArray[k] -= this.SeedArray[0x1 + ((k + 0x1e) % 0x37)]; 
       if (this.SeedArray[k] < 0x0) 
       { 
        this.SeedArray[k] += 0x7fffffff; 
       } 
      } 
     } 
     this.inext = 0x0; 
     this.inextp = 0x15; 
     Seed = 0x1; 
    } 

    public virtual int Next() 
    { 
     return (int)(this.Sample() * 2147483647.0); 
    } 

    public virtual int Next(int maxValue) 
    { 
     if (maxValue < 0x0) 
     { 
      throw new ArgumentOutOfRangeException("maxValue"); 
     } 
     return (int)(this.Sample() * maxValue); 
    } 

    public virtual int Next(int minValue, int maxValue) 
    { 
     if (minValue > maxValue) 
     { 
      throw new ArgumentOutOfRangeException("minValue"); 
     } 
     int num = maxValue - minValue; 
     if (num < 0x0) 
     { 
      long num2 = maxValue - minValue; 
      return (((int)((long)(this.Sample() * num2))) + minValue); 
     } 
     return (((int)(this.Sample() * num)) + minValue); 
    } 

    public virtual void NextBytes(byte[] buffer) 
    { 
     if (buffer == null) 
     { 
      throw new ArgumentNullException("buffer"); 
     } 
     for (int i = 0x0; i < buffer.Length; i++) 
     { 
      buffer[i] = (byte)(this.Sample() * 256.0); 
     } 
    } 

    public virtual double NextDouble() 
    { 
     return this.Sample(); 
    } 

    protected virtual double Sample() 
    { 
     int inext = this.inext; 
     int inextp = this.inextp; 
     if (++inext >= 0x38) 
     { 
      inext = 0x1; 
     } 
     if (++inextp >= 0x38) 
     { 
      inextp = 0x1; 
     } 
     int num = this.SeedArray[inext] - this.SeedArray[inextp]; 
     if (num < 0x0) 
     { 
      num += 0x7fffffff; 
     } 
     this.SeedArray[inext] = num; 
     this.inext = inext; 
     this.inextp = inextp; 
     return (num * 4.6566128752457969E-10); 
    } 
} 

テストされ、出力が得られます。

+0

ちなみに、あなたがそれらを反映している場合、2つの違いは何ですか? –

+0

主なものは、大きなサンプルのサポートが追加されたようです。ここでは、.net 4.0のランダムなクラスです。 http://pastebin.com/stFuTzCk。 – Will

+11

法的に疑わしい。逆コンパイルや他の人のコードの使用はできません。 – CodesInChaos

27

これはRandomの問題ではなく、ドキュメント化されたインターフェイスを完璧に満たしています。これは、実装の詳細に依存するソフトウェアの問題です。この間違いから学び、はもう一度やり直さないでください

問題を修正する限り、1.1の擬似乱数生成を実装してデコードし、次に不安定な動作に依存しない新しいエンコード/デコードアルゴリズムを実装することができます(RandomまたはGetHashCode)を新しいバージョンのソフトウェアに適用します。

+2

これは非常に不公平です。私が覚えているので、使用されたシードを保存することは、ランダムを使用する信頼できる方法でした。 –

+11

それはまったく不公正ではありません。アルゴリズムは文書化されていないため、実装の詳細です。これは実際に適切なAPIを使用することに関する基本的な概念です。実装の詳細に頼らないでください。不公平なのは、提供されているAPIを使用していて、公開された契約の代わりに愚かな実装の詳細に依存しているため、そのAPIを優れたものに変更しないように強制していることです。 –

+4

@RussC本当ですか?ドキュメントから:「Randomクラスの乱数ジェネレータの実装は、メジャーバージョンの.NET Frameworkで同じであることが保証されていません。結果として、アプリケーションコードでは、同じシードが異なるバージョンの.NET Frameworkで同じ擬似乱数シーケンスになることを想定するべきではありません。 " – Stilgar

3

.NET 1.1バージョンのRandomに完全に頼っているのであれば、私が考えることができるのは、1.1をターゲットとする新しいアセンブリを作成し、アップグレードした.NET 4アプリケーションから呼び出すことだけです。

しかし、なぜあなたはこの種子を維持するために非常に重要であるかを詳しく説明できますか?より良い方法があるかもしれません。

+1

私はハイの答えはおそらくここに行く方法だと思います。 –

+0

それは1.1のアセンブリはまだ高いCLIで実行されるでしょうか? –

+0

1.1フレームワークがインストールされている場合、いいえ。 –

0

また、System.Security.Cryptography.RandomNumberGeneratorクラスを使用して暗号的に強力なランダムなバイト配列を生成することをお勧めしますか?

RandomNumberGenerator rng = RandomNumberGenerator.Create(); 
byte[] bytes = new byte[128]; 
rng.GetBytes(bytes); 

私は、コメントの残りの部分に参加し、文書化されていない実装に依存すること悪いことを言及します。さらに、実際に予測可能な「ランダム性」に頼っている場合 - これを「安全」でなければならないものに使用しているなら、それは全く間違っています。

+0

"文書化されていない実装に依存するのは悪い"。ばかじゃないの?すべての標準.NETクラスのすべてのメソッドとプロパティには、ドキュメント化されていない実装があります。重要なのは結果です - バージョン間で*変更してはいけません。 – adelphus

2

ここでは答えはありませんが、ここでは多くの人々に反して、私はばかげた行動を文書化するだけで十分だとは思いません。

なぜ、最初にシーディングメカニズムを提供しますか?さて、私はあなたに言います:おそらく何百万もの乱数を保持するのではなく、単一のシードからランダムシーケンスをいつでも再現できるようにします。私は「常に」と言っていましたが、「次のバージョンの.NETにアップグレードするまで」というわけではありません。バージョン間で一貫しないことにより、現在の.NET乱数ジェネレータはこの機能を提供しません。マイクロソフトでは、欠陥のある動作を記録するのではなく、これを実装する(または実装していない)方が良いはずです。

そして、実際にはアルゴリズムは実装の詳細ですが、メソッド呼び出しの結果を実装の詳細と呼ぶことができますか?次のバージョンでは、2つの文字列を連結したり、平方根を計算したりすることとは異なる結果を得る危険性がないことを確かめるために、.NET Frameworkのすべてのメソッドのドキュメントを実際に調べる必要がありますか?

私の意見では、私たちがここに持っているものは、ひどく実装された乱数ジェネレータです。もちろん、新しい実装に基づいて新しい機能に別の名前を付けることで、問題全体を簡単に回避することができました。

+1

"なぜ、最初にシードメカニズムを提供しますか?"これは擬似乱数生成器の働きですそれらは実際にランダムではありませんが、シードに基づいて一連の数字を生成します。種子なし、擬似乱数なし。それが理由です。 –

関連する問題