How to get random number list, with fixed sum and size












1















How to get random number list by give size, and expectd sum, fully support



i hava a code sum-int.ts sum-float.ts internal/sum-num.ts



whai i want to do




  1. rN = radom number ( float or int ) between min ~ max

  2. size = [r1, r2, r3, ...rN].length

  3. sum = r1 + r2 + r3 + ...rN

  4. all rN should >= min, and <= max

  5. support (unique / no-unique) values



but now there has problem




  • can't make it work when max <= 0 with float (so i disable input it)

  • can't make it work when max <= 1 with int (so i disable input it)

  • the code has logic bug, so i come ask here how to make it by js






update



thx @SeverinPappadeux for int version, and idea for float





  • coreFnRandSumInt int version


  • coreFnRandSumFloat float version




{ size: 2, sum: 5, min: 0, max: 5, n: 5, maxv: 5 }
true 0 5 [ 2, 3 ] 0 [ 2, 3 ]
{ bool: true,
ret_a: [ 2, 3 ],
a_sum: 5,
ret_b: [ 2, 3 ],
b_sum: 5 }
[ 2, 3 ] 5
----------
{ size: 6, sum: 13, min: -8, max: 15, n: 61, maxv: 23 }
false 0 61 [ 9, 8, 7, 3, 6, 28 ] -8 [ 9, 8, 7, 3, 6, 28 ]
false 1 61 [ 11, 9, 7, 4, 5, 25 ] -8 [ 11, 9, 7, 4, 5, 25 ]
true 2 13 [ 1, -1, 0, -2, 2, 13 ] -8 [ 9, 7, 8, 6, 10, 21 ]
{ bool: true,
ret_a: [ 9, 7, 8, 6, 10, 21 ],
a_sum: 61,
ret_b: [ 1, -1, 0, -2, 2, 13 ],
b_sum: 13 }
[ 1, -1, 0, -2, 2, 13 ] 13
----------
{ size: 6, sum: -13, min: -8, max: 15, n: 35, maxv: 23 }
true 0 -13 [ 0, -6, -1, -4, -7, 5 ] -8 [ 8, 2, 7, 4, 1, 13 ]
{ bool: true,
ret_a: [ 8, 2, 7, 4, 1, 13 ],
a_sum: 35,
ret_b: [ 0, -6, -1, -4, -7, 5 ],
b_sum: -13 }
[ 0, -6, -1, -4, -7, 5 ] -13
{ size: 6, sum: 0, min: -8, max: 15, n: 48, maxv: 23 }
true 0 0 [ -1, 0, -3, -2, -4, 10 ] -8 [ 7, 8, 5, 6, 4, 18 ]
{ bool: true,
ret_a: [ 7, 8, 5, 6, 4, 18 ],
a_sum: 48,
ret_b: [ -1, 0, -3, -2, -4, 10 ],
b_sum: 0 }
[ -1, 0, -3, -2, -4, 10 ] 0




/**
* not support unique, but will try make unique if can
* thx @SeverinPappadeux for int version
*
* @see https://stackoverflow.com/questions/53279807/how-to-get-random-number-list-with-fixed-sum-and-size
*/
export function coreFnRandSumInt(argv: ISumNumParameterWuthCache)
{
let {
random,
size,
sum,
min,
max,
} = argv;

let sum_1_to_size = sum_1_to_n(size);

sum = isUnset(sum) ? sum_1_to_size : sum;

expect(sum).integer();

min = isUnset(min) ? (sum > 0 ? 0 : sum) : min;
max = isUnset(max) ? Math.abs(sum) : max;

expect(min).integer();
expect(max).integer();

let n_sum = Math.abs(sum - size * min);
let maxv = max - min;

/*
console.log({
sum_1_to_size,
size,
sum,
min,
max,
n_sum,
maxv,
});
*/

if (sum > 0)
{
expect(sum).gt(min)
}

/**
* pre-check
*/
//expect(maxv, `(max - min) should > sum_1_to_size`).gte(sum_1_to_size);

/**
* probabilities
*/
let prob = get_prob(size, maxv);

expect(prob).is.array.lengthOf(size);

/**
* make rmultinom use with random.next
*/
let rmultinomFn = libRmath.Multinomial(fakeLibRmathRng(random.next)).rmultinom;

/**
* low value for speed up, but more chance fail
*/
let n_len = argv.limit || 5 || n_sum;
/**
* rebase number
*/
let n_diff: number = min;

const rmultinomCreateFn = (n_len: number) => {
return (rmultinomFn(n_len, n_sum, prob) as number)
.reduce((a, value) =>
{
let i = value.length;
let b_sum = 0;
let bool = false;
let unique_len = 0;

while(i--)
{
let v = value[i];
let n = v + n_diff;

if (value.indexOf(v) === i)
{
unique_len++;
}

if (n >= min && n <= max)
{
bool = true;
value[i] = n;

b_sum += n
}
else
{
bool = false;
break;
}
}

if (bool && b_sum === sum)
{
let item = {
value,
unique_len,
b_sum,
bool,
};

a.push(item)
}

return a
}, as {
value: number,
unique_len: number,
b_sum: number,
bool: boolean,
})
.sort((a, b) => b.unique_len - a.unique_len)
;
};

/**
* pre-make fail-back value
*/
const cache_max = 10;
let cache: number = ;

{
let len = 200;

let arr = array_unique(rmultinomCreateFn(len));

if (arr.length)
{
let i = Math.min(cache_max, arr.length);

while(i--)
{
cache.push(arr[i].value)
}

cache = array_unique(cache.map(v => v.sort()))
}

arr = undefined;

// console.log(cache);
}

/**
* try reset memory
*/
argv = undefined;

return () =>
{
let arr = rmultinomCreateFn(n_len);

let ret_b: number;
let bool_toplevel: boolean;

let c_len = cache.length;

if (arr.length)
{
ret_b = arr[0].value;
bool_toplevel = arr[0].bool;

if (bool_toplevel && c_len < cache_max)
{
cache.push(ret_b);
}
}
else if (c_len)
{
let i = UtilDistributions.randIndex(random, c_len);

ret_b = cache[i];
bool_toplevel = true;
}

if (!bool_toplevel || !ret_b)
{
throw new Error(`can't generator value by current input argv, or try set limit for high number`)
}

return ret_b;
}
}












share|improve this question

























  • What do you mean "size"? Some interval between min and max? Could you elaborate? Do you want N random integers (or floats) which sums to fixed number?

    – Severin Pappadeux
    Nov 13 '18 at 14:35











  • post the relevant part of your code here as well, not just the link to it

    – mihai
    Nov 13 '18 at 20:00











  • @mihai can't , stackoverflow block me post long code

    – bluelovers
    Nov 13 '18 at 23:49











  • @SeverinPappadeux i edited desc

    – bluelovers
    Nov 13 '18 at 23:50











  • ok, now it is better. Could you please provide examples what kind of values you want it to be for float and integer case? How many numbers, what kind of a sum to make?

    – Severin Pappadeux
    Nov 14 '18 at 0:10
















1















How to get random number list by give size, and expectd sum, fully support



i hava a code sum-int.ts sum-float.ts internal/sum-num.ts



whai i want to do




  1. rN = radom number ( float or int ) between min ~ max

  2. size = [r1, r2, r3, ...rN].length

  3. sum = r1 + r2 + r3 + ...rN

  4. all rN should >= min, and <= max

  5. support (unique / no-unique) values



but now there has problem




  • can't make it work when max <= 0 with float (so i disable input it)

  • can't make it work when max <= 1 with int (so i disable input it)

  • the code has logic bug, so i come ask here how to make it by js






update



