这是我昨晚在床上时想到的一个问题:)。
如何在M个随机部分中分割一个数字,我们称之为N,这样每个部分的概率都可以是从0到N的数字。
例如:N = 5,M = 3
算法应返回一个类似以下数组的数组:
[0,2,3] //0 + 2 +3 =5
[1,1,3]
[0,0,5]
[3,2,0]
[2,2,1]
...etc
编程语言并不重要。
答案 0 :(得分:2)
我刚订阅了这个特定问题的分享结果。我找到了一个让我高兴的解决方案,即使它显然不是100%的随机数分割。但它符合我的需求,而且资源非常少。
我给你的方法代码(它是一个递归方法),对特定部分有评论(其他人非常直截了当)。代码是在AS3中完成的,但语言很容易阅读:
/**
* This function splits a unique number into many numbers whose total equals to the one given as a parameter.
* The function only works for size equals to a power of 2, but I think it can be easily done for any size,
* by making as many "power of 2" calls as necessary to get a good final result.
* @param total The expected value of the sum of the resulting numbers
* @param minValue The minimum value each number can take
* @param maxValue The maximum value each number can take
* @param size The number of numbers we want to split the total into
* @param stepNum The step number of the recursive calls. Used to enhance the results of the algorithm.
* @return
*/
private function splitTotalInTwo(total:int, minValue:int, maxValue:int, size:int, stepNum:int):Vector.<int>
{
var result:Vector.<int> = new Vector.<int>();
// we determine the min and max values allowed for the random generated number so that it doesn't
// make the second group impossible to process with success
var minRand:int = Math.max(size * minValue, total - (size * maxValue));
var maxRand:int = Math.min(size * maxValue, total - (size * minValue));
// the balanceFactor is used to make the split tighter in the early stages of the recursive algorithm,
// therefore ensuring a best number distribution in the end.
// You can comment the next three lines to see the number distribution of th inital algorithm.
// You can alsocchange the balancefactor to get which results you like most.
// This var good also be passed as parameter to fine tune the algorithm a little bit more.
var balanceFactor:Number = 0.4;
var delta:int = Math.floor((maxRand - minRand) * (0.4 / stepNum));
minRand += delta;
maxRand -= delta;
var random:int = Math.floor(Math.random() * (maxRand - minRand)) + minRand;
if (size > 1)
{
result = result.concat(splitTotalInTwo(random, minValue, maxValue, size / 2, stepNum+1), splitTotalInTwo(total - random, minValue, maxValue, size / 2, stepNum+1));
}
else
{
result.push(random);
result.push(total - random);
}
return result;
}
希望这会有所帮助......
答案 1 :(得分:1)
让我们假设我们有一种方法可以在[0,N]
之间生成均匀随机的数字分布。一种显而易见的方法是在M
范围内连续生成[0,N]
个随机数,我们在每次生成后更新N,因为所有生成的数字的总和必须等于N
。你必须在数学上证明这将导致一对随机分布的对[x1,x2,....,xM]
集合。我会说这是不是微不足道的。 (例如,您的第一个数字随机选择为5的示例,以下两个数字 为零,因为总和不能超过N=5
,因此随机性是有偏差的)< / p>
您可能会考虑的另一个“暴力”方法是生成所有可能排列[x1,x2,....,xM]
的集合,其中总和为x1+x2+...+xM = N
。如果集合包含Y
个可能的排列,您可以使用我们之前定义的随机生成器获取[1,Y]
范围内的随机数,并从集合中选择该元素
请注意,这只是我的头脑,如果你想确保真正随机的均匀分布,你必须以数学方式检查这些提议。
编辑:我也刚刚意识到,他们可能是你描述问题的方式,分割数字的顺序是无关紧要的(即[0,2,3]
与[2,3,0]
相同,与{相同} {1}})。这减少了[0,3,2]
分组
答案 2 :(得分:0)
您可以尝试我的方法。最初我计算了可能的结果数量,然后选择随机(在C#上实现):
class Program
{
private static int[,] dp;
private static void Populate(int m, int n)
{
dp[0, 0] = 1;
for (int i = 1; i <= m; i++)
{
dp[i, 0] = 1;
for (int j = 1; j <= n; j++)
{
dp[i, j] = dp[i - 1, j] + dp[i, j - 1];
}
}
}
private static int[] GetResultAt(int index, int m, int n)
{
int[] result = new int[m];
while (m > 0)
{
for (int i = 0; i <= n; i++)
{
if (index <= dp[m - 1, i] && dp[m - 1, i] > 0)
{
m--;
result[m] = n - i;
n = i;
break;
}
index = index - dp[m - 1, i];
}
}
return result;
}
static void Main(string[] args)
{
int n = 5;
int m = 3;
dp = new int[m + 1, n + 1];
Populate(m, n);
int randomPosition = 7;// Random value from 1 and dp[m,n] range.
int[] result = GetResultAt(randomPosition, m, n);
Console.WriteLine(result);
}
}
定义必需的m
,n
和randomPosition
以获得结果。
对于prealc,此算法的复杂度为 O(M * N),对于获得随机数组,此算法的 O(M + N)。
答案 3 :(得分:0)
public static Integer[] sliceUnequally(int value, int numberOfSlices) {
if (numberOfSlices > value) {
throw new IllegalArgumentException(
String.format("!!! Cannot carve %d into %d slices", value, numberOfSlices));
}
final Integer[] slices = new Integer[numberOfSlices];
int allocated = numberOfSlices;
for (int i = 0; i < numberOfSlices; i++) {
if (i == (numberOfSlices - 1)) {
slices[i] = 1 + (value - allocated);
} else {
int slice = new Double((value - allocated) * random.nextDouble()).intValue();
slices[i] = 1 + slice;
allocated += slice;
}
}
return slices;
}