web-dev-qa-db-ja.com

Rand()+ Rand()が負の数を生成するのはなぜですか?

Rand()ライブラリ関数は、ループ内で一度だけ呼び出されると、ほとんど常に正の数を生成することに気付きました。

for (i = 0; i < 100; i++) {
    printf("%d\n", Rand());
}

しかし、2つのRand()呼び出しを追加すると、生成された数はより負の数になります。

for (i = 0; i < 100; i++) {
    printf("%d = %d\n", Rand(), (Rand() + Rand()));
}

私が2番目のケースで負の数を見ている理由を誰かが説明できますか?

PS:ループの前のシードをsrand(time(NULL))として初期化しました。

302
badmad

Rand()は、0Rand_MAXの間の整数を返すように定義されています。

Rand() + Rand()

オーバーフローする可能性があります。あなたが観察するものはおそらく整数オーバーフローによって引き起こされた 未定義の振る舞い の結果です。

539
P.P.

問題は追加です。 Rand() は、0...Rand_MAXint値を返します。したがって、それらを2つ追加すると、Rand_MAX * 2に到達します。それがINT_MAXを超える場合、加算の結果はintが保持できる有効範囲をオーバーフローします。署名された値のオーバーフローは未定義の動作であり、キーボードが異国語で話しかける可能性があります。

ここでは、2つのランダムな結果を追加しても何も得られないため、単純なアイデアはそれを行わないことです。あるいは、合計を保持できる場合は、加算の前に各結果をunsigned intにキャストできます。または、より大きなタイプを使用してください。 longは必ずしもintよりも広いわけではないことに注意してください。intが少なくとも64ビットの場合、long longにも同じことが当てはまります。

結論:追加することは避けてください。それ以上の「ランダム性」は提供しません。さらに多くのビットが必要な場合は、値sum = a + b * (Rand_MAX + 1)を連結できますが、intよりも大きなデータ型が必要になる可能性もあります。

あなたの述べた理由はゼロ結果を回避することです:それは2つのRand()呼び出しの結果を追加することで回避できません。両方ともゼロになる可能性があるからです。代わりに、単にインクリメントできます。 Rand_MAX == INT_MAXの場合、これはintで実行できません。ただし、(unsigned int)Rand() + 1は非常に可能性が高いです。おそらく(決定的ではありません)、UINT_MAX > INT_MAXを必要とするため、これは私が知っているすべての実装に当てはまります(過去30年間の組み込みアーキテクチャ、DSP、すべてのデスクトップ、モバイル、およびサーバープラットフォームを網羅) 。

警告:

すでにコメントが散らばっていますが、2つのランダムな値を追加するとnot均一な分布は得られませんが、2つのサイコロを振るような三角形の分布が得られることに注意してください:12( 2つのサイコロ)両方のサイコロは6を表示する必要があります。 11の場合、すでに2つのバリエーションがあります:6 + 5または5 + 6など。

そのため、この面からも追加は悪いです。

また、Rand()が生成する結果は、 疑似乱数ジェネレーター によって生成されるため、互いに独立しているわけではありません。また、標準では、計算値の品質または均一な分布が指定されていないことに注意してください。

これは、 この回答 にコメントした質問の説明に対する回答です。

私が追加したのは、コード内の乱数として「0」を避けるためです。 Rand()+ Rand()がすぐに頭に浮かぶ迅速で汚い解決策でした。

問題は0を避けることでした。提案された解決策には(少なくとも)2つの問題があります。 1つは、他の答えが示すように、Rand()+Rand()が未定義の動作を呼び出す可能性があることです。最善のアドバイスは、未定義の動作を決して呼び出さないことです。もう1つの問題は、Rand()が連続して2回0を生成しないという保証がないことです。

次のコードはゼロを拒否し、未定義の動作を回避します。ほとんどの場合、Rand()を2回呼び出すよりも速くなります。

int rnum;
for (rnum = Rand(); rnum == 0; rnum = Rand()) {}
// or do rnum = Rand(); while (rnum == 0);
35
David Hammen

基本的にRand()0Rand_MAXの間の数字、そしてあなたの場合は2 Rand_MAX > INT_MAXを生成します。

オーバーフローを防ぐために、データ型の最大値でモジュラスすることができます。これは乱数の分布を乱すことになりますが、Randは素早い乱数を得るための単なる方法です。

#include <stdio.h>
#include <limits.h>

int main(void)
{
    int i=0;

    for (i=0; i<100; i++)
        printf(" %d : %d \n", Rand(), ((Rand() % (INT_MAX/2))+(Rand() % (INT_MAX/2))));

    for (i=0; i<100; i++)
        printf(" %d : %ld \n", Rand(), ((Rand() % (LONG_MAX/2))+(Rand() % (LONG_MAX/2))));

    return 0;
}
3
Khaled.K

2 Rand()の合計で返される値がRand_MAXの値を超えないようにすることで、ややトリッキーなアプローチを試すことができます。考えられるアプローチは、sum = Rand()/ 2 + Rand()/ 2です。これは、両方のRandが偶然32767を返しても(32767/2 = 16383)16383 + 16383 = 32766であっても、Rand_MAX値が32767の16ビットコンパイラの場合、負の合計にならないことを保証します。

2
Jibin Mathew

0を回避するには、これを試してください。

int rnumb = Rand()%(INT_MAX-1)+1;

limits.hを含める必要があります。

1
Doni

私が追加したのは、コード内の乱数として「0」を避けるためです。 Rand()+ Rand()がすぐに頭に浮かぶ迅速で汚い解決策でした。

結果がゼロになることも、オーバーフローすることもない簡単な解決策(わかりました、「ハック」と呼びます):

x=(Rand()/2)+1    // using divide  -or-
x=(Rand()>>1)+1   // using shift which may be faster
                  // compiler optimization may use shift in both cases

これはあなたの最大値を制限しますが、あなたがそれを気にしないのであれば、これはあなたのためにうまくいくはずです。

1
Kevin Fegan