thx @SeverinPappadeux for int version, and idea for float





  • coreFnRandSumInt int version


  • coreFnRandSumFloat float version




{ size: 2, sum: 5, min: 0, max: 5, n: 5, maxv: 5 }
true 0 5 [ 2, 3 ] 0 [ 2, 3 ]
{ bool: true,
ret_a: [ 2, 3 ],
a_sum: 5,
ret_b: [ 2, 3 ],
b_sum: 5 }
[ 2, 3 ] 5
----------
{ size: 6, sum: 13, min: -8, max: 15, n: 61, maxv: 23 }
false 0 61 [ 9, 8, 7, 3, 6, 28 ] -8 [ 9, 8, 7, 3, 6, 28 ]
false 1 61 [ 11, 9, 7, 4, 5, 25 ] -8 [ 11, 9, 7, 4, 5, 25 ]
true 2 13 [ 1, -1, 0, -2, 2, 13 ] -8 [ 9, 7, 8, 6, 10, 21 ]
{ bool: true,
ret_a: [ 9, 7, 8, 6, 10, 21 ],
a_sum: 61,
ret_b: [ 1, -1, 0, -2, 2, 13 ],
b_sum: 13 }
[ 1, -1, 0, -2, 2, 13 ] 13
----------
{ size: 6, sum: -13, min: -8, max: 15, n: 35, maxv: 23 }
true 0 -13 [ 0, -6, -1, -4, -7, 5 ] -8 [ 8, 2, 7, 4, 1, 13 ]
{ bool: true,
ret_a: [ 8, 2, 7, 4, 1, 13 ],
a_sum: 35,
ret_b: [ 0, -6, -1, -4, -7, 5 ],
b_sum: -13 }
[ 0, -6, -1, -4, -7, 5 ] -13
{ size: 6, sum: 0, min: -8, max: 15, n: 48, maxv: 23 }
true 0 0 [ -1, 0, -3, -2, -4, 10 ] -8 [ 7, 8, 5, 6, 4, 18 ]
{ bool: true,
ret_a: [ 7, 8, 5, 6, 4, 18 ],
a_sum: 48,
ret_b: [ -1, 0, -3, -2, -4, 10 ],
b_sum: 0 }
[ -1, 0, -3, -2, -4, 10 ] 0




/**
* not support unique, but will try make unique if can
* thx @SeverinPappadeux for int version
*
* @see https://stackoverflow.com/questions/53279807/how-to-get-random-number-list-with-fixed-sum-and-size
*/
export function coreFnRandSumInt(argv: ISumNumParameterWuthCache)
{
let {
random,
size,
sum,
min,
max,
} = argv;

let sum_1_to_size = sum_1_to_n(size);

sum = isUnset(sum) ? sum_1_to_size : sum;

expect(sum).integer();

min = isUnset(min) ? (sum > 0 ? 0 : sum) : min;
max = isUnset(max) ? Math.abs(sum) : max;

expect(min).integer();
expect(max).integer();

let n_sum = Math.abs(sum - size * min);
let maxv = max - min;

/*
console.log({
sum_1_to_size,
size,
sum,
min,
max,
n_sum,
maxv,
});
*/

if (sum > 0)
{
expect(sum).gt(min)
}

/**
* pre-check
*/
//expect(maxv, `(max - min) should > sum_1_to_size`).gte(sum_1_to_size);

/**
* probabilities
*/
let prob = get_prob(size, maxv);

expect(prob).is.array.lengthOf(size);

/**
* make rmultinom use with random.next
*/
let rmultinomFn = libRmath.Multinomial(fakeLibRmathRng(random.next)).rmultinom;

/**
* low value for speed up, but more chance fail
*/
let n_len = argv.limit || 5 || n_sum;
/**
* rebase number
*/
let n_diff: number = min;

const rmultinomCreateFn = (n_len: number) => {
return (rmultinomFn(n_len, n_sum, prob) as number)
.reduce((a, value) =>
{
let i = value.length;
let b_sum = 0;
let bool = false;
let unique_len = 0;

while(i--)
{
let v = value[i];
let n = v + n_diff;

if (value.indexOf(v) === i)
{
unique_len++;
}

if (n >= min && n <= max)
{
bool = true;
value[i] = n;

b_sum += n
}
else
{
bool = false;
break;
}
}

if (bool && b_sum === sum)
{
let item = {
value,
unique_len,
b_sum,
bool,
};

a.push(item)
}

return a
}, as {
value: number,
unique_len: number,
b_sum: number,
bool: boolean,
})
.sort((a, b) => b.unique_len - a.unique_len)
;
};

/**
* pre-make fail-back value
*/
const cache_max = 10;
let cache: number = ;

{
let len = 200;

let arr = array_unique(rmultinomCreateFn(len));

if (arr.length)
{
let i = Math.min(cache_max, arr.length);

while(i--)
{
cache.push(arr[i].value)
}

cache = array_unique(cache.map(v => v.sort()))
}

arr = undefined;

// console.log(cache);
}

/**
* try reset memory
*/
argv = undefined;

return () =>
{
let arr = rmultinomCreateFn(n_len);

let ret_b: number;
let bool_toplevel: boolean;

let c_len = cache.length;

if (arr.length)
{
ret_b = arr[0].value;
bool_toplevel = arr[0].bool;

if (bool_toplevel && c_len < cache_max)
{
cache.push(ret_b);
}
}
else if (c_len)
{
let i = UtilDistributions.randIndex(random, c_len);

ret_b = cache[i];
bool_toplevel = true;
}

if (!bool_toplevel || !ret_b)
{
throw new Error(`can't generator value by current input argv, or try set limit for high number`)
}

return ret_b;
}
}












share|improve this question

























  • What do you mean "size"? Some interval between min and max? Could you elaborate? Do you want N random integers (or floats) which sums to fixed number?

    – Severin Pappadeux
    Nov 13 '18 at 14:35











  • post the relevant part of your code here as well, not just the link to it

    – mihai
    Nov 13 '18 at 20:00











  • @mihai can't , stackoverflow block me post long code

    – bluelovers
    Nov 13 '18 at 23:49











  • @SeverinPappadeux i edited desc

    – bluelovers
    Nov 13 '18 at 23:50











  • ok, now it is better. Could you please provide examples what kind of values you want it to be for float and integer case? How many numbers, what kind of a sum to make?

    – Severin Pappadeux
    Nov 14 '18 at 0:10














1












1








1


1






How to get random number list by give size, and expectd sum, fully support



i hava a code sum-int.ts sum-float.ts internal/sum-num.ts



whai i want to do




  1. rN = radom number ( float or int ) between min ~ max

  2. size = [r1, r2, r3, ...rN].length

  3. sum = r1 + r2 + r3 + ...rN

  4. all rN should >= min, and <= max

  5. support (unique / no-unique) values



but now there has problem




  • can't make it work when max <= 0 with float (so i disable input it)

  • can't make it work when max <= 1 with int (so i disable input it)

  • the code has logic bug, so i come ask here how to make it by js






update



thx @SeverinPappadeux for int version, and idea for float





  • coreFnRandSumInt int version


  • coreFnRandSumFloat float version




