0

I have this functions which return a random array in c++:

int* randomArray(int countOfRows){
    int test1 [countOfRows] = {};
    int insertValue;
    int check;
        for (int n=0; n < countOfRows; ++n){
            srand(time (NULL) );
            while (test1[n] == NULL){
                insertValue = (rand () %100 + 1 );
                for(int i = 0; i < countOfRows; i++){
                    if (test1[i] == insertValue){
                        check = 1;
                        break;
                    }
                    else{
                        check = 0;
                    }
                }

                if (check == 0){
                    test1[n] = insertValue;
                }
            }
        }
    return test1;
}
  • How can I call that array?
  • what is the difference between int* and int[]

thank you :)

8
  • 4
    Use std::vector. It will spare you all the pitfalls you are about to face. Commented Aug 28, 2017 at 5:48
  • You cannot return a pointer to an array that is allocated on the stack - the array disappears as soon as the function returns. Commented Aug 28, 2017 at 5:51
  • Unrelated, I'm fairly confident while (test1[n] == NULL) would be better represented as while (test1[n] == 0). Related, and moreover, if you want countOfRows unique values between 1..100, (where countOfRows had better be no more than 100 or this spins to infinity), you're better off spinning up a sequence of 1..100, shuffling it, then just peeling off the first countOfRows elements off the front for your result. Best part: you can do it all in a single vector; just resize down to countOfRows before returning it. Commented Aug 28, 2017 at 5:56
  • are you aware that test1 is a variable that will be free after the return of the function.??? (you are returning a pointer to something that you dont have later anymore....) Commented Aug 28, 2017 at 6:02
  • @ΦXocę웃Пepeúpaツ how can I change that? Commented Aug 28, 2017 at 6:35

3 Answers 3

4

Your code has four significant problems, one of them critical, one non-standard and implementation dependent, and two general algorithmic problems.

First, the most important, you're returning the address of an automatic variable, which means it is both useless and will invoke undefined behavior to dereference by the caller. Declared at the top of your function is:

int test1 [countOfRows] = {};

which itself brings up the second point, this non-standard for two reasons: variable-length arrays are not supported by the C++ standard, and by inference, initialization of said-same is likewise not supported. Then later...

return test1;

The caller of your function will receive an address, but that address is useless. It no longer addresses anything concrete, as test1 no longer exists once the function returns. This is remedied a number of ways, and considering this is C++, the easiest is using a std::vector<int>, which supports value-return.

The two significant algorithm problems are

  • Your seeding of srand should not be in the for loop. In fact, if you're using srand and rand, the seeding should be done once in your entire process.
  • The process of exhaustive searching to see if a current random pick has already been used to avoid duplicates is needless if you simply use a different algorithm, which I'll cover later.

Therefore, the simplest fix for your code will be to do this:

#include <iostream>
#include <vector>
#include <cstdlib>
#include <ctime>

std::vector<int> randomArray(int countOfRows)
{
    std::vector<int> test1(countOfRows);
    int check = 0;
    for (int n=0; n < countOfRows; ++n)
    {
        while (test1[n] == 0)
        {
            int insertValue = (rand () %100 + 1 );
            for(int i = 0; i < countOfRows; i++)
            {
                if (test1[i] == insertValue){
                    check = 1;
                    break;
                }
                else{
                    check = 0;
                }
            }

            if (check == 0){
                test1[n] = insertValue;
            }
        }
    }
    return test1;
}

int main()
{
    std::srand(static_cast<unsigned>(std::time(NULL)));
    std::vector<int> vec = randomArray(20);
    for (auto x : vec)
        std::cout << x << ' ';
    std::cout.put('\n');
}

Output (varies, obviously)

8 50 74 59 31 73 45 79 24 10 41 66 93 43 88 4 28 30 13 70 

A Finite Set Algorithm

What you're really trying to generate here is a finite set of integers in the range of 1..100. I.e., there are no duplicate values used, and the number of items being returned could be anything from 1..100 as well. To do this, consider this algorithm:

  1. Generate a sequence of 1..100 in a std::vector<int>
  2. Using a pseudorandom generator from the standard library, shuffle the sequence using std::shuffle
  3. Resize the resulting vector to be the number of elements you want to return.

Regarding #3 from above, consider a small example, suppose you wanted just ten elements. Initially you build a sequence vector that looks like this:

1 2 3 4 5 6 7 8 9 10 11 12 13...  ...99 100

Now you shuffle this vector using a std::shuffle and a pseudorandom generator like std::mt19937 : (the first twenty elements shown for brevity):

48 39 31 44 68 84 98 40 57 76 70 16 30 93 9 51 63 65 45 81...

