0

こんにちは、私のコードには現在 3 つの関数があり、それぞれが大量の乱数配列を生成しています。リンクされたリストまたは多次元配列を返す関数を1つだけにして、少しすっきりさせる方法があるかどうか疑問に思っています。

( http://pastebin.com/Y5aE6XKSからコピー)

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
#ifndef RAND_MAX
#define RAND_MAX 2147483648
#endif
#define N 420000

double* rdm_X(void);
double* rdm_Y(void);
double* rdm_Z(void);

void main(void)
{
   double* Random_number_list_X = rdm_X();
   double* Random_number_list_Y = rdm_Y();
   double* Random_number_list_Z = rdm_Z();
   double X[N+1], Y[N+1], Z[N+1], density = 1, vol = 42.0;
   double sum = 0, sum_x = 0, sum_y = 0, sum_z = 0;
   int i;

   for (i = 0; i <= N; i++) {
      X[i] = 3 * Random_number_list_X[i] + 1;
      Y[i] = 7 * Random_number_list_Y[i] - 3;
      Z[i] = 2 * Random_number_list_Z[i] - 1;
      if ((Z[i]*Z[i]) + (sqrt(X[i]*X[i] + Y[i]*Y[i]) - 3)*(sqrt(X[i]*X[i] + Y[i]*Y[i]) - 3) <= 1) {
         sum += density;
         sum_x += X[i] * density;
         sum_y += Y[i] * density;
         sum_z += Z[i] * density;
      }
   }
   printf("(%.5lf, %.5lf, %.5lf)\n",
            sum_x/sum, sum_y/sum, sum_z/sum);
}

double* rdm_X(void)
{
   double* Random_number_list_X = calloc(N + 1, sizeof(double));
   int i;

   srand(time(NULL));
   for (i = 1; i <= N; i++) {
      Random_number_list_X[i] = (float) rand() / (float) RAND_MAX;
   }
   return Random_number_list_X;
}

double* rdm_Y(void)
{
   double* Random_number_list_Y = calloc(N + 1, sizeof(double));
   int i;
   sleep(1);
   srand(time(NULL));
   for (i = 1; i <= N; i++) {
      Random_number_list_Y[i] = (float) rand() / (float) RAND_MAX;
   }
   return Random_number_list_Y;
}

double* rdm_Z(void)
{
   double* Random_number_list_Z = calloc(N + 1, sizeof(double));
   int i;
   sleep(2);
   srand(time(NULL));
   for (i = 1; i <= N; i++) {
      Random_number_list_Z[i] = (float) rand() / (float) RAND_MAX;
   }
   return Random_number_list_Z;
}
4

5 に答える 5

6

いくつかのポイント:

  1. 自分を定義しないでRAND_MAXください。
  2. mainint を返します。
  3. 電話srandは一度だけ。
  4. への余分な呼び出しを削除し、srand1 つの関数を使用して配列を初期化します。
  5. X、Y、および Z を配列として定義しましたが、実際にはそれぞれ 1 つの値しか使用または必要としませんでした。
  6. 配列のサイズが固定されているため、動的割り当てを使用する理由はないようです。
于 2010-09-10T21:41:58.370 に答える
4

srand電話は 1 回だけにすべきだと指摘したのは私が初めてではありませんが、その理由を説明します。

より頻繁に呼び出すほど、出力のランダム性srand低くなりrandます。

このrand関数は疑似乱数ジェネレーターです。これは、ランダムに見え、ランダム性に対応する数学的特性を持つ数値を生成することを意味しますが、実際にはランダムではありません。の出力は、rand実際には固定された、完全に決定論的な数列です。

むしろ、完全に決定論的なシーケンスの大きなファミリーの 1 つを生成します。を使用して「シード」値を指定することにより、必要なシーケンスを選択しますsrandsrandシードを与えるとx、 の次の出力はrand、シードによって識別される疑似乱数 (ただし完全に決定論的!) シーケンスの最初の数になりますx。言い換えると:

int foo(int x)
{
   srand(x);
   return rand();
}

入力ごとに異なる値を返しますが、特定xの に対しては常に同じ値を返します。まったくランダムではありません!

の出力に依存するプログラムでバグを発見した場合、から同じシーケンスを取得して同じ動作を取得するためにrandに同じシードを提供することで、バグを確実に再現できるため、これは実際には便利な機能です。プログラム。srandrand

一度呼び出す必要がある理由srandは、そうしないと、プログラムが常に同じ数のシーケンスを受け取るためですrand(シードによって識別されるシーケンス1)。複数回呼び出したくない理由(ほとんどの場合) は、シーケンスの 1 つを完全に取得させるのではなく、シーケンスの先頭に繰り返し強制的に戻すためです。与えられたシーケンスにはランダム性の特性がありますが、シーケンスの始まりのシーケンスは必ずしもこの特性を持っているわけではありません。srandrand

明らかに、同じシードで繰り返し呼び出すと、特に悪いことになります。なぜなら、毎回同じシーケンスの先頭に強制的に戻ることになり、常に同じ値が生成されるためです。まさにあなたが望んでいないものです。srandrandrand

一般的に見られる理由srand(time(NULL))は、特定のプログラムの 2 つの呼び出しの間で時間が異なる可能性が高いためです。つまり、プログラムが実行されるたびに、異なる疑似乱数シーケンスが使用されます。ただしtime、時間を秒単位の粒度で返すだけなので、プログラムの 1 回の呼び出し内でこれを繰り返し実行し、呼び出しの間に 1 秒未満が経過するsrandと、同じシードで繰り返し再シードされます。 、あなたが観察したように、ばかげた結果が得られました。

結論:srandを初めて使用する前に、1 回だけ呼び出してくださいrand。C ライブラリの実装者がまともな疑似乱数ジェネレータを作成したことを信頼し、存在しない問題を補おうとして「ランダム性を高めよう」としないでください。

于 2010-09-10T22:09:21.517 に答える
1

3 つの関数の唯一の違いは、呼び出し方sleep()です。3 つすべてを 1 つの関数にまとめて、ループ内で 3 回呼び出すことができますか?

于 2010-09-10T20:20:00.867 に答える
1

他の人があなたのプログラムのいくつかの問題にすでに対処していますが、実行するたびに 10 メガバイト以上のメモリがリークしていることに気付いていますか? 自由()...

于 2010-09-10T21:47:23.510 に答える
1

通常、プログラムの呼び出しごとに 1srand() だけ呼び出しますmain()

int main(void) {
    /* initializations */
    srand(time(NULL));

    /* rest of program, with no more calls to srand() */
}
于 2010-09-10T20:33:52.393 に答える