Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to generate a n-sized random float array that sums up to 0.0?

Tags:

c++

arrays

c

random

Consider that I need a n-sized vector where each element is defined between [-1,1]. The element a[i] is a float generated by -1 + 2*rand(). I need a elegant way to ensure that the sum of the elements of my array is equal to zero.

I've found two possible solutions:

The first one is this matlab function https://www.mathworks.com/matlabcentral/fileexchange/9700-random-vectors-with-fixed-sum. It has also a implementation in R, however it is too much work to implement it on C, since this function is used for a 2d array.

The second one is provided in this thread here: Generate random values with fixed sum in C++. Essentially, the idea is to generate n numbers with a normal distribution then normalize them to with my sum. (I have implemented it using python bellow) for a vector with sum up to 1.0. It works for every sum value except for zero.

import random as rd

mySum = 1;
randomVector = []
randomSum = 0

for i in range(7):
    randomNumber = -1 + 2*rd.random()
    randomVector.append(randomNumber)
    randomSum  += randomNumber

coef = mySum/randomSum
myNewList = [j * coef for j in randomVector]
newsum = sum(myNewList)

So, is there a way to do that using C or C++? If you know a already implemented function it would be awesome. Thanks.

like image 528
Thales Carl Avatar asked Oct 16 '19 19:10

Thales Carl


Video Answer


1 Answers

I figured out a solution to your problem. This is not perfect since its randomness is limited by the range requirement.

The strategy is:

  1. Define a function able to generate a random float in a customizable range. No need to reinvent the wheel: I borrowed it from https://stackoverflow.com/a/44105089/11336762
  2. Malloc array (I omit pointer check in my example) and initialize the seed. In my example I just used current time but it can be improved
  3. For every element to be generated, pre-calculate random range. Given the i-th sum, make sure that the next sum is NEVER out of range: if the sum is positive, the range needs to be (-1,1-sum); if it is negative it the range needs to be (-1-sum,1)
  4. Do this until (n-1)th element. Last element must be directly assigned as the sum with the sign changed.
    #include<stdio.h>
    #include<stdlib.h>
    #include<time.h>

    float float_rand( float min, float max )
    {
        float scale = rand() / (float) RAND_MAX; /* [0, 1.0] */
        return min + scale * ( max - min );      /* [min, max] */
    }

    void main( int argc, char *argv[] )
    {
        if( argc == 2 )
        {
            int i, n = atoi ( argv[1] );
            float *outArr = malloc( n * sizeof( float ) );
            float sum = 0;

            printf( "Input value: %d\n\n", n );

            /* Initialize seed */
            srand ( time( NULL ) );

            for( i=0; i<n-1; i++ )
            {
                /* Limit random generation range in order to make sure the next sum is  *
                 * not outside (-1,1) range.                                            */
                float min = (sum<0? -1-sum : -1);
                float max = (sum>0? 1-sum : 1);

                outArr[i] = float_rand( min, max );
                sum += outArr[i];
            }

            /* Set last array element */
            outArr[n-1] = -sum;

            /* Print results */
            sum=0;
            for( i=0; i<n; i++ )
            {
                sum += outArr[i];
                printf( "  outArr[%d]=%f \t(sum=%f)\n", i, outArr[i], sum );
            }

            free( outArr );
        }  
        else
        {
          printf( "Only a parameter allowed (integer N)\n" );
        }
    }

I tried it, and it works also when n=1. In case of n=0 a sanity check should be added to my example.

Some output examples:

N=1:

Input value: 1

  outArr[0]=-0.000000   (sum=-0.000000)

N=4

Input value: 4

  outArr[0]=-0.804071   (sum=-0.804071)
  outArr[1]=0.810685    (sum=0.006614)
  outArr[2]=-0.353444   (sum=-0.346830)
  outArr[3]=0.346830    (sum=0.000000)

N=8:

Input value: 8

  outArr[0]=-0.791314   (sum=-0.791314)
  outArr[1]=0.800182    (sum=0.008867)
  outArr[2]=-0.571293   (sum=-0.562426)
  outArr[3]=0.293300    (sum=-0.269126)
  outArr[4]=-0.082886   (sum=-0.352012)
  outArr[5]=0.818639    (sum=0.466628)
  outArr[6]=-0.301473   (sum=0.165155)
  outArr[7]=-0.165155   (sum=0.000000)
like image 105
Roberto Caboni Avatar answered Oct 12 '22 14:10

Roberto Caboni