拆分多个范围内的数字

时间:2016-02-24 14:48:12

标签: c# algorithm random numbers distribution

我正在尝试将数字拆分为适合预定义范围的较小数字,而我似乎无法使算法正确。我正在使用C#。

示例

20 分成三个数字,其中数字必须符合以下范围: 1-3,3-10和0-15 。最终数字可能如下所示:1,5,14或2,3,15

另一个例子可能是将 100 分成四个符合以下范围的数字: 0-10,0-10,0-40,0-40 。结果自然是10,10,40,40。在相同的范围内拆分90可能会产生5,8,38,39等。

你能帮助我朝正确的方向发展吗?

(不,这不是作业,而是个人项目)

4 个答案:

答案 0 :(得分:1)

以下描述了一种非常有效的方法,假设存储桶具有某种排序。

首先选择每个范围的最小值并将其相加。

  • 如果总和等于您的数字,则停止。
  • 如果金额大于您的数字,则发出错误。
  • 如果金额小于您的数字,请继续。

接下来,从每个范围中减去最小值,使它们全部归一化为0。 。 n并从您的数字中减去总和。这不是绝对必要的,但它有助于解释算法的其余部分。

接下来,执行最大范围值的累积和。找到新总和适合的水桶(对于之前的水桶来说太大但适合)。如果没有找到,则发出错误。

然后分配箱子,使前面的桶达到最大值,并将找到的桶设置为适当的值。

这为您提供了一组符合您条件的值。

如果想要在范围的“中间”中更多的值,则从范围的中间值开始。然后在所有存储桶中以块的形式添加或减去值,直到达到最大值。这需要多一点迭代,但它也非常有效。

答案 1 :(得分:1)

试试这个:

List<KeyValuePair<int, int>> ranges = new List<KeyValuePair<int, int>>();
ranges.Add(new KeyValuePair<int, int>(1, 3));
ranges.Add(new KeyValuePair<int, int>(1, 3));
ranges.Add(new KeyValuePair<int, int>(1, 100));

int totalSum = ranges.Sum(i => i.Value - i.Key);

double ws = 0.0;
int rIndex = 0;
var rangeAndWeight = ranges.Select(i => new { index = rIndex++, range = i, maxw = (ws += (double)(i.Value - i.Key) / totalSum) }).ToList();

int[] nums = ranges.Select(i => i.Key).ToArray();

int number = 50;

Random r = new Random();
while (nums.Sum() != number)
{
    double rDouble = r.NextDouble();

    var index = rangeAndWeight.SkipWhile(i => i.maxw < rDouble).First().index;

    if (nums[index] < ranges[index].Value)
        nums[index] += 1;
}

nums数组包含您需要的较小数字

答案 2 :(得分:1)

您可以使用递归来完成。

算法的想法是这样的:

  • 在每次执行中,您将迭代所有可能的间隔数。

  • 递归调用以生成下一个间隔的下一个数字。

  • 如果总和超过所需的值,则返回。

  • 一旦生成了所有数字,如果总和等于所需的数字,那么您就有了可能的组合。

它可以改进,但它是一种解决方案。

以下代码在控制台中打印所有有效序列:

SplitNumber(100, new Interval[]
{
    new Interval { Min = 0, Max = 11 },
    new Interval { Min = 0, Max = 11 },
    new Interval { Min = 0, Max = 40 },
    new Interval { Min = 0, Max = 40 },
});

public static void SplitNumber(int n, Interval[] intervals)
{
    SplitNumber(n, 0, intervals, "");
}

public static void SplitNumber(int n, int k, Interval[] intervals, string s)
{
    if (n < 0) return;

    if (k >= intervals.Length) { if (n == 0) Console.WriteLine(s); }
    else
        for (int i = intervals[k].Min; i <= intervals[k].Max; i++)
            SplitNumber(n - i, k + 1, intervals, string.Format("{0} {1}", s, i));
}

Interval类是这样的:

public class Interval
{
    public int Min { get; set; }
    public int Max { get; set; }
}

答案 3 :(得分:0)

您可以编写一个程序,只是尝试在允许的范围内总结可能的数字,并希望结果是正确的,如果结果错误则回溯。它非常无用......