How to get random number list, with fixed sum and size
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
- rN = radom number ( float or int ) between min ~ max
- size = [r1, r2, r3, ...rN].length
- sum = r1 + r2 + r3 + ...rN
- all rN should >= min, and <= max
- 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
|
show 5 more comments
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
- rN = radom number ( float or int ) between min ~ max
- size = [r1, r2, r3, ...rN].length
- sum = r1 + r2 + r3 + ...rN
- all rN should >= min, and <= max
- 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
What do you mean "size"? Some interval between min and max? Could you elaborate? Do you wantN
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
|
show 5 more comments
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
- rN = radom number ( float or int ) between min ~ max
- size = [r1, r2, r3, ...rN].length
- sum = r1 + r2 + r3 + ...rN
- all rN should >= min, and <= max
- 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
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
- rN = radom number ( float or int ) between min ~ max
- size = [r1, r2, r3, ...rN].length
- sum = r1 + r2 + r3 + ...rN
- all rN should >= min, and <= max
- 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
javascript node.js typescript math random
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 wantN
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
|
show 5 more comments
What do you mean "size"? Some interval between min and max? Could you elaborate? Do you wantN
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
|
show 5 more comments
1 Answer
1
active
oldest
votes
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
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 ofsample_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
|
show 9 more comments
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
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
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
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 ofsample_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
|
show 9 more comments
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
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 ofsample_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
|
show 9 more comments
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
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
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 ofsample_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
|
show 9 more comments
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 ofsample_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
|
show 9 more comments
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.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
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
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