Now, you simply resize the vector down to the size you want, in this case ten elements:

48 39 31 44 68 84 98 40 57

And that is your result. If this sounds complicated, you may be surprised to see how little code it actually takes:

Code

#include <iostream>
#include <algorithm>
#include <vector>
#include <numeric>
#include <random>

std::vector<int> randomSequence100(std::size_t count)
{
    if (count > 100)
        count = 100;

    static std::random_device rd;
    std::vector<int> result(100);
    std::iota(result.begin(), result.end(), 1);
    std::shuffle(result.begin(), result.end(), std::mt19937(rd()));
    result.resize(count);

    return result;
}


int main()
{
    // run twenty tests of random shuffles.
    for (int i=0; i<20; ++i)
    {
        auto res = randomSequence100(20);
        for (auto x : res)
            std::cout << x << ' ';
        std::cout.put('\n');
    }
}

Output

27 71 58 6 74 65 56 37 53 44 25 91 10 86 51 75 31 79 18 46 
6 61 92 74 30 20 91 89 64 55 19 12 28 13 5 80 62 71 29 43 
92 42 2 1 78 89 65 39 37 64 96 20 62 33 6 12 85 34 29 19 
46 63 8 44 42 80 70 2 68 56 86 84 45 85 91 33 20 83 16 93 
100 99 4 20 47 32 58 57 11 35 39 43 87 55 77 51 80 7 46 83 
48 39 31 44 68 84 98 40 57 76 70 16 30 93 9 51 63 65 45 81 
32 73 97 83 56 49 39 29 3 59 45 89 43 78 61 5 57 51 82 8 
21 46 25 29 48 37 77 74 32 56 87 91 94 86 57 67 33 9 23 36 
27 46 66 40 1 72 41 64 53 26 31 77 42 38 81 47 58 73 4 11 
79 77 46 48 70 82 62 87 8 97 51 99 53 43 47 91 98 81 64 26 
27 55 28 12 49 5 70 94 77 29 84 23 52 3 25 56 18 45 74 48 
95 33 25 80 81 53 55 11 70 2 38 77 65 13 27 48 40 57 87 93 
70 95 66 84 15 87 94 43 73 1 13 89 44 96 10 58 39 2 23 72 
43 53 93 7 95 6 19 89 37 71 26 4 17 39 30 79 54 44 60 98 
63 26 92 64 83 84 30 19 12 71 95 4 81 18 42 38 87 45 62 70 
78 80 95 64 71 17 14 57 54 37 51 26 12 16 56 6 98 45 92 85 
89 73 2 15 43 65 21 55 14 27 67 31 54 52 25 72 41 6 85 33 
4 87 19 95 78 97 27 13 15 49 3 17 47 10 84 48 37 2 94 81 
15 98 77 64 99 68 34 79 95 48 49 4 59 32 17 24 36 53 75 56 
78 46 20 30 29 35 87 53 84 61 65 85 54 94 68 75 43 91 95 52 

Each row above was a set of twenty elements take from the sequence of 1..100. No single row has duplicates (check if you want).

Caveat

This technique works wonderfully for either small domains or large result sets from larger domains. But it has its limits to consider.

For example: Once your potential domain reaches the point significant size (say, numbers in 1...1000000) and you want only small result sets (say, no larger than 100 elements), you're better off using a std::unordered_set and iterative probing similar to what you're doing now. The technique you use depends entirely on your performance goals and your usage patterns.

Counterexample: If you wanted a half-million unique elements shuffled from a million-element domain, the load/shuffle/resize technique will work well.

Ultimately you have to decide, and measure to confirm.


Some useful links about some of the things used here (bookmark this site, as it is absolute gold for information about C++):

Sign up to request clarification or add additional context in comments.

2 Comments

A cut above. Top notch answer :)
Just curious, should the mt19937 be created each time or should it be static like the random_device or does it not matter at all?
0

From my view this function has problems.

It return the point of test1, which is allocated in the stack, which is invalid out of the scope of randomArray.

So if you change to malloc, this is allocated in heap, then it still valid when out of the scope of randomArray.

int *test1 = (int*) malloc(countOfRows* sizeof(int));

And you can using test1[x] to get the value of each int, for sure you should know the length of test1 is countOfRows.

Please don't forget to delete this point when it is not used...

Call this array is simple

int* values = randomArray(1000); printf("%d\r\n",values[0]);

Comments

0

In the function randomArray() declare test1[] as a static int[]. return the array using pointers, " return test1 " in the main function use a pointer to access the return value " int *ptr=randomArray(n) "

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.