web-dev-qa-db-ja.com

Cプログラミング言語の整数配列の一意の乱数

可能性のある複製:
O(1)の一意の乱数?

Cで整数配列に一意の値(重複なし)を入力するにはどうすればよいですか?

int vektor[10];   

for (i = 0; i < 10; i++) {
    vektor[i] = Rand() % 100 + 1;
}

//No uniqueness here
28
Chris_45

あなたの例(1から100までの10個のユニークな乱数を選択)では、1から100までの数字でリストを作成し、乱数ジェネレーターを使用してリストをシャッフルし、リストから最初の10個の値を取得できます。

int list[100], vektor[10];
for (i = 0; i < 100; i++) {
    list[i] = i;
}
for (i = 0; i < 100; i++) {
    int j = i + Rand() % (100 - i);
    int temp = list[i];
    list[i] = list[j];
    list[j] = temp;
}
for (i = 0; i < 10; i++) {
    vektor[i] = list[i];
}

以下のcobbalのコメントに基づいて、次のように言った方が良いでしょう。

for (i = 0; i < 10; i++) {
    int j = i + Rand() % (100 - i);
    int temp = list[i];
    list[i] = list[j];
    list[j] = temp;

    vektor[i] = list[i];
}

リストを設定するのはO(N)ですが、ランダムな要素を選択するのはO(M)です。

5
mob

私はこれでうまくいくと思います(私はそれを構築しようとしませんでしたので、構文エラーは読者のための練習として修正するために残されています)。もっとエレガントな方法があるかもしれませんが、これはブルートフォースソリューションです。

int vektor[10];    
int random;
int uniqueflag;
int i, j

for(i = 0; i < 10; i++) {
     do {
        /* Assume things are unique... we'll reset this flag if not. */
        uniqueflag = 1;
        random = Rand() % 100+ 1;
        /* This loop checks for uniqueness */
        for (j = 0; j < i && uniqueflag == 1; j++) {
           if (vektor[j] == random) {
              uniqueflag = 0;
           }
        }
     } while (uniqueflag != 1);
     vektor[i] = random;
}
3
Chris J

単純に乱数を生成し、それらが問題ないかどうかを確認することは、一般的にこの問題を解決するための貧弱な方法です。このアプローチでは、可能なすべての値を取得し、それらをシャッフルしてから上位10を取得します。これは、カードのデッキをシャッフルし、トップをオフに配ることに直接類似しています。

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define randrange(N) Rand() / (Rand_MAX/(N) + 1)

#define MAX 100        /* Values will be in the range (1 .. MAX) */
static int vektor[10];
int candidates[MAX];

int main (void) {
  int i;

  srand(time(NULL));   /* Seed the random number generator. */

  for (i=0; i<MAX; i++)
    candidates[i] = i;

  for (i = 0; i < MAX-1; i++) {
    int c = randrange(MAX-i);
    int t = candidates[i];
    candidates[i] = candidates[i+c];
    candidates[i+c] = t;
  }

  for (i=0; i<10; i++)
    vektor[i] = candidates[i] + 1;

  for (i=0; i<10; i++)
    printf("%i\n", vektor[i]);

  return 0;
}

詳細については、シャッフルについては comp.lang.c FAQ質問13.19をリスト を、乱数の生成については 質問13.16 を参照してください。

3
Tim

1つの方法は、配列に新しい乱数が既に含まれているかどうかを確認し、含まれている場合は、新しい乱数を作成して再試行することです。

これは、(ランダム;))配列にない数値を取得する可能性がないことを示しています。したがって、数がすでに配列にあるかどうかをチェックする回数をカウントする必要があります。カウントがMAX_DUPLICATE_COUNTを超える場合は、例外をスローします。代わりにコード:P)

0
cwap

O(M)平均時間法です。

方法:M <= N/2の場合、プロシージャS(M、N)(下)を使用して結果配列Rを生成し、Rを返します。M> N/2の場合、プロシージャS(NM、N)を使用してRを生成します。次にX = {1..M}\R [{1..M}のRの補数]を計算し、Xを Fisher-Yates shuffle [in time O(M)]でシャッフルし、Xを返します。

O(M) == O(N)であるM> N/2の場合、補数を計算するいくつかの高速な方法があります。 main()にインラインでコーディングされた手順S(M、N)の例のみが含まれています。Fisher-YatesシャッフルはO(M)であり、関連する質問に対する主な回答 #196017 。その他の以前の関連質問: #158716 および #54059

