随机部分中的分割数

时间:2013-01-29 13:02:15

标签: algorithm random

这是我昨晚在床上时想到的一个问题:)。

如何在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

编程语言并不重要。

4 个答案:

答案 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);
    }
}

定义必需的mnrandomPosition以获得结果。 对于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;
}