{ size: 2, sum: 5, min: 0, max: 5, n: 5, maxv: 5 }
true 0 5 [ 2, 3 ] 0 [ 2, 3 ]
{ bool: true,
ret_a: [ 2, 3 ],
a_sum: 5,
ret_b: [ 2, 3 ],
b_sum: 5 }
[ 2, 3 ] 5
----------
{ size: 6, sum: 13, min: -8, max: 15, n: 61, maxv: 23 }
false 0 61 [ 9, 8, 7, 3, 6, 28 ] -8 [ 9, 8, 7, 3, 6, 28 ]
false 1 61 [ 11, 9, 7, 4, 5, 25 ] -8 [ 11, 9, 7, 4, 5, 25 ]
true 2 13 [ 1, -1, 0, -2, 2, 13 ] -8 [ 9, 7, 8, 6, 10, 21 ]
{ bool: true,
ret_a: [ 9, 7, 8, 6, 10, 21 ],
a_sum: 61,
ret_b: [ 1, -1, 0, -2, 2, 13 ],
b_sum: 13 }
[ 1, -1, 0, -2, 2, 13 ] 13
----------
{ size: 6, sum: -13, min: -8, max: 15, n: 35, maxv: 23 }
true 0 -13 [ 0, -6, -1, -4, -7, 5 ] -8 [ 8, 2, 7, 4, 1, 13 ]
{ bool: true,
ret_a: [ 8, 2, 7, 4, 1, 13 ],
a_sum: 35,
ret_b: [ 0, -6, -1, -4, -7, 5 ],
b_sum: -13 }
[ 0, -6, -1, -4, -7, 5 ] -13
{ size: 6, sum: 0, min: -8, max: 15, n: 48, maxv: 23 }
true 0 0 [ -1, 0, -3, -2, -4, 10 ] -8 [ 7, 8, 5, 6, 4, 18 ]
{ bool: true,
ret_a: [ 7, 8, 5, 6, 4, 18 ],
a_sum: 48,
ret_b: [ -1, 0, -3, -2, -4, 10 ],
b_sum: 0 }
[ -1, 0, -3, -2, -4, 10 ] 0




/**
* not support unique, but will try make unique if can
* thx @SeverinPappadeux for int version
*
* @see https://stackoverflow.com/questions/53279807/how-to-get-random-number-list-with-fixed-sum-and-size
*/
export function coreFnRandSumInt(argv: ISumNumParameterWuthCache)
{
let {
random,
size,
sum,
min,
max,
} = argv;

let sum_1_to_size = sum_1_to_n(size);

sum = isUnset(sum) ? sum_1_to_size : sum;

expect(sum).integer();

min = isUnset(min) ? (sum > 0 ? 0 : sum) : min;
max = isUnset(max) ? Math.abs(sum) : max;

expect(min).integer();
expect(max).integer();

let n_sum = Math.abs(sum - size * min);
let maxv = max - min;

/*
console.log({
sum_1_to_size,
size,
sum,
min,
max,
n_sum,
maxv,
});
*/

if (sum > 0)
{
expect(sum).gt(min)
}

/**
* pre-check
*/
//expect(maxv, `(max - min) should > sum_1_to_size`).gte(sum_1_to_size);

/**
* probabilities
*/
let prob = get_prob(size, maxv);

expect(prob).is.array.lengthOf(size);

/**
* make rmultinom use with random.next
*/
let rmultinomFn = libRmath.Multinomial(fakeLibRmathRng(random.next)).rmultinom;

/**
* low value for speed up, but more chance fail
*/
let n_len = argv.limit || 5 || n_sum;
/**
* rebase number
*/
let n_diff: number = min;

const rmultinomCreateFn = (n_len: number) => {
return (rmultinomFn(n_len, n_sum, prob) as number)
.reduce((a, value) =>
{
let i = value.length;
let b_sum = 0;
let bool = false;
let unique_len = 0;

while(i--)
{
let v = value[i];
let n = v + n_diff;

if (value.indexOf(v) === i)
{
unique_len++;
}

if (n >= min && n <= max)
{
bool = true;
value[i] = n;

b_sum += n
}
else
{
bool = false;
break;
}
}

if (bool && b_sum === sum)
{
let item = {
value,
unique_len,
b_sum,
bool,
};

a.push(item)
}

return a
}, as {
value: number,
unique_len: number,
b_sum: number,
bool: boolean,
})
.sort((a, b) => b.unique_len - a.unique_len)
;
};

/**
* pre-make fail-back value
*/
const cache_max = 10;
let cache: number = ;

{
let len = 200;

let arr = array_unique(rmultinomCreateFn(len));

if (arr.length)
{
let i = Math.min(cache_max, arr.length);

while(i--)
{
cache.push(arr[i].value)
}

cache = array_unique(cache.map(v => v.sort()))
}

arr = undefined;

// console.log(cache);
}

/**
* try reset memory
*/
argv = undefined;

return () =>
{
let arr = rmultinomCreateFn(n_len);

let ret_b: number;
let bool_toplevel: boolean;

let c_len = cache.length;

if (arr.length)
{
ret_b = arr[0].value;
bool_toplevel = arr[0].bool;

if (bool_toplevel && c_len < cache_max)
{
cache.push(ret_b);
}
}
else if (c_len)
{
let i = UtilDistributions.randIndex(random, c_len);

ret_b = cache[i];
bool_toplevel = true;
}

if (!bool_toplevel || !ret_b)
{
throw new Error(`can't generator value by current input argv, or try set limit for high number`)
}

return ret_b;
}
}












share|improve this question
















How to get random number list by give size, and expectd sum, fully support



i hava a code sum-int.ts sum-float.ts internal/sum-num.ts



whai i want to do




  1. rN = radom number ( float or int ) between min ~ max

  2. size = [r1, r2, r3, ...rN].length

  3. sum = r1 + r2 + r3 + ...rN

  4. all rN should >= min, and <= max

  5. support (unique / no-unique) values



but now there has problem




  • can't make it work when max <= 0 with float (so i disable input it)

  • can't make it work when max <= 1 with int (so i disable input it)

  • the code has logic bug, so i come ask here how to make it by js






update



thx @SeverinPappadeux for int version, and idea for float





  • coreFnRandSumInt int version


  • coreFnRandSumFloat float version




{ size: 2, sum: 5, min: 0, max: 5, n: 5, maxv: 5 }
true 0 5 [ 2, 3 ] 0 [ 2, 3 ]
{ bool: true,
ret_a: [ 2, 3 ],
a_sum: 5,
ret_b: [ 2, 3 ],
b_sum: 5 }
[ 2, 3 ] 5
----------
{ size: 6, sum: 13, min: -8, max: 15, n: 61, maxv: 23 }
false 0 61 [ 9, 8, 7, 3, 6, 28 ] -8 [ 9, 8, 7, 3, 6, 28 ]
false 1 61 [ 11, 9, 7, 4, 5, 25 ] -8 [ 11, 9, 7, 4, 5, 25 ]
true 2 13 [ 1, -1, 0, -2, 2, 13 ] -8 [ 9, 7, 8, 6, 10, 21 ]
{ bool: true,
ret_a: [ 9, 7, 8, 6, 10, 21 ],
a_sum: 61,
ret_b: [ 1, -1, 0, -2, 2, 13 ],
b_sum: 13 }
[ 1, -1, 0, -2, 2, 13 ] 13
----------
{ size: 6, sum: -13, min: -8, max: 15, n: 35, maxv: 23 }
true 0 -13 [ 0, -6, -1, -4, -7, 5 ] -8 [ 8, 2, 7, 4, 1, 13 ]
{ bool: true,
ret_a: [ 8, 2, 7, 4, 1, 13 ],
a_sum: 35,
ret_b: [ 0, -6, -1, -4, -7, 5 ],
b_sum: -13 }
[ 0, -6, -1, -4, -7, 5 ] -13
{ size: 6, sum: 0, min: -8, max: 15, n: 48, maxv: 23 }
true 0 0 [ -1, 0, -3, -2, -4, 10 ] -8 [ 7, 8, 5, 6, 4, 18 ]
{ bool: true,
ret_a: [ 7, 8, 5, 6, 4, 18 ],
a_sum: 48,
ret_b: [ -1, 0, -3, -2, -4, 10 ],
b_sum: 0 }
[ -1, 0, -3, -2, -4, 10 ] 0