S(M、N)がO(M)の代わりにO(N)時間をとる理由) クーポンコレクターの問題 で説明されている期待値E(t_k)はkH_kであり、そこからE(t_ {k/2})= k(H_k-H_ {k/2})または約k *(ln(k)-ln(k/2)+ O(1))= k *(ln(k /(k/2)) + O(1))= k *(ln(2)+ O(1))= O(k)。

手順S(k、N):[この手順の本文は、以下のコードのコメント「Gen M distinct random numbers」の後にある12行です。] 3つのM + 1要素整数配列H、L、およびVからすべて-1の値。 i = 0〜M-1の場合:ランダム値vをV [i]とセンチネルノードV [-1]に入れます。 H [v%M]からM個のリストヘッドの1つを取得し、vと一致するまでそのリストをたどります。一致がV [-1]にある場合、vは新しい値です。リストヘッドH [v%M]とリストリンクL [i]を更新します。一致がV [-1]にない場合、別のvなどを取得してテストします。

「リストに従う」各ステップには、コストO(1)が必要です。なぜなら、最後を除く各ステップで、リストの平均長が1未満であるためです(処理の終了時に、MリストにはM要素なので、平均長は徐々に正確に1になります。

 // randomMofN - jiw 8 Nov 2011     
 // Re: https://stackoverflow.com/questions/1608181/
 #include <stdlib.h>
 #include <stdio.h>
 int main(int argc, char *argv[]) {
   int h, i, j, tM, M, N, par=0, *H, *L, *V, cxc=0;
   // Get M and N values
   ++par; M = 42;  if (argc > par) M = atoi(argv[par]);
   ++par; N = 137; if (argc > par) N = atoi(argv[par]);
   tM = 3*M+3;
   H = malloc(tM*sizeof(int));
   printf ("M = %d,  N = %d  %s\n", M, N, H?"":"\nmem error");
   if (!H) exit(13);
   for (i=0; i<tM; ++i)           // Init arrays to -1's
     H[i] = -1;
   L = H+M;  V = L+M;

   // Gen M distinct random numbers
   for (i=0; i<M; ++i) {
     do {
       ++cxc;                     // complexity counter
       V[-1] = V[i] = random()%N;
       h = V[i]%M;                // h = list-head index
       j = H[h];
       while (V[j] != V[i])
         j = L[j];
     } while (j>=0);
     L[i] = H[h];
     H[h] = i;
   }

   // Print results
   for (j=i=0; i<M; ++i) {
     j += printf ("%4d ", V[i]);
     if (j>66) j = printf ("\n");
   }
   printf ("\ncxc %d\n", cxc);
   return 0;
 }

簡単な解決策は、ゼロに初期化されたすべての可能な数のマスク配列を作成し、その数が生成された場合にエントリを設定することです

int Rand_array[100] = {0};
int vektor[10];   
int i=0, rnd;
while(i<10) {
    rnd = Rand() % 100+ 1;
    if ( Rand_array[rnd-1] == 0 ) {
        vektor[i++] = rnd;
        Rand_array[rnd-1] = 1;
    }
}
0
Amro

1桁目と2桁目を別々に生成します。必要に応じて後でシャッフルします。 (メモリからの構文)

_int vektor[10];
int i = 0;

while(i < 10) {
  int j = Rand() % 10;
  if (vektor[j] == 0) { vektor[j] = Rand() % 10 + j * 10; i ++;}
}
_

ただし、数字はnでほぼ離れています。0<n <10です。

または、新しく生成されたものの存在をすばやく確認できるように(O(n log n))、番号をソートしたままにする必要があります(O(log n))。

0
Milind C

フロイドアルゴリズムが好きです。

ただし、0からMinではなく)からすべての乱数を取得できます。

#define M 10
#define N 100    

unsigned char is_used[N] = { 0 }; /* flags */
int in, im;

im = 0;

for (in = N - M; in < N && im < M; ++in) {
  int r = Rand() % (N + 1); /* generate a random number 'r' */

  while (is_used[r])
  {
     /* we already have 'r' */
     r = Rand() % (N + 1);
  }
  vektor[im++] = r + 1; /* +1 since your range begins from 1 */
  is_used[r] = 1;
}

assert(im == M);