2

I have a code like this:

function myArr(N){

    let arr = [];

    function randomNumber(min,max) {
        if (min > max) {
            let vMin = min;
            min = parseInt(max,10);
            max = parseInt(vMin,10);
        }
        return Math.floor(Math.random()*(max-min+1)+min);
    }

    for(let i = 0; i < N; i++) {
        arr.push(randomNumber(100,-100));
    }
    return arr;
}

This function generates an array with N numbers. But I want that the sum of these generated numbers will be equal to 0. How to make it? I was thinking about conditional 'if' but I don't exactly know, how to use it in this case ... Maybe some of you know, how to do this? Thanks for any tips!

  • Calculate the sum of the first `n-1` numbers and insert `result*-1` as last number? – jp-jee Sep 21 '18 at 15:30
  • generate n-1 random numbers, then add them, let the last number be `0-sum`, `sum+0-sum` is always 0, problem solved. – Vaibhav Vishal Sep 21 '18 at 15:32
  • you could use an alogrithm which uses an approach by checking the min and max number of a random value plus an offset, like in this [answer](https://stackoverflow.com/a/50405632/1447675) – Nina Scholz Sep 21 '18 at 15:35

4 Answers4

1

While generating numbers you have to make sure that the numbers stay close to zero, then you generate N - 1 numbers and calculate the last one:

 const arr = [];
 let sum = 0;

 for(let i = 0; i < N - 2; i++) {
   let number;
   if(sum >= 100) {
     number = randomNumber(100 - sum, -100);
   } else if(sum <= -100) {
     number = randomNumber(100, -100 - sum);
   } else {
     number = randomNumber(100, -100);
   }
   sum += number;
   arr.push(number);
 }
 arr.push(Math.floor(-sum / 2), Math ceil(-sum / 2));

Try it

(Won't work well for N < 4)

Jonas Wilms
  • 132,000
  • 20
  • 149
  • 151
  • @iwona glad to help :) – Jonas Wilms Sep 21 '18 at 15:46
  • FYI - in testing, I noticed that your solution seems to produce numbers between -100 and 100 that, although still not uniform, over time are more evenly distributed than mine. I'm not sure how that happens. – גלעד ברקן Sep 22 '18 at 13:02
  • @גלעד ברקן maybe cause you used the OPs `randomNumber` function which excludes the lower border. Therefore the random numbers are narrowed down be one at each iteration. – Jonas Wilms Sep 22 '18 at 14:01
  • 1
    Thanks but after reviewing it, I'm not sure if that's the reason. OPs `randomNumber` function seems to work fine with the parameters my solution passes to it (they are always ordered `(low, high)`). – גלעד ברקן Sep 22 '18 at 14:48
1

There are many ways to generate an array of randomly-generated values that add up to zero, but they all have different implications for the distribution of values.

For example, one simple approach is to first generate the values and compute their average, and then subtract that average from each value. The consequence of this is that the values may end up outside the range you originally wanted; for example, if you randomly generate [100, 100, 100, -100], then the average is 50, so you'd end up with [50, 50, 50, -150]. You can compensate for that by starting out with a narrower range than you really need; but then that means that values in or near that narrower range will be much more likely to appear than values near the end of your full range.

Another simple approach is to generate only n/2 values, and for each value that you generate, to include both that value and its arithmetic inverse (e.g., if you generate 37, then your result will include both 37 and -37). You can then randomly shuffle the result; so, for example, if you randomly generate [17, -84, 12], then your final array might be [-12, 17, -84, -17, 84, 12].

. . . all of which is to say that you need to figure out your precise requirements. Randomness is complicated!

ruakh
  • 175,680
  • 26
  • 273
  • 307
  • Randomness is easy. Uniformity is complicated. :-) None of the answers produces uniformity. – rici Sep 21 '18 at 22:53
  • @rici: "Complicated" is not the opposite of "easy", but of "simple". Randomess is complicated because it's much easier to make something "random" than to make sure it's really what you wanted. People usually want something more specific than just "random", but don't know hiw to make it precise. (You mention uniformity [over some domain]; another common implicit desideratum is nonrepetition.) – ruakh Sep 22 '18 at 00:40
  • So I guess we agree that randomness is easy. As you indicate, what's difficult (which is the opposite of easy) is articulating accurate requirements. Once articulated, it will be easier to provide an appropriate solution, but the solution may well be complicated. Algorithms which accomplish uniform sampling with or without replacement over a complete solution domain tend to fall into this category. [Random: :-)](https://xkcd.com/221/) – rici Sep 22 '18 at 00:55
1

Here's another way. Start with zero and split a random array element N-1 times:

function myArr(N){
    let arr = [0];
    
    function randomNumber(min,max) {
        if (min > max) {
            let vMin = min;
            min = parseInt(max,10);
            max = parseInt(vMin,10);
        }
        return Math.floor(Math.random()*(max-min+1)+min);
    }
    
    function split(n){
        let low = Math.max(-100, n - 100);
        let high = Math.min(100, n + 100);
        let r = randomNumber(low, high);
  
        return [r, n - r]
    }
    
    for(let i = 0; i < N-1; i++) {
      let idx = ~~(Math.random() * arr.length);
      let newNums = split(arr[idx]);
      
      arr[idx] = newNums[0];
      arr.push(newNums[1]);
    }
    return arr;
}

console.log(myArr(5));
גלעד ברקן
  • 23,602
  • 3
  • 25
  • 61
  • Thaks for this solution :) – Iwona Hajczewska Sep 22 '18 at 06:22
  • 1
    @IwonaHajczewska actually, testing reveals that Jonas Wilms' answer seems to produce numbers between -100 and 100 that, although still not uniform, over time are more evenly distributed than this solution. I'd like to understand more about how that happens, but depending on your needs you may have been more correct in selecting that answer. – גלעד ברקן Sep 22 '18 at 12:57
  • @גלעדברקן: "Not uniform" over what domain? The OP's requirement that the numbers sum to zero means that some bias towards zero is to be expected, because it excludes arrays like [max, max, max, max] and [min, min, min, min] but allows arrays like [0, 0, 0, 0]. Your solution also has such a bias, because your `split` always allows [0, n] but does not always allow [max, n - max] or [min, n - min]. I'm not sure that that's a bad thing. – ruakh Sep 22 '18 at 14:51
  • @ruakh Thanks for your comment. I'm not sure what you mean, though, about `split` - it always allows choosing a random number in the maximum range it can have (e.g., `(low, high) for -98 => [-100, 2]; (low, high) for 67 => [-33, 100]`. – גלעד ברקן Sep 22 '18 at 14:59
  • @גלעדברקן: I mean that "the maximum range it can have" is inherently going to be biased toward zero, since that range will include 0 for all values of `n`, will include 1 for 99.5% of such values, will include 2 for 99% of them, etc., but will include 100 for only 50% of the values. – ruakh Sep 22 '18 at 20:48
0

This is my solution, basically you do a for loop and start adding elements.

Whenever you add an element, just add the same element * -1

You will end up with an array of elements with sum 0.

function arrayOfSumZero(N) {
    let sum = 0;
    let i = N % 2 === 0 ? 0 : 1;
    let output = [];

    for (i; i < N; i++) {
        if (output.length < N) {
            sum += i;
            output.push(i);
        }

        if (sum > 0) {
            sum += i * -1;
            output.push(i * -1);
        }
    }

    return output;
}
Willy
  • 3,506
  • 3
  • 22
  • 35