/**
* not support unique, but will try make unique if can
* thx @SeverinPappadeux for int version
*
* @see https://stackoverflow.com/questions/53279807/how-to-get-random-number-list-with-fixed-sum-and-size
*/
export function coreFnRandSumInt(argv: ISumNumParameterWuthCache)
{
let {
random,
size,
sum,
min,
max,
} = argv;

let sum_1_to_size = sum_1_to_n(size);

sum = isUnset(sum) ? sum_1_to_size : sum;

expect(sum).integer();

min = isUnset(min) ? (sum > 0 ? 0 : sum) : min;
max = isUnset(max) ? Math.abs(sum) : max;

expect(min).integer();
expect(max).integer();

let n_sum = Math.abs(sum - size * min);
let maxv = max - min;

/*
console.log({
sum_1_to_size,
size,
sum,
min,
max,
n_sum,
maxv,
});
*/

if (sum > 0)
{
expect(sum).gt(min)
}

/**
* pre-check
*/
//expect(maxv, `(max - min) should > sum_1_to_size`).gte(sum_1_to_size);

/**
* probabilities
*/
let prob = get_prob(size, maxv);

expect(prob).is.array.lengthOf(size);

/**
* make rmultinom use with random.next
*/
let rmultinomFn = libRmath.Multinomial(fakeLibRmathRng(random.next)).rmultinom;

/**
* low value for speed up, but more chance fail
*/
let n_len = argv.limit || 5 || n_sum;
/**
* rebase number
*/
let n_diff: number = min;

const rmultinomCreateFn = (n_len: number) => {
return (rmultinomFn(n_len, n_sum, prob) as number)
.reduce((a, value) =>
{
let i = value.length;
let b_sum = 0;
let bool = false;
let unique_len = 0;

while(i--)
{
let v = value[i];
let n = v + n_diff;

if (value.indexOf(v) === i)
{
unique_len++;
}

if (n >= min && n <= max)
{
bool = true;
value[i] = n;

b_sum += n
}
else
{
bool = false;
break;
}
}

if (bool && b_sum === sum)
{
let item = {
value,
unique_len,
b_sum,
bool,
};

a.push(item)
}

return a
}, as {
value: number,
unique_len: number,
b_sum: number,
bool: boolean,
})
.sort((a, b) => b.unique_len - a.unique_len)
;
};

/**
* pre-make fail-back value
*/
const cache_max = 10;
let cache: number = ;

{
let len = 200;

let arr = array_unique(rmultinomCreateFn(len));

if (arr.length)
{
let i = Math.min(cache_max, arr.length);

while(i--)
{
cache.push(arr[i].value)
}

cache = array_unique(cache.map(v => v.sort()))
}

arr = undefined;

// console.log(cache);
}

/**
* try reset memory
*/
argv = undefined;

return () =>
{
let arr = rmultinomCreateFn(n_len);

let ret_b: number;
let bool_toplevel: boolean;

let c_len = cache.length;

if (arr.length)
{
ret_b = arr[0].value;
bool_toplevel = arr[0].bool;

if (bool_toplevel && c_len < cache_max)
{
cache.push(ret_b);
}
}
else if (c_len)
{
let i = UtilDistributions.randIndex(random, c_len);

ret_b = cache[i];
bool_toplevel = true;
}

if (!bool_toplevel || !ret_b)
{
throw new Error(`can't generator value by current input argv, or try set limit for high number`)
}

return ret_b;
}
}









javascript node.js typescript math random






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Nov 21 '18 at 17:59







bluelovers

















asked Nov 13 '18 at 11:15









blueloversbluelovers

1608




1608













  • What do you mean "size"? Some interval between min and max? Could you elaborate? Do you want N random integers (or floats) which sums to fixed number?

    – Severin Pappadeux
    Nov 13 '18 at 14:35











  • post the relevant part of your code here as well, not just the link to it

    – mihai
    Nov 13 '18 at 20:00











  • @mihai can't , stackoverflow block me post long code

    – bluelovers
    Nov 13 '18 at 23:49











  • @SeverinPappadeux i edited desc

    – bluelovers
    Nov 13 '18 at 23:50











  • ok, now it is better. Could you please provide examples what kind of values you want it to be for float and integer case? How many numbers, what kind of a sum to make?

    – Severin Pappadeux
    Nov 14 '18 at 0:10



















  • What do you mean "size"? Some interval between min and max? Could you elaborate? Do you want N random integers (or floats) which sums to fixed number?

    – Severin Pappadeux
    Nov 13 '18 at 14:35











  • post the relevant part of your code here as well, not just the link to it

    – mihai
    Nov 13 '18 at 20:00











  • @mihai can't , stackoverflow block me post long code

    – bluelovers
    Nov 13 '18 at 23:49











  • @SeverinPappadeux i edited desc

    – bluelovers
    Nov 13 '18 at 23:50











  • ok, now it is better. Could you please provide examples what kind of values you want it to be for float and integer case? How many numbers, what kind of a sum to make?

    – Severin Pappadeux
    Nov 14 '18 at 0:10

















What do you mean "size"? Some interval between min and max? Could you elaborate? Do you want N random integers (or floats) which sums to fixed number?

– Severin Pappadeux
Nov 13 '18 at 14:35





What do you mean "size"? Some interval between min and max? Could you elaborate? Do you want N random integers (or floats) which sums to fixed number?

– Severin Pappadeux
Nov 13 '18 at 14:35













post the relevant part of your code here as well, not just the link to it

– mihai
Nov 13 '18 at 20:00





post the relevant part of your code here as well, not just the link to it

– mihai
Nov 13 '18 at 20:00













@mihai can't , stackoverflow block me post long code

– bluelovers
Nov 13 '18 at 23:49





@mihai can't , stackoverflow block me post long code

– bluelovers
Nov 13 '18 at 23:49













@SeverinPappadeux i edited desc

– bluelovers
Nov 13 '18 at 23:50





@SeverinPappadeux i edited desc

– bluelovers
Nov 13 '18 at 23:50













ok, now it is better. Could you please provide examples what kind of values you want it to be for float and integer case? How many numbers, what kind of a sum to make?

– Severin Pappadeux
Nov 14 '18 at 0:10





ok, now it is better. Could you please provide examples what kind of values you want it to be for float and integer case? How many numbers, what kind of a sum to make?

– Severin Pappadeux
Nov 14 '18 at 0:10












1 Answer
1






active

oldest

votes


















2














Ok, here it is. Let's start with integer problem. Simplest way to proceed is to use statistical distribution which is naturally produced set of numbers summed to a fixed value. And there is such distribution - Multinomial distribution. It has fixed sum equal to n, it provides sampled values from 0 to n. Because requirement is for sampling interval to be arbitrary, first we will shift interval to have minimum at 0, sample and then shift it back. Note, that sampled values might be higher than desired max, thus we have to use rejection technique, where any sample above max will be reject and next attempt will be sampled.



We use multinomial sampling from Python/Numpy. Along with rejection you could add test for uniqueness as well. Code, python 3.7



import numpy as np

def sample_sum_interval(n: int, p, maxv: int):
while True:
q = np.random.multinomial(n, p, size=1)[0]
v = np.where(q > maxv)
if len(v[0]) == 0: # if len(v) > 0, some values are outside the range, reject
# test on unique if len(np.unique(q)) == len(q)
return q
return None

k = 6
min = -8
max = 13
sum = 13

