Proceed in two steps:
- Distribute exactly
n/3
zeros randomly over your array and set the rest to 1
.
- Assign a random sign to the remaining ones to get the desired -1/+1.
Example code:
int F[n];
// fill with 1
for(k=0; k<n; k++) {
F[k] = 1;
}
// distribute n/3 zeros
for(k=0; k<n/3; k++) {
// find a location which does not have a 0 yet
int i;
do {
i = myRand() % n;
} while(F[i] == 0);
F[i] = 0;
}
// change remaining (non zero) to -1 with 50% probability
for(k=0; k<n; k++) {
if(F[k] && myRand()%2) F[k] = -1;
}
This has a runtime of around 2.4 n, but I do not think you can get much faster than that.
The while loop in the second for loop is in average executed around 1.2 times for the case of n/3 zeros.
Remark:
Trial and error used in the second for
loop works quite well if the success probability is high enough. The number of trials you need in average for a probability of p is 1/p.
In our case (n/3 zeroes) the worst probability to find a good location (i.e. for the last zero) is 2/3, thus in average 1.5 iterations. To find places for all n/3 zeros you need in average around 0.2*n iterations.
The average runtime can be computed as -log(1-a)
, where a
is the percentage of zeroes you want to distribute (in your case a = 1/3
).
Some more examples: If you would want to distribute 2/3*n zeros, it would take 1.1*n iterations. For 0.99*n zeroes it is already 4.6*n iterations.
All in average. In the worst case it takes forever.
If you need a runtime guarantee you are perhaps better off by implementing real sampling without reselection, i.e. filling a container with all possible indices, sampling a random element as index and removing it from the container. But this probably has a runtime of around O(n*log(n)). Thus it works good for small n or a large percentage of zeroes.