Generating random numbers without repetitions

I sit there, racking my brain , and it just doesn't work out. You need to create an array of random numbers in the range from 1 to 7 and that there are no repetitions. I know that there are many solutions, but I chose the following: when generating a number, enter it in the array, and when generating the next one, run through the array in search of a match. If found, then generate again. I wrote the code, everything seems to be easy, but the response still slips repetitions. Tell me where a joint?

#include "pch.h"
#include <iostream>
#include <string>
#include <locale>
#include <windows.h>
#include <conio.h>
#include <ctime>
#include <time.h>
#include<stdlib.h> 
#include <stdio.h> 

using namespace std;

int main()
{
setlocale(LC_ALL, "Russian");
SetConsoleCP(1251);
SetConsoleOutputCP(1251);

srand(time(0));

int a[7] = {0,0,0,0,0,0,0}; //создаю массив и заполняю нулями
int random; //переменная для случайных чисел

for (int i = 0; i < 7; i++) { //цикл создания случайных чисел
             random = 1 + rand() % 7; //создание случайного числа
             for (int j = 0; j < 7; j++) { //цикл проверки массива на совпадение
                 if (random == a[j]) { //условие совпадения
                     while (random == a[j]) { //цикл для создания нового случайного числа, пока оно не будет повторяться
                         random = 1 + rand() % 7;
                     }
                 }
                 else {
                     continue; //если повтора нет, переходим к следующей итерации
                 }
            }
             a[i] = random; // присваивание рандомного числа элементу массива
        } 

for (int k = 0; k < 7; k++) { //вывод массива на экран
            cout << a[k];
        }

cout << "Для выхода из консоли нажмите любую клавишу";
    _getch();
    return 0;
}
Author: Harry, 2018-08-26

3 answers

For such a small number, just take the array {1,2,3,4,5,6,7} and randomly shuffle it (for example, using shuffle). It will be faster and easier.

And your problem is that after checking the number for equality to one a[j] and correcting it, you immediately forget about it when checking for the next value j. That is, your loop should look something like

for (int i = 0; i < 7; i++)
{
    for(;;)
    {
        bool good = true;
        random = 1 + rand() % 7;
        for (int j = 0; j < 7; j++)
        {
            if (random == a[j]) { good = false; break; }
        }
        if (good) break;
    }
    a[i] = random; // присваивание рандомного числа элементу массива
}
 10
Author: Harry, 2018-08-26 04:00:28

If the size of your array matches (or is close to) the size of the domain being sampled from, then your approach will be disastrously inefficient, even if implemented correctly.

To solve this problem, there is the simplest Fisher-Yates algorithm, which immediately generates a randomly mixed sequence in one pass without the need for any checks

int main()
{
  int a[7] = { 0 };

  for (unsigned i = 0; i < 7; ++i)
  {
    unsigned j = rand() % (i + 1);
    a[i] = a[j];
    a[j] = i + 1;
  }

  for (int i : a)
    cout << i << " ";

  cout << endl;
}
 12
Author: AnT, 2018-08-26 15:42:59

Since std::unordered_map stores pairs with a unique key:

const int r = 7;
int a[r] = {0};
unordered_map<int, int> m;
for (int i = 0; i < r; ++i)
    while (!(m.emplace(make_pair(1 + rand()%r, i))).second);
//вводим пару, пока попытка ввода не будет удачной
for (auto p : m)
    a[p.second] = p.first;

This is faster (the boundaries of random numbers can be any).

But specifically for your case, when the array elements take values according to some criteria(for example, от 1 по r), you can simply:

for (int i = 0; i < r; ++i)
        a[i] = i + 1;
std::random_shuffle(a, a + r);
 1
Author: AR Hovsepyan, 2018-08-26 09:45:00