n = sum - k*min # redefined sum
maxv = max - min # redefined max, min would be 0
p = np.full((k), np.float64(1.0)/np.float64(k), dtype=np.float64) # probabilities

q = sample_sum_interval(n, p, maxv) + min # back to original interval
print(q)
print(np.sum(q))
print(np.mean(q))

q = sample_sum_interval(n, p, maxv) + min
print(q)
print(np.sum(q))
print(np.mean(q))

q = sample_sum_interval(n, p, maxv) + min
print(q)
print(np.sum(q))
print(np.mean(q))


Output



[ 5  0 -2  3  3  4]
13
2.1666666666666665
[ 3 3 -1 2 1 5]
13
2.1666666666666665
[-4 0 0 3 10 4]
13
2.1666666666666665


In order to translate it to Javascript, you would need either multinomial sampling, or binomial one, from binomial it is easy to get to multinomial.



UPDATE



ok, here is output when I don't add min to the result, sum would be 61 (13+6*8),
range [0...21]



[11  7  6  8  9 20]
61
10.166666666666666
[ 5 10 14 13 14 5]
61
10.166666666666666
[ 9 12 7 15 7 11]
61
10.166666666666666


Apparently, there is a Javascript library with multinomial sampling, which is modeled after R,
thank you @bluelovers



It should be called in the loop like this:



v = rmultinom(1, n, p);


and then v should be checked to be within range [0...maxv] and accepted or rejected if outside.



UPDATE II



Let me quickly (sorry, really have no time, will get to it tomorrow) describethe idea how I would do it for floats. There is similar distribution with generated bunch of numbers in the [0...1] range called Dirichlet distribution, and it always sums to fixed value of 1. In Python/Numpy it is https://docs.scipy.org/doc/numpy-1.15.1/reference/generated/numpy.random.dirichlet.html.



Suppose I sampled n numbers di from Dirichlet and then map them to [min...max] interval:



xi = min + di*(max-min)



Then I sum them all, using property that all di sums to 1:



Sum = n*min + (max - min) = (n-1)*min + max



If Sum is fixed, then it mean we have to redefine maximum value - lets call it sampling maxs.



So sampling procedure would be as following - sample n [0...1] numbers from Dirichlet, map them to [min...maxs] interval, and check if any of those numbers are below desired max (original, not redefined). If it is, you accept, otherwise reject, like in integer case.



Code below



import numpy as np

def my_dirichlet(n: int):
"""
This is equivalent to numpy.random.dirichlet when all alphas are equal to 1
"""
q = np.random.exponential(scale = np.float64(1.0), size=n)
norm = 1.0/np.sum(q)
return norm * q

def sample_sum_interval(n: int, summa: np.float64, minv: np.float64, maxv: np.float64):
maxs = summa - np.float64(n-1)*minv # redefine maximum value of the interval is sum is fixed
alpha = np.full(n, np.float64(1.0), dtype=np.float64)

while True:
q = my_dirichlet(n) # q = np.random.dirichlet(alpha)
q = minv + q*(maxs - minv) # we map it to [minv...maxs]
v = np.where(q > maxv) # but we need it in the [minv...maxv], so accept or reject test
if len(v[0]) == 0: # if len(v) > 0, some values are outside the range, reject, next sample
return q
return None

n = 5
min = np.float64(-2.0)
max = np.float64(3.0)
sum = np.float64(1.0)

q = sample_sum_interval(n, sum, min, max)
print(q)
print(np.sum(q))

q = sample_sum_interval(n, sum, min, max)
print(q)
print(np.sum(q))

q = sample_sum_interval(n, sum, min, max)
print(q)
print(np.sum(q))


I've put standard NumPy Dirichlet sampling as well as custom Dirichlet sampling. Apparently, libRmath.js has exponential distribution sampling, but no Dirichlet, but it could be replaced with user-define code and exponentials. Remember, NumPy operates on vectors with single operator, loops are implicit.



Output:



[-0.57390094 -1.80924001  0.47630282  0.80008638  2.10675174]
1.0000000000000013
[-1.12192892 1.18503129 0.97525135 0.69175429 -0.73010801]
0.9999999999999987
[-0.34803521 0.36499743 -1.165332 0.9433809 1.20498888]
0.9999999999999991





share|improve this answer


























  • hmm look like this is numpy.random.multinomial made it lol

    – bluelovers
    Nov 18 '18 at 17:05











  • @bluelovers well, you have to know multinomial is applicable here. If Javascript could use C library, calling, say, GNU GSL would be simplest approach.

    – Severin Pappadeux
    Nov 18 '18 at 17:52











  • i found lib-r-math.js can do multinomial

    – bluelovers
    Nov 18 '18 at 19:14











  • can u print value of sample_sum_interval(n, p, maxv), before it + min

    – bluelovers
    Nov 18 '18 at 19:38











  • @bluelovers please check update

    – Severin Pappadeux
    Nov 19 '18 at 2:03











Your Answer






StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");

StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});

function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});


}
});














draft saved

draft discarded


















StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53279807%2fhow-to-get-random-number-list-with-fixed-sum-and-size%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown

























1 Answer
1






active

oldest

votes








1 Answer
1






active

oldest

votes









active

oldest

votes






active

oldest

votes









2














Ok, here it is. Let's start with integer problem. Simplest way to proceed is to use statistical distribution which is naturally produced set of numbers summed to a fixed value. And there is such distribution - Multinomial distribution. It has fixed sum equal to n, it provides sampled values from 0 to n. Because requirement is for sampling interval to be arbitrary, first we will shift interval to have minimum at 0, sample and then shift it back. Note, that sampled values might be higher than desired max, thus we have to use rejection technique, where any sample above max will be reject and next attempt will be sampled.



We use multinomial sampling from Python/Numpy. Along with rejection you could add test for uniqueness as well. Code, python 3.7



import numpy as np

def sample_sum_interval(n: int, p, maxv: int):
while True:
q = np.random.multinomial(n, p, size=1)[0]
v = np.where(q > maxv)
if len(v[0]) == 0: # if len(v) > 0, some values are outside the range, reject
# test on unique if len(np.unique(q)) == len(q)
return q
return None

k = 6
min = -8
max = 13
sum = 13

n = sum - k*min # redefined sum
maxv = max - min # redefined max, min would be 0
p = np.full((k), np.float64(1.0)/np.float64(k), dtype=np.float64) # probabilities

q = sample_sum_interval(n, p, maxv) + min # back to original interval
print(q)
print(np.sum(q))
print(np.mean(q))

q = sample_sum_interval(n, p, maxv) + min
print(q)
print(np.sum(q))
print(np.mean(q))

q = sample_sum_interval(n, p, maxv) + min
print(q)
print(np.sum(q))
print(np.mean(q))


Output



[ 5  0 -2  3  3  4]
13
2.1666666666666665
[ 3 3 -1 2 1 5]
13
2.1666666666666665
[-4 0 0 3 10 4]
13
2.1666666666666665


In order to translate it to Javascript, you would need either multinomial sampling, or binomial one, from binomial it is easy to get to multinomial.



UPDATE



ok, here is output when I don't add min to the result, sum would be 61 (13+6*8),
range [0...21]



[11  7  6  8  9 20]
61
10.166666666666666
[ 5 10 14 13 14 5]
61
10.166666666666666
[ 9 12 7 15 7 11]
61
10.166666666666666


Apparently, there is a Javascript library with multinomial sampling, which is modeled after R,
thank you @bluelovers



It should be called in the loop like this:



v = rmultinom(1, n, p);


and then v should be checked to be within range [0...maxv] and accepted or rejected if outside.



UPDATE II



