1

Okay guys, I'm an idiot apparently, and can't figure out this simple simple problem.

The rest of my code isn't worth anything, so I'll just post relevant code. I already know WHY it's broken, but I don't know HOW to fix it. I'm really just looking more for pseudocode to give me a starting point.

What I'm trying to do is make an array called "temp", and store in each of its 5 indices a unique random number (these numbers will be used later as indices to a different array). It's not simply a matter of initializing it from 1 to 5 and using a shuffle though because the random numbers can be up to the number of vertices in my graph, numVertices.

int place = -1;

int temp[5] = {-1};
for(int i = 0; i < 5 || i < numVertices; i++){
    do{
        place = rand();
        place = (double)place / RAND_MAX * numVertices;
        temp[i] = place;
    } while(place == temp[0] or
          place == temp[1] or
          place == temp[2] or
          place == temp[3] or 
          place == temp[4]); ......`

Clearly, my code gets stuck in an infinite loop because it compares place (the random index I'm storing into a different array) to temp[i], and after it performs the operation it will always equal temp[i]. What I'm TRYING to do is compare it to the others and go "If it matches one of the stored numbers already, randomize place again until it doesn't match, then store it in the next slot in temp." However, I can't for my life think of how to do this..

πάντα ῥεῖ
  • 1
  • 13
  • 116
  • 190
zeBugMan
  • 435
  • 1
  • 6
  • 9
  • If I understand you clearly - you need to get 1..N numbers in random order (1..5 in this case). Just create an array with a[i] = i, and use random_shuffle() on it. Am I wrong? – Ralor Apr 23 '14 at 19:56
  • Kinda.. The array temp must be size 5. We're doing a graph, and as we insert, we connect to 5 random other nodes in the graph. The graph is up to size 50000 though. The integers stored in temp are indices to specific nodes in the graph, so they can be anywhere from 1-N. However, I want to avoid connecting to the same node twice or connecting to itself. – zeBugMan Apr 23 '14 at 19:58
  • Hhh, I think I got that! But you must be careful with RAND_MAX, it can be less than 50000. – Ralor Apr 23 '14 at 20:03
  • I still like Ralor's idea; maybe it could still work if you create the full-sized random array, and then determine the target index based on the shuffled array. It might be a bit more work than needed, but at least than it's direct log(n) complexity. When finding random numbers, in general you want to be *very* careful about using while loops until you get what you want - eventually, it will start holding up the whole system while slot 5000 is waiting until it gets '5000'. – Katana314 Apr 23 '14 at 20:43
  • @Katana314 he need just 5 random neighbours, I gas there is no need to think of stable solution. But fixing the bugs in case vertex count <= 5 is very important) – Ralor Apr 23 '14 at 21:31

4 Answers4

2

It helps if you break things down into simple pieces. It looks like you're using the C language here. So, first a definition for number of vertices:

#define NUM_VERTICES 1000

Then a trivial function to generate a random number x such that lower_bound <= x < upper_bound:

int next_random( int inclusive_lower_bound , int exclusive_upper_bound )
{
  int n = exclusive_upper_bound - inclusive_lower_bound ; // size of desired domain
  int r = rand() % n ;                                    // 0 <= r < n
  int x = inclusive_lower_bound + r ;                     // min <= x < max
  return x ;
}

Then a trivial function to see if your buffer already contains a value:

int contains( int x, int *p, int limit)
{
  int j = 0 ;
  while ( j < limit && p[j] != x )
  {
    ++j;
  }
  return (j < limit ? 1 : 0);
}

Then a trivial function to generate the next unique value in the domain:

int next_unique_random( int* p, int limit, int upper_bound )
{
  int x , j ;
  do
  {
    x = next_random( 0 , upper_bound ) ;
  } while ( contains(x,p,limit) ) ;
  return x ;
}

Finally, we can put it all together:

int main()
{
  int temp[5] = { 0, 0, 0, 0 } ;
  int i = 0;

  for (i = 0; i < 5; ++i )
  {
    temp[i] = next_unique_random( temp , i , NUM_VERTICES ) ;
  }

  return 0;
}

Easy!

Nicholas Carey
  • 71,308
  • 16
  • 93
  • 135
  • Hmm, I thinked about how to make algorithms frendly for those who are not interested in it, but needed to use it) Did you learned to do it from somewhere? I really need to know, It's beautiful! – Ralor Apr 23 '14 at 20:37
  • You should change `RAND_MAX + 1` to `double(RAND_MAX) + 1` –  Apr 23 '14 at 21:18
  • @DieterLücking: fixed it make the scope of the cast clear. The numerator is now `((double)rand())`; the denominator will be implicitly cast to double (the required widen conversion). – Nicholas Carey Apr 23 '14 at 21:53
  • @NicholasCarey Not that one - what integer value has (RAND_MAX + 1) ? –  Apr 23 '14 at 21:59
  • @NicholasCarey You should be happy, but now (after your edit) you are moving to a downvote (lookup rand issues) –  Apr 23 '14 at 23:43
1

Instead of writing a huge conditional, write another loop.

...
while (true) {
    ...
    bool tryAgain = false;
    for(int j = 0; j < i; j++) {
        if (temp[j] == temp[i]) 
            tryAgain = true;
    }
    if (!tryAgain)
         break;
}

But if I were writing this in Java, I would use a HashSet to store the numbers and use its efficient contains method to tell me if i've used the number already. Although, because the array is so small (5 elements), the above code should be pretty fast regardless.

Aleksandr Dubinsky
  • 22,436
  • 15
  • 82
  • 99
  • @DieterLücking I believe that for similar questions it's better to show someone how to make their code work with minimal changes. To help them do what *they* wanted to do. – Aleksandr Dubinsky Apr 23 '14 at 20:20
  • @DieterLücking My flow control isn't confusing. `while(true)` means "custom loop". Then I construct my boolean, and finally test it. You were testing not a boolean, but the equivalence of the values of certain integers, which were implicitly determined by how many times a previous loop executed. – Aleksandr Dubinsky Apr 23 '14 at 20:26
  • -1 confusing flow control and needless comparisons after tryAgain has become true. –  Apr 23 '14 at 20:51
0
#include <array>
#include <cstdlib>
#include <ctime>
#include <set>
#include <iostream>

int main()
{    
  srand(time(NULL));

  std::array<int, 5> a;
  std::set<int> s;
  int r;

  for(size_t i = 0; i < a.size(); ++i)
  {
    do 
    {
      r = rand();
    } while(s.find(r) != s.end());

    a[i] = r;
    s.insert(r);
  }

  for(const auto& i: a)
    std::cout << i << std::endl;

  return 0;
}
AntiClimacus
  • 1,380
  • 7
  • 22
0

Edit, again : I just saw your comment :

'use strict'

var numVertices = 100,
    temp = [];

var RAND_MAX = 32767;

for(var i = 0; i < 5; i++){
    var place = parseInt(Math.random() * numVertices),  // C++ : rand() % numVertices
        valid = true;
    //place = place * numVertices;
    for(var x = 0; x < temp.length; x++) {
        if(temp[x] == place) {
            i--;
            valid = false;
            break
        }
    }
    if(valid) temp[i] = place;
}
console.log(temp);

You can't use == when comparing floats / doubles, here's the code to do it in Javascript, but it's simple enough to port :

*edit : * check What is the most effective way for float and double comparison? for more information about comparing floats.

'use strict'

var numVertices = 5,
    temp = [];

var RAND_MAX = 32767,
    EPSILON = 1.00000001

for(var i = 0; i < 5 || i < numVertices; i++){
    var place = Math.random(),
        valid = true;
    place = place / RAND_MAX * numVertices;
    for(var x = 0; x < temp.length; x++) {
        if(Math.abs(temp[x] - place) > EPSILON) { //Math.abs == fabs in C/C++
            i--;
            valid = false;
            break
        }
    }
    if(valid) temp[i] = place;
}
console.log(temp);
Community
  • 1
  • 1
OneOfOne
  • 95,033
  • 20
  • 184
  • 185
  • 1
    Mutating the for loop counter is not a good idea for writing readable code. At least, I would mutate it at the end, in `if (valid) temp[i] = place; else i--;`. – Aleksandr Dubinsky Apr 23 '14 at 20:29