web-dev-qa-db-ja.com

ランダム性をテストするにはどうすればよいですか?

配列の要素をランダムにシャッフルする方法を検討してください。これが機能していることを確認するために、シンプルで堅牢な単体テストをどのように記述しますか?

私は2つのアイデアを思いつきましたが、どちらにも顕著な欠陥があります。

  • 配列をシャッフルし、その順序が以前とは異なることを確認します。これは良さそうに聞こえますが、シャッフルが同じ順序でシャッフルした場合は失敗します。 (可能性は低いですが、可能です。)
  • 一定のシードで配列をシャッフルし、所定の出力と照合します。これは、ランダム関数が、同じシードが与えられると常に同じ値を返すことに依存しています。ただし、これは時々無効な仮定 です。

サイコロの転がりをシミュレートし、乱数を返す2番目の関数について考えます。この機能をどのようにテストしますか?その関数をどのようにテストしますか...

  • 与えられた範囲外の数値を返すことはありませんか?
  • 有効な分布で数値を返しますか? (1つのダイでは均一、多数のダイでは通常。)

これらの例だけでなく、コードのランダムな要素一般のテストに関する洞察を提供する答えを探しています。単体テストはここでも適切なソリューションですか?そうでない場合、どのようなテストですか?


みんなの心を和らげるためだけに、私は自分の乱数ジェネレータを書いているではありません

135
dlras2

単体テストはランダム性をテストするための適切なツールではないと思います。単体テストでは、メソッドを呼び出し、戻り値(またはオブジェクトの状態)を期待値と比較してテストする必要があります。ランダム性のテストの問題は、テストしたいほとんどの項目に期待値がないことです。与えられたシードでテストできますが、テストできるのは繰り返し性だけです。分布がどれほどランダムであるかどれほどランダムであるかを測定する方法も、まったくランダムであるかもわかりません。