Let me quickly (sorry, really have no time, will get to it tomorrow) describethe idea how I would do it for floats. There is similar distribution with generated bunch of numbers in the [0...1] range called Dirichlet distribution, and it always sums to fixed value of 1. In Python/Numpy it is https://docs.scipy.org/doc/numpy-1.15.1/reference/generated/numpy.random.dirichlet.html.



Suppose I sampled n numbers di from Dirichlet and then map them to [min...max] interval:



xi = min + di*(max-min)



Then I sum them all, using property that all di sums to 1:



Sum = n*min + (max - min) = (n-1)*min + max



If Sum is fixed, then it mean we have to redefine maximum value - lets call it sampling maxs.



So sampling procedure would be as following - sample n [0...1] numbers from Dirichlet, map them to [min...maxs] interval, and check if any of those numbers are below desired max (original, not redefined). If it is, you accept, otherwise reject, like in integer case.



Code below



import numpy as np

def my_dirichlet(n: int):
"""
This is equivalent to numpy.random.dirichlet when all alphas are equal to 1
"""
q = np.random.exponential(scale = np.float64(1.0), size=n)
norm = 1.0/np.sum(q)
return norm * q

def sample_sum_interval(n: int, summa: np.float64, minv: np.float64, maxv: np.float64):
maxs = summa - np.float64(n-1)*minv # redefine maximum value of the interval is sum is fixed
alpha = np.full(n, np.float64(1.0), dtype=np.float64)

while True:
q = my_dirichlet(n) # q = np.random.dirichlet(alpha)
q = minv + q*(maxs - minv) # we map it to [minv...maxs]
v = np.where(q > maxv) # but we need it in the [minv...maxv], so accept or reject test
if len(v[0]) == 0: # if len(v) > 0, some values are outside the range, reject, next sample
return q
return None

n = 5
min = np.float64(-2.0)
max = np.float64(3.0)
sum = np.float64(1.0)

q = sample_sum_interval(n, sum, min, max)
print(q)
print(np.sum(q))

q = sample_sum_interval(n, sum, min, max)
print(q)
print(np.sum(q))

q = sample_sum_interval(n, sum, min, max)
print(q)
print(np.sum(q))


I've put standard NumPy Dirichlet sampling as well as custom Dirichlet sampling. Apparently, libRmath.js has exponential distribution sampling, but no Dirichlet, but it could be replaced with user-define code and exponentials. Remember, NumPy operates on vectors with single operator, loops are implicit.



Output:



[-0.57390094 -1.80924001  0.47630282  0.80008638  2.10675174]
1.0000000000000013
[-1.12192892 1.18503129 0.97525135 0.69175429 -0.73010801]
0.9999999999999987
[-0.34803521 0.36499743 -1.165332 0.9433809 1.20498888]
0.9999999999999991





share|improve this answer


























  • hmm look like this is numpy.random.multinomial made it lol

    – bluelovers
    Nov 18 '18 at 17:05











  • @bluelovers well, you have to know multinomial is applicable here. If Javascript could use C library, calling, say, GNU GSL would be simplest approach.

    – Severin Pappadeux
    Nov 18 '18 at 17:52











  • i found lib-r-math.js can do multinomial

    – bluelovers
    Nov 18 '18 at 19:14











  • can u print value of sample_sum_interval(n, p, maxv), before it + min

    – bluelovers
    Nov 18 '18 at 19:38











  • @bluelovers please check update

    – Severin Pappadeux
    Nov 19 '18 at 2:03
















2














Ok, here it is. Let's start with integer problem. Simplest way to proceed is to use statistical distribution which is naturally produced set of numbers summed to a fixed value. And there is such distribution - Multinomial distribution. It has fixed sum equal to n, it provides sampled values from 0 to n. Because requirement is for sampling interval to be arbitrary, first we will shift interval to have minimum at 0, sample and then shift it back. Note, that sampled values might be higher than desired max, thus we have to use rejection technique, where any sample above max will be reject and next attempt will be sampled.



We use multinomial sampling from Python/Numpy. Along with rejection you could add test for uniqueness as well. Code, python 3.7



import numpy as np

def sample_sum_interval(n: int, p, maxv: int):
while True:
q = np.random.multinomial(n, p, size=1)[0]
v = np.where(q > maxv)
if len(v[0]) == 0: # if len(v) > 0, some values are outside the range, reject
# test on unique if len(np.unique(q)) == len(q)
return q
return None

k = 6
min = -8
max = 13
sum = 13

n = sum - k*min # redefined sum
maxv = max - min # redefined max, min would be 0
p = np.full((k), np.float64(1.0)/np.float64(k), dtype=np.float64) # probabilities

q = sample_sum_interval(n, p, maxv) + min # back to original interval
print(q)
print(np.sum(q))
print(np.mean(q))

q = sample_sum_interval(n, p, maxv) + min
print(q)
print(np.sum(q))
print(np.mean(q))

q = sample_sum_interval(n, p, maxv) + min
print(q)
print(np.sum(q))
print(np.mean(q))


Output



[ 5  0 -2  3  3  4]
13
2.1666666666666665
[ 3 3 -1 2 1 5]
13
2.1666666666666665
[-4 0 0 3 10 4]
13
2.1666666666666665


In order to translate it to Javascript, you would need either multinomial sampling, or binomial one, from binomial it is easy to get to multinomial.



UPDATE



ok, here is output when I don't add min to the result, sum would be 61 (13+6*8),
range [0...21]



[11  7  6  8  9 20]
61
10.166666666666666
[ 5 10 14 13 14 5]
61
10.166666666666666
[ 9 12 7 15 7 11]
61
10.166666666666666


Apparently, there is a Javascript library with multinomial sampling, which is modeled after R,
thank you @bluelovers



It should be called in the loop like this:



v = rmultinom(1, n, p);


and then v should be checked to be within range [0...maxv] and accepted or rejected if outside.



UPDATE II



Let me quickly (sorry, really have no time, will get to it tomorrow) describethe idea how I would do it for floats. There is similar distribution with generated bunch of numbers in the [0...1] range called Dirichlet distribution, and it always sums to fixed value of 1. In Python/Numpy it is https://docs.scipy.org/doc/numpy-1.15.1/reference/generated/numpy.random.dirichlet.html.



Suppose I sampled n numbers di from Dirichlet and then map them to [min...max] interval:



xi = min + di*(max-min)



Then I sum them all, using property that all di sums to 1:



Sum = n*min + (max - min) = (n-1)*min + max



If Sum is fixed, then it mean we have to redefine maximum value - lets call it sampling maxs.



So sampling procedure would be as following - sample n [0...1] numbers from Dirichlet, map them to [min...maxs] interval, and check if any of those numbers are below desired max (original, not redefined). If it is, you accept, otherwise reject, like in integer case.



Code below



import numpy as np

def my_dirichlet(n: int):
"""
This is equivalent to numpy.random.dirichlet when all alphas are equal to 1
"""
q = np.random.exponential(scale = np.float64(1.0), size=n)
norm = 1.0/np.sum(q)
return norm * q

def sample_sum_interval(n: int, summa: np.float64, minv: np.float64, maxv: np.float64):
maxs = summa - np.float64(n-1)*minv # redefine maximum value of the interval is sum is fixed
alpha = np.full(n, np.float64(1.0), dtype=np.float64)

while True:
q = my_dirichlet(n) # q = np.random.dirichlet(alpha)
q = minv + q*(maxs - minv) # we map it to [minv...maxs]
v = np.where(q > maxv) # but we need it in the [minv...maxv], so accept or reject test
if len(v[0]) == 0: # if len(v) > 0, some values are outside the range, reject, next sample
return q
return None

n = 5
min = np.float64(-2.0)
max = np.float64(3.0)
sum = np.float64(1.0)

