マイクロコントローラーで乱数を効率的に生成するにはどうすればよいですか?一般的なガイドラインや特定の高速な方法はありますか?
8 に答える
通常、実際の乱数ではなく疑似乱数を生成しますが、どちらもさまざまなレートで可能です。
シーケンスが暗号化の目的で使用されるかどうかに応じて、2つの一般的なカテゴリがあります。主な違いは、シーケンス内の1つの数値の知識が、次の数値の予測を可能にするかどうかです。汎用RNGは、アルゴリズムの知識によってオブザーバーがシーケンスを複製できるかどうかを心配する必要はなく、実行速度はかなり速くなります。
典型的な汎用RNGアルゴリズムは、メルセンヌツイスターです。さまざまなアルゴリズムの多くの公開実装があります。こちらをご覧ください。
MTが必要とするメモリが多すぎる場合、中途半端なフォールバックは線形合同法です。(MTは1997年まで発明されませんでした。)このジェネレーターには特定の問題がありますが、メモリやコードはほとんど必要なく、非常に高速です。実装はいたるところにあり、それはクヌースの半数値アルゴリズムで詳細に説明されています。
RNGをシードするには、エントロピーのソースが必要です。http://en.wikipedia.org/wiki/Entropy_(computing)を参照してください(注:SOは、そのリンクの()について混乱します)。これは通常、キーストローク、(私はあなたにとってはうまくいかないと思います)割り込み、パケットの到着など、CPUが監視できるタイミングイベントによって導き出されます。リアルタイムクロックは、それ自体の状態を維持している場合、多くの場合、許容できるソースです。これは、再起動がどのような順序でも行われることはめったにないためです。
シードをEEPROMに保存でき、デバイスの起動時にシードをインクリメントして再度保存できます。したがって、再起動するたびに異なる乱数が使用されます。
線形フィードバックシフトレジスタをシミュレートすることにより、ビットを操作して疑似乱数を生成できます。
その場合、問題は「何ビットをシミュレートしたいですか?」になります。
ウィキペディアにはいくつかの情報があります。
ADCにアクセスできる場合は、ADCから最下位ビットを読み取り、他の人が投稿したように、疑似乱数ジェネレーターのシードとして使用できます。明らかに、ADCから複数のビットを読み取る必要があるため、複数の読み取りが必要になり、時間がかかる場合があります。ただし、これを実行する必要があるのは、たとえば起動時に1回だけで、その後、より高速なPRNGを使用して新しい乱数を生成します。
多くの組み込みデバイスには、たとえばAtmelのATMegaファミリなどのインADCが組み込まれています。
ハードウェアにユーザー用のボタンがある場合、簡単なトリックはボタンが押された時間を数えることです。十分に速い短いカウンターを使用すると、「乱数」を取得できます。
命令セット(シフトとxorのみ、乗算または除算なし)で最も高速で要求の少ない疑似乱数ジェネレーターは、メルセンヌツイスターのアイデア(一般化線形フィードバックシフトレジスターと呼ばれます)の小さな変形です。メルセンヌツイスター自体は、マイクロコントローラー用に多すぎるメモリを必要とします。
これらのジェネレーターの問題は、運が悪ければゼロに近い長いシーケンスを生成する可能性があることです。ただし、状態空間の適切なサイズと別のPNRGからの初期化では、これは起こりそうにありません。
また、暗号化やギャンブルに対しても安全ではありません。インテリジェントな攻撃者は、出力を観察した後、将来の状態を予測できます。これは、線形であるためです。
私はかつて、約50個の24ビットワードの状態空間を持つ小さな非標準プロセッサ用にこのようなジェネレータを設計しました。良いものが見つかるまで、Diehardテストスイートでバリアントをテストしました。アプリケーションは、ハードウェアテスト用にランダムなバリエーションを生成していました。
タイマーを読み取り、一連のビットでxoring / nanding / etcを実行すると、ユーザーに半ランダムが与えられます。これは、イベント間のタイミングが十分に離れている可能性が高いため、ユーザーは実際にはタイマー。
ピンをフローティングのままにしておくことができる場合は、線形フィードバックシフトレジスタを使用して乱数を生成すると役立つ場合があります。これが進むべき道かどうかはわかりませんが、私のコードを見てください。
// This code was written for 8051 (AT89S52)
unsigned char lfsr = 231; //8 bit shift register, with the seed of 231 or '11100111b'
unsigned char input_bit, i;
void main (void)
{
//Setup
P0_0 = 0; // Leave Pin 0 Port 0 floating
uart_init(); //Initializing uart/serial communication with pc
while (1)
{
for (i = 0; i < 255; i++)
{
if (P0_0 == 1) // If Pin 0.0 is HIGH
{
input_bit = ((lfsr >> 0) ^ (lfsr >> 2) ^ (lfsr >> 3) ^ (lfsr >> 4)) & 1;
lfsr = (lfsr >> 1) | (input_bit << 7);
}
}
printf_tiny("%u\n", lfsr); //Send the random number to PC
soft_delay(65535); //Simple delay function
} //end while (1) loop
}
編集:私の答えは悪いものかもしれないことがわかりました。フローティングデジタルピンを使用すべきでない理由の詳細:https ://electronics.stackexchange.com/questions/50476/random-number-generators-using-a-gpio-pin