幸いにも、 ランダム性のテストのDiehardバッテリー など、実行できる統計テストはたくさんあります。以下も参照してください。

  1. 疑似乱数ジェネレータを単体テストする方法は?

    • Steve Jessop は、使用しているものと同じRNGアルゴリズムのテスト済み実装を見つけ、その出力を選択したシードと独自の実装と比較することをお勧めします。
    • Greg Hewgill[〜#〜] ent [〜#〜] 一連の統計的検定を推奨します。
    • John D.Cook は読者にCodeProjectの記事を紹介します Simple Random Number Generation 。これには、Donald Knuthの第2巻、Seminumerical Algorithmsで言及されたKolmogorov-Smirnovテストの実装が含まれます。
    • 生成された数値の分布が均一であることの検定、カイ2乗検定、および平均と標準偏差が期待される範囲内であることの検定を推奨する人もいます。 (分布をテストするだけでは十分ではないことに注意してください。[1,2,3,4,5,6,7,8]は一様分布ですが、ランダムではありません。)
  2. ランダムな結果を返す関数を使用した単体テスト

    • Brian Genisio は、RNGのモックがテストを再現可能にするための1つのオプションであることを指摘し、C#サンプルコードを提供します。
    • 繰り返しになりますが、再現性を高めるために固定シード値を使用したり、均一分布やカイ2乗などの簡単なテストを行ったりする人もいます。
  3. nit Testing Randomness は、その性質上、再現性がないものをテストするときにすでに触れられている多くの課題について述べているWiki記事です。私がそれから収集した興味深いビットの1つは次のとおりです。

    以前に、値のファイルのランダム性を測定するツールとしてwinzipが使用されているのを見てきました(明らかに、ファイルを圧縮できるサイズが小さいほど、ランダムさが少なくなります)。

104
Bill the Lizard

1.アルゴリズムのユニットテスト

最初の質問では、アルゴリズムの結果がわかっている一連の乱数をフィードする偽のクラスを作成します。そうすることで、ランダム関数のを構築するアルゴリズムが機能することを確認できます。したがって、次のようなものがあります。

Random r = new RandomStub([1,3,5,3,1,2]);
r.random(); //returns 1
r.random(); //returns 3
...

2.ランダム関数に意味があるかどうかを確認します

単体テストには、複数回実行して結果を主張するテストを追加する必要があります

  • あなたが設定した境界内にあるため(ダイスの目は1から6の間)、そして
  • 賢明な分布を表示します(複数のテストを実行し、分布が期待したもののx%以内であるかどうかを確認します。たとえば、サイコロの場合は2 1,000回ロールした場合、時間の10%から20%(1/6 = 16.67%)の間で上昇します)。

3.アルゴリズムとランダム関数の統合テスト

配列が元のソートでソートされると予想される頻度はどれくらいですか?数百回ソートして、ソートが変化しないのは時間のx%だけであると断言します。

これは実際にはすでに統合テストです。ランダム関数とともにアルゴリズムをテストしています。本当のランダム関数を使用すると、単一のテストを実行することはできなくなります。

経験(私は遺伝的アルゴリズムを書いた)から、私はあなたのアルゴリズムの単体テスト、あなたのランダム関数の分布テスト、および統合テストを組み合わせることは行く方法と言うでしょう。

21
sebastiangeiger

忘れられているように思われるPRNGの側面は、そのプロパティのすべてが本質的に統計的であることです。配列をシャッフルすると、元の配列とは異なる順列になるとは期待できません。基本的に、通常のPRNGを使用している場合、保証される唯一のことは、単純なパターンを使用しないこと(願わくば)であり、返される数値のセットに均等に分布することです。

PRNGの適切なテストでは、少なくとも100回実行してから、出力の分布を確認します(これは質問の2番目の部分に対する直接の回答です)。

最初の質問への答えはほとんど同じです。{1、2、...、n}でテストを約100回実行し、各要素が各位置にあった回数をカウントします。シャッフル方法が良い場合、それらはすべてほぼ等しいはずです。

まったく異なる問題は、暗号化グレードのPRNGをテストする方法です。これは、自分が何をしているのか本当に理解していない限り、おそらく留まるべきではない問題です。人々は destroy (読む:壊滅的な穴を開く)ほんの少しの「最適化」またはささいな編集で良い暗号システムを知っています。

編集:私は質問、トップアンサー、そして私自身を徹底的に読み直しました。私が指摘する点はまだ残っていますが、私はビルザリザードの答えを2番目にします。単体テストは本質的にブール値です-失敗するか成功するため、PRNG(またはPRNGを使用するメソッド)のプロパティは「どれほど良い」かというテストには適していません。この質問に対する答えは、極性ではなく量的であるためです。

15
K.Steff

何度も実行して、データを視覚化します

Coding Horror からのシャッフルの例を次に示します。アルゴリズムに問題がないかどうかを確認できます。

enter image description here

すべての可能なアイテムが少なくとも1回(境界はOK)返され、配布はOKであることが簡単にわかります。

7
Carra

これには2つの部分があります。ランダム化のテストとランダム化を使用するもののテストです。

ランダム化のテストは比較的簡単です。乱数ジェネレーターの周期が予想どおりであること(いくつかのランダムシードを使用するいくつかのサンプルの場合、しきい値内)、および大きなサンプルサイズでの出力の分布が予想どおりであることを確認します。 (あるしきい値内で).

ランダム化を使用するもののテストは、決定論的な疑似乱数ジェネレータを使用して行うのが最適です。ランダム化の出力はシード(その入力)に基づいて既知であるため、入力と期待される出力に基づいて通常どおりユニットテストを実行できます。 RNGがnot確定的である場合、確定的(または単にランダムではない)なものでそれを模擬します。ランダム化を使用するコードとは別にテストします。

6
Telastyn

ランダム化された入力を取るコードを処理するときに役立つとわかった一般的なポインタ:予想されるランダム性(最大値と最小値、および該当する場合は最大値+1と最小値-1)のEdgeケースを確認します。数値に変曲点がある場所(上、上、下)を確認します(つまり、-1、0、1、または1より大きく、1未満で、小数値が関数を台無しにする可能性がある場合は負ではありません)。許可された入力の完全に外側のいくつかの場所を確認してください。いくつかの典型的なケースを確認してください。ランダム入力を追加することもできますが、ユニットテストの場合、テストが実行されるたびに同じ値がテストされないという望ましくない副作用があります(シードアプローチは機能しますが、シードから最初の1,000個の乱数をテストします) Sなど)。

ランダム関数の出力をテストするには、目標を特定することが重要です。カードの場合、目標は0-1ランダムジェネレーターの均一性をテストして、52枚すべてのカードが結果に表示されるかどうかを決定することですか、それとも他の目標(このリストのすべてなど)ですか?

特定の例では、乱数ジェネレータが不透明であると想定する必要があります(OSを記述しない限り、OS syscallまたはmallocの単体テストを行うのは意味がないのと同じように)。乱数ジェネレーターを測定することは役立つかもしれませんが、目標は乱数ジェネレーターを作成することではなく、毎回52枚のカードを取得し、カードの順番が変わることを確認することです。