q = sample_sum_interval(n, sum, min, max)
print(q)
print(np.sum(q))

q = sample_sum_interval(n, sum, min, max)
print(q)
print(np.sum(q))

q = sample_sum_interval(n, sum, min, max)
print(q)
print(np.sum(q))


I've put standard NumPy Dirichlet sampling as well as custom Dirichlet sampling. Apparently, libRmath.js has exponential distribution sampling, but no Dirichlet, but it could be replaced with user-define code and exponentials. Remember, NumPy operates on vectors with single operator, loops are implicit.



Output:



[-0.57390094 -1.80924001  0.47630282  0.80008638  2.10675174]
1.0000000000000013
[-1.12192892 1.18503129 0.97525135 0.69175429 -0.73010801]
0.9999999999999987
[-0.34803521 0.36499743 -1.165332 0.9433809 1.20498888]
0.9999999999999991





share|improve this answer


























  • hmm look like this is numpy.random.multinomial made it lol

    – bluelovers
    Nov 18 '18 at 17:05











  • @bluelovers well, you have to know multinomial is applicable here. If Javascript could use C library, calling, say, GNU GSL would be simplest approach.

    – Severin Pappadeux
    Nov 18 '18 at 17:52











  • i found lib-r-math.js can do multinomial

    – bluelovers
    Nov 18 '18 at 19:14











  • can u print value of sample_sum_interval(n, p, maxv), before it + min

    – bluelovers
    Nov 18 '18 at 19:38











  • @bluelovers please check update

    – Severin Pappadeux
    Nov 19 '18 at 2:03














2












2








2







Ok, here it is. Let's start with integer problem. Simplest way to proceed is to use statistical distribution which is naturally produced set of numbers summed to a fixed value. And there is such distribution - Multinomial distribution. It has fixed sum equal to n, it provides sampled values from 0 to n. Because requirement is for sampling interval to be arbitrary, first we will shift interval to have minimum at 0, sample and then shift it back. Note, that sampled values might be higher than desired max, thus we have to use rejection technique, where any sample above max will be reject and next attempt will be sampled.



We use multinomial sampling from Python/Numpy. Along with rejection you could add test for uniqueness as well. Code, python 3.7



import numpy as np

def sample_sum_interval(n: int, p, maxv: int):
while True:
q = np.random.multinomial(n, p, size=1)[0]
v = np.where(q > maxv)
if len(v[0]) == 0: # if len(v) > 0, some values are outside the range, reject
# test on unique if len(np.unique(q)) == len(q)
return q
return None

k = 6
min = -8
max = 13
sum = 13

n = sum - k*min # redefined sum
maxv = max - min # redefined max, min would be 0
p = np.full((k), np.float64(1.0)/np.float64(k), dtype=np.float64) # probabilities

q = sample_sum_interval(n, p, maxv) + min # back to original interval
print(q)
print(np.sum(q))
print(np.mean(q))

q = sample_sum_interval(n, p, maxv) + min
print(q)
print(np.sum(q))
print(np.mean(q))

q = sample_sum_interval(n, p, maxv) + min
print(q)
print(np.sum(q))
print(np.mean(q))


Output



[ 5  0 -2  3  3  4]
13
2.1666666666666665
[ 3 3 -1 2 1 5]
13
2.1666666666666665
[-4 0 0 3 10 4]
13
2.1666666666666665


In order to translate it to Javascript, you would need either multinomial sampling, or binomial one, from binomial it is easy to get to multinomial.



UPDATE



ok, here is output when I don't add min to the result, sum would be 61 (13+6*8),
range [0...21]



[11  7  6  8  9 20]
61
10.166666666666666
[ 5 10 14 13 14 5]
61
10.166666666666666
[ 9 12 7 15 7 11]
61
10.166666666666666


Apparently, there is a Javascript library with multinomial sampling, which is modeled after R,
thank you @bluelovers



It should be called in the loop like this:



v = rmultinom(1, n, p);


and then v should be checked to be within range [0...maxv] and accepted or rejected if outside.



UPDATE II



Let me quickly (sorry, really have no time, will get to it tomorrow) describethe idea how I would do it for floats. There is similar distribution with generated bunch of numbers in the [0...1] range called Dirichlet distribution, and it always sums to fixed value of 1. In Python/Numpy it is https://docs.scipy.org/doc/numpy-1.15.1/reference/generated/numpy.random.dirichlet.html.



Suppose I sampled n numbers di from Dirichlet and then map them to [min...max] interval:



xi = min + di*(max-min)



Then I sum them all, using property that all di sums to 1:



Sum = n*min + (max - min) = (n-1)*min + max



If Sum is fixed, then it mean we have to redefine maximum value - lets call it sampling maxs.



So sampling procedure would be as following - sample n [0...1] numbers from Dirichlet, map them to [min...maxs] interval, and check if any of those numbers are below desired max (original, not redefined). If it is, you accept, otherwise reject, like in integer case.



Code below



import numpy as np

def my_dirichlet(n: int):
"""
This is equivalent to numpy.random.dirichlet when all alphas are equal to 1
"""
q = np.random.exponential(scale = np.float64(1.0), size=n)
norm = 1.0/np.sum(q)
return norm * q

def sample_sum_interval(n: int, summa: np.float64, minv: np.float64, maxv: np.float64):
maxs = summa - np.float64(n-1)*minv # redefine maximum value of the interval is sum is fixed
alpha = np.full(n, np.float64(1.0), dtype=np.float64)

while True:
q = my_dirichlet(n) # q = np.random.dirichlet(alpha)
q = minv + q*(maxs - minv) # we map it to [minv...maxs]
v = np.where(q > maxv) # but we need it in the [minv...maxv], so accept or reject test
if len(v[0]) == 0: # if len(v) > 0, some values are outside the range, reject, next sample
return q
return None

n = 5
min = np.float64(-2.0)
max = np.float64(3.0)
sum = np.float64(1.0)

q = sample_sum_interval(n, sum, min, max)
print(q)
print(np.sum(q))

q = sample_sum_interval(n, sum, min, max)
print(q)
print(np.sum(q))

q = sample_sum_interval(n, sum, min, max)
print(q)
print(np.sum(q))


I've put standard NumPy Dirichlet sampling as well as custom Dirichlet sampling. Apparently, libRmath.js has exponential distribution sampling, but no Dirichlet, but it could be replaced with user-define code and exponentials. Remember, NumPy operates on vectors with single operator, loops are implicit.



Output:



[-0.57390094 -1.80924001  0.47630282  0.80008638  2.10675174]
1.0000000000000013
[-1.12192892 1.18503129 0.97525135 0.69175429 -0.73010801]
0.9999999999999987
[-0.34803521 0.36499743 -1.165332 0.9433809 1.20498888]
0.9999999999999991





share|improve this answer















Ok, here it is. Let's start with integer problem. Simplest way to proceed is to use statistical distribution which is naturally produced set of numbers summed to a fixed value. And there is such distribution - Multinomial distribution. It has fixed sum equal to n, it provides sampled values from 0 to n. Because requirement is for sampling interval to be arbitrary, first we will shift interval to have minimum at 0, sample and then shift it back. Note, that sampled values might be higher than desired max, thus we have to use rejection technique, where any sample above max will be reject and next attempt will be sampled.



We use multinomial sampling from Python/Numpy. Along with rejection you could add test for uniqueness as well. Code, python 3.7



import numpy as np

def sample_sum_interval(n: int, p, maxv: int):
while True:
q = np.random.multinomial(n, p, size=1)[0]
v = np.where(q > maxv)
if len(v[0]) == 0: # if len(v) > 0, some values are outside the range, reject
# test on unique if len(np.unique(q)) == len(q)
return q
return None

k = 6
min = -8
max = 13
sum = 13

n = sum - k*min # redefined sum
maxv = max - min # redefined max, min would be 0
p = np.full((k), np.float64(1.0)/np.float64(k), dtype=np.float64) # probabilities

q = sample_sum_interval(n, p, maxv) + min # back to original interval
print(q)
print(np.sum(q))
print(np.mean(q))

q = sample_sum_interval(n, p, maxv) + min
print(q)
print(np.sum(q))
print(np.mean(q))

q = sample_sum_interval(n, p, maxv) + min
print(q)
print(np.sum(q))
print(np.mean(q))


Output



[ 5  0 -2  3  3  4]
13
2.1666666666666665
[ 3 3 -1 2 1 5]
13
2.1666666666666665
[-4 0 0 3 10 4]
13
2.1666666666666665


In order to translate it to Javascript, you would need either multinomial sampling, or binomial one, from binomial it is easy to get to multinomial.



UPDATE



ok, here is output when I don't add min to the result, sum would be 61 (13+6*8),
range [0...21]



[11  7  6  8  9 20]
61
10.166666666666666
[ 5 10 14 13 14 5]
61
10.166666666666666
[ 9 12 7 15 7 11]
61
10.166666666666666


Apparently, there is a Javascript library with multinomial sampling, which is modeled after R,
thank you @bluelovers



It should be called in the loop like this:



v = rmultinom(1, n, p);


and then v should be checked to be within range [0...maxv] and accepted or rejected if outside.



UPDATE II



Let me quickly (sorry, really have no time, will get to it tomorrow) describethe idea how I would do it for floats. There is similar distribution with generated bunch of numbers in the [0...1] range called Dirichlet distribution, and it always sums to fixed value of 1. In Python/Numpy it is https://docs.scipy.org/doc/numpy-1.15.1/reference/generated/numpy.random.dirichlet.html.



Suppose I sampled n numbers di from Dirichlet and then map them to [min...max] interval:



xi = min + di*(max-min)



Then I sum them all, using property that all di sums to 1:



Sum = n*min + (max - min) = (n-1)*min + max



If Sum is fixed, then it mean we have to redefine maximum value - lets call it sampling maxs.



So sampling procedure would be as following - sample n [0...1] numbers from Dirichlet, map them to [min...maxs] interval, and check if any of those numbers are below desired max (original, not redefined). If it is, you accept, otherwise reject, like in integer case.



Code below



import numpy as np

def my_dirichlet(n: int):
"""
This is equivalent to numpy.random.dirichlet when all alphas are equal to 1
"""
q = np.random.exponential(scale = np.float64(1.0), size=n)
norm = 1.0/np.sum(q)
return norm * q

def sample_sum_interval(n: int, summa: np.float64, minv: np.float64, maxv: np.float64):
maxs = summa - np.float64(n-1)*minv # redefine maximum value of the interval is sum is fixed
alpha = np.full(n, np.float64(1.0), dtype=np.float64)

while True:
q = my_dirichlet(n) # q = np.random.dirichlet(alpha)
q = minv + q*(maxs - minv) # we map it to [minv...maxs]
v = np.where(q > maxv) # but we need it in the [minv...maxv], so accept or reject test
if len(v[0]) == 0: # if len(v) > 0, some values are outside the range, reject, next sample
return q
return None

n = 5
min = np.float64(-2.0)
max = np.float64(3.0)
sum = np.float64(1.0)

q = sample_sum_interval(n, sum, min, max)
print(q)
print(np.sum(q))

q = sample_sum_interval(n, sum, min, max)
print(q)
print(np.sum(q))

q = sample_sum_interval(n, sum, min, max)
print(q)
print(np.sum(q))


I've put standard NumPy Dirichlet sampling as well as custom Dirichlet sampling. Apparently, libRmath.js has exponential distribution sampling, but no Dirichlet, but it could be replaced with user-define code and exponentials. Remember, NumPy operates on vectors with single operator, loops are implicit.



Output:



[-0.57390094 -1.80924001  0.47630282  0.80008638  2.10675174]
1.0000000000000013
[-1.12192892 1.18503129 0.97525135 0.69175429 -0.73010801]
0.9999999999999987
[-0.34803521 0.36499743 -1.165332 0.9433809 1.20498888]
0.9999999999999991






share|improve this answer














share|improve this answer



share|improve this answer








edited Nov 23 '18 at 18:10

























answered Nov 17 '18 at 20:50









Severin PappadeuxSeverin Pappadeux

9,45621432




9,45621432













  • hmm look like this is numpy.random.multinomial made it lol

    – bluelovers
    Nov 18 '18 at 17:05











  • @bluelovers well, you have to know multinomial is applicable here. If Javascript could use C library, calling, say, GNU GSL would be simplest approach.

    – Severin Pappadeux
    Nov 18 '18 at 17:52











  • i found lib-r-math.js can do multinomial

    – bluelovers
    Nov 18 '18 at 19:14











  • can u print value of sample_sum_interval(n, p, maxv), before it + min

    – bluelovers
    Nov 18 '18 at 19:38











  • @bluelovers please check update

    – Severin Pappadeux
    Nov 19 '18 at 2:03



















  • hmm look like this is numpy.random.multinomial made it lol

    – bluelovers
    Nov 18 '18 at 17:05











  • @bluelovers well, you have to know multinomial is applicable here. If Javascript could use C library, calling, say, GNU GSL would be simplest approach.

    – Severin Pappadeux
    Nov 18 '18 at 17:52











  • i found lib-r-math.js can do multinomial

    – bluelovers
    Nov 18 '18 at 19:14











  • can u print value of sample_sum_interval(n, p, maxv), before it + min

    – bluelovers
    Nov 18 '18 at 19:38











  • @bluelovers please check update

    – Severin Pappadeux
    Nov 19 '18 at 2:03

















hmm look like this is numpy.random.multinomial made it lol

– bluelovers
Nov 18 '18 at 17:05





hmm look like this is numpy.random.multinomial made it lol

– bluelovers
Nov 18 '18 at 17:05













@bluelovers well, you have to know multinomial is applicable here. If Javascript could use C library, calling, say, GNU GSL would be simplest approach.

– Severin Pappadeux
Nov 18 '18 at 17:52





@bluelovers well, you have to know multinomial is applicable here. If Javascript could use C library, calling, say, GNU GSL would be simplest approach.

– Severin Pappadeux
Nov 18 '18 at 17:52













i found lib-r-math.js can do multinomial

– bluelovers
Nov 18 '18 at 19:14





i found lib-r-math.js can do multinomial

– bluelovers
Nov 18 '18 at 19:14













can u print value of sample_sum_interval(n, p, maxv), before it + min

– bluelovers
Nov 18 '18 at 19:38





can u print value of sample_sum_interval(n, p, maxv), before it + min

– bluelovers
Nov 18 '18 at 19:38













@bluelovers please check update

– Severin Pappadeux
Nov 19 '18 at 2:03





@bluelovers please check update

– Severin Pappadeux
Nov 19 '18 at 2:03


















draft saved

draft discarded




















































Thanks for contributing an answer to Stack Overflow!


  • Please be sure to answer the question. Provide details and share your research!

But avoid



  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.


To learn more, see our tips on writing great answers.




draft saved


draft discarded














StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53279807%2fhow-to-get-random-number-list-with-fixed-sum-and-size%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown





















































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown

































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown







Popular posts from this blog

404 Error Contact Form 7 ajax form submitting

How to know if a Active Directory user can login interactively

Refactoring coordinates for Minecraft Pi buildings written in Python