これは、実際には2つのテストタスクがあるという長い言い方です。RNGが適切なディストリビューションを生成していることのテストと、カードシャッフルコードがそのRNGを使用してランダムな結果を生成していることを確認します。 RNGを作成している場合は、統計分析を使用して分布を証明します。カードシャッフラーを作成している場合は、各出力に繰り返されていないカードが52枚あることを確認します(使用している検査によるテストの方が良いケースです) RNG)。

4
anon

安全な乱数ジェネレータを使用できます

あなたはあなた自身の乱数ジェネレータを書いていないのですか?

あなたがそうではないと仮定した場合、他の人のコード(フレームワークのSecureRandom実装など)ではなく、担当するコードをテストするを実行する必要があります。

コードのテスト

コードが正しく応答するかどうかをテストするには、通常、低可視性メソッドを使用して乱数を生成し、ユニットテストクラスで簡単にオーバーライドできるようにします。このオーバーライドされたメソッドは、乱数ジェネレーターを効果的にモックアウトし、何をいつ生成するかを完全に制御できるようにします。その結果、単体テストの目的であるコードを完全に実行できます。

明らかに、エッジ条件をチェックし、適切な入力を与えられたアルゴリズムが要求するとおりにシャッフルが行われることを確認します。

安全な乱数ジェネレータのテスト

言語の安全な乱数ジェネレータが本当にランダムではないかバグがある(範囲外の値が提供されるなど)かどうかが不明な場合は、数億回の反復で出力の詳細な統計分析を実行する必要があります。各数値の出現頻度をプロットすると、等しい確率で表示されます。結果が何らかの形で歪んでいる場合は、結果をフレームワーク設計者に報告する必要があります。安全な乱数ジェネレータは多くの暗号化アルゴリズムの基本であるため、彼らは間違いなく問題の修正に関心を持っています。

4
Gary Rowe

まあ、100%確実になることは決してないので、あなたができる最善のことは、数字がランダムである可能性が高いことです。確率を選びます-数百万のサンプルが与えられた場合、数値またはアイテムのサンプルがx倍になり、誤差の範囲内になると言います。 100万回実行し、マージン内にあるかどうかを確認します。幸いなことに、コンピューターはこの種のことを簡単に行うことができます。

1
Matthew Flynn

乱数のソースが少なくともランダムに見えるものを生成していることをテストするには、テストでかなり大きなバイトのシーケンスを生成し、それらを一時ファイルに書き込んでから、シェルに出力します Fourmilab's ent ツール。 entに-t(簡潔)スイッチを指定すると、解析しやすいCSVが生成されます。次に、さまざまな数値をチェックして、それらが「良好」であることを確認します。

どの数値が良いかを判断するには、 既知のランダム性のソースを使用 テストを調整します。乱数の適切なセットが与えられた場合、テストはほとんど常に合格するはずです。真にランダムなシーケンスであっても、ランダムではないように見えるシーケンスが生成される可能性があるため、確実に合格するテストを取得することはできません。ランダムなシーケンスによってテストが失敗する可能性を低くするしきい値を選択するだけです。ランダムさは楽しいじゃないですか?

注:PRNGが「ランダムな」シーケンスを生成することを示すテストを作成することはできません。合格した場合、=によって生成されたシーケンスが何らかの確率を示すテストのみを作成できます。 PRNGは「ランダム」です。ランダム性の喜びへようこそ!

1
Wayne Conrad

ケース1:シャッフルのテスト:

配列[0、1、2、3、4、5]を検討し、それをシャッフルします。何が問題になるのでしょうか?通常のもの:a)まったくシャッフルしない、b)1から5までシャッフルして0ではなく、0から4までシャッフルして5ではなく、常に同じパターンを生成する、...

それらすべてをキャッチする1つのテスト:

100回シャッフルし、各スロットに値を追加します。各スロットの合計は、他の各スロットと同様でなければなりません。平均/標準偏差を計算できます。 (5 + 0)/2=2.5、100*2.5 =25。たとえば、期待値は約25です。

値が範囲外の場合は、誤検知が発生する可能性が少しあります。その可能性がどれほど大きいかを計算できます。テストを繰り返します。まあ-もちろん、テストが連続して2回失敗する可能性はわずかです。しかし、あなたはあなたのソースを自動的に削除するルーチンを持っていません、ユニットテストが失敗した場合、あなたはどうですか?もう一度実行してください!

それは3回続けて失敗する可能性がありますか?宝くじで運を試してみるべきかもしれません。

ケース2:サイコロを振る

ダイスロールの質問は同じ質問です。サイコロを6000回投げます。

for (i in 0 to 6000) 
    ++slot [Random.nextInt (6)];
return (slot.max - slot.min) < threshold;
1
user unknown