生成一系列随机数,在c#中加起来为N

时间:2009-01-23 05:47:41

标签: c# .net random

如何在1-9之间生成30个随机数,在C#中总共加起来200(或任意N)?

我正在尝试生成一串可以加起来为N的数字。

16 个答案:

答案 0 :(得分:10)

我不确定这方面的统计数据但是,这里的问题是你不想随意选择一个数字,这使得无法通过过冲或下冲来将N与M个条目相加。我就是这样做的:

static void Main()
{
    int count = 30;
    int[] numbers = getNumbers(count, 155);
    for (int index = 0; index < count; index++)
    {
        Console.Write(numbers[index]);
        if ((index + 1) % 10 == 0)
            Console.WriteLine("");
        else if (index != count - 1)
            Console.Write(",");
    }
    Console.ReadKey();
}
static int[] getNumbers(int count, int total)
{
    const int LOWERBOUND = 1;
    const int UPPERBOUND = 9;

    int[] result = new int[count];
    int currentsum = 0;
    int low, high, calc;

    if((UPPERBOUND * count) < total ||
        (LOWERBOUND * count) > total ||
        UPPERBOUND < LOWERBOUND)
        throw new Exception("Not possible.");

    Random rnd = new Random();

    for (int index = 0; index < count; index++)
    {
        calc = (total - currentsum) - (UPPERBOUND * (count - 1 - index));
        low = calc < LOWERBOUND ? LOWERBOUND : calc;
        calc = (total - currentsum) - (LOWERBOUND * (count - 1 - index));
        high = calc > UPPERBOUND ? UPPERBOUND : calc;

        result[index] = rnd.Next(low, high + 1);

        currentsum += result[index];
    }

    // The tail numbers will tend to drift higher or lower so we should shuffle to compensate somewhat.

    int shuffleCount = rnd.Next(count * 5, count * 10);
    while (shuffleCount-- > 0)
        swap(ref result[rnd.Next(0, count)], ref result[rnd.Next(0, count)]);

    return result;
}
public static void swap(ref int item1, ref int item2)
{
    int temp = item1;
    item1 = item2;
    item2 = temp;
}

如果我的逻辑中存在缺陷,我没有太多时间来测试这个,所以道歉。

编辑:

我做了一些测试,一切看起来都很稳固。如果你想要一个漂亮的漂亮传播,看起来你想要Total = Count * ((UPPER + LOWER) / 2)的线条。虽然我相当确定UPPERLOWER之间的差异会增加,但这会变得更加灵活。

答案 1 :(得分:7)

问题是我们希望所有数字都有界限1-9 加起来N.所以我们必须逐个生成每个数字并确定下一个数字的实际界限。 / p>

这当然会在列表末尾产生统计偏差,因此我建议在生成后将数组洗牌一次。

要确定下一个数字的边界,请执行以下操作:上限=取剩余的总和减去(剩余的元素数量* min)。下限=取剩余的总和减去(剩余的元素数量*最大值)。

像(未经测试)的东西:

public static List<int> RandomList(int digitMin, int digitMax, 
                                   int targetSum, int numDigits)
{
    List<int> ret = new List<int>(numDigits);

    Random random = new Random();
    int localMin, localMax, nextDigit;
    int remainingSum = targetSum;

    for(int i=1; i<=numDigits; i++)
    {
          localMax = remainingSum - ((numDigits - i) * min);
          if(localMax > max)
              localMax = max;

          localMin = remainingSum - ((length - i) * max);
          if(localMin > min)
              localMin = min;

          nextDigit = random.Next(localMin, localMax);
          ret.Add(nextDigit);
          remainingSum -= nextDigit;
    }

    return ret;
}

这里的想法是在生成数字时,剩余数字的可能值范围变小,就像限制函数归零目标总和一样。排序。

编辑:我必须将for循环更改为从1开始,因为我们希望生成此元素后剩余的元素数量。

Edit2:将其置于完整性方法中,并将length更改为numDigits以提高可读性。

答案 2 :(得分:4)

我的原始声明:

您只能生成29个随机数。第30个数字将由其他29和总和定义。这在统计上很重要......

我想在考虑之后添加一些澄清,并pinging社区......

我现在相信我的原始陈述是错误的。这太宽松了(lc指出)。你甚至无法生成29个真正随机的数字。当你越来越接近30时,最后的数字不是随机的,就像rnd [1..9]是随机的一样。 lc试图缓解这一问题以便提出解决方案,但我相信他提出的解决方案(以及Spencer)回答了一个非常不同的问题。那个问题是“在1到9之间的所有30个数字的集合中,总计200个,随机构造一个”。

我认为的情况是,所述的问题是无法解决的,我相信可以用Pigeonhole Principle证明(也被Knuth用来证明某些“随机”洗牌不是真正随机的) ,但我没有做过数学计算。

大家好好谈谈。

答案 3 :(得分:3)

该程序将尝试为您提供答案。但是因为你正在处理随机数,所以有可能永远不会给你答案。

public static IEnumerable<int> GetRandom()
{
    var rand = new Random();
    while (true)
    {
        yield return
        rand.Next(1, 9);
    }
}

public static List<int> GetThirtyThatAddToTwoHundred()
{
    do
    {
        var current = GetRandom().Take(30);
        if (200 == current.Sum())
        {
            return current.ToList();
        }
    } while (true);
}

答案 4 :(得分:1)

在这里进行了所有讨论之后,还有另外一种方法可以生成一个不会引入偏见的列表。是的,它确实与问题的不同,但不是随机选择数字,您可以随机递增数字,直到达到总和。如下(再次未经测试):

public static List<int> RandomListByIncrementing(int digitMin, int digitMax, 
                                                 int targetSum, int numDigits)
{
    if(targetSum < digitMin * numDigits || targetSum > digitMax * numDigits)
        throw new ArgumentException("Impossible!", "targetSum");

    List<int> ret = new List<int>(Enumerable.Repeat(digitMin, numDigits));
    List<int> indexList = new List<int>(Enumerable.Range(0, numDigits-1));

    Random random = new Random();
    int index;

    for(int currentSum=numDigits * digitMin; currentSum<targetSum; currentSum++)
    {
        //choose a random digit in the list to increase by 1
        index = random.Next(0,indexList.Length-1);

        if(++ret[indexList[index]] == digitMax)
        {
            //if you've increased it up to the max, remove its reference
            //because you can't increase it anymore
            indexList.RemoveAt(index);
        }
    }

    return ret;
}

这里的想法是保留一份对您的号码列表的引用列表。随机选择一个参考,并增加相应的数字。如果您不能再增加它,请删除参考,以便下次不要选择它。

现在没有任何改组业务可以在一天结束时完成,虽然可以说这仍然会产生问题的一组可用答案,这是一个“感觉更好”或运行速度更快的问题

答案 5 :(得分:1)

所以我要问: 是否有实际目的,还是只是练习或家庭作业? 为防止“偏见”,还有很多工作要做。这是一个实际要求,还是任何相当随机的解决方案呢?在不了解要求的情况下,很容易浪费大量时间。如果这是一个真正的问题,请解释实际要求是什么。

答案 6 :(得分:0)

如果真实随机性的统计偏差是可接受的,您可以添加最多N - [最大随机数]的数字,然后选择最后一个数字作为N - 总和(到目前为止选择)。

答案 7 :(得分:0)

算法:

  1. 设置总数= 200(或其他)
  2. 在1-9
  3. 之间生成随机数
  4. 检查是否(总计 - newRandomNumber&gt; = 0),如果没有转到6
  5. total - = newRandomNumber
  6. 将newRandomNumber添加到数组,转到2。
  7. newRandomNumber = total
  8. 如果newRandomNumber!= 0
  9. ,则将newRandomNumber添加到数组
  10. 结束

答案 8 :(得分:0)

此方法将返回30个随机数,这些数字加起来为任意N.有一些0值是可能的。如果这不可行,只需将数组全部初始化为1,如果总和大于任意N,则将vals [nextIdx]设置为1而不是0.希望这会有所帮助。

    private int[] getNumbers(int arbitraryN) {
        int[] vals = new int[30];
        int nextIdx = 0;
        int nextNumber=0;
        Random r = new Random();
        if (arbitraryN > 270 || arbitraryN < 30)
            throw new Exception("Not a Valid number");
        while (vals.Sum() < arbitraryN)
        {
            nextNumber = r.Next(1, 9);
            nextIdx = r.Next(29);
            vals[nextIdx] = nextNumber;
            if (vals.Sum() > arbitraryN)
            {
                vals[nextIdx] = 0;
                vals[nextIdx] = 270 - vals.Sum();
                break;
            }
        }
        return vals;
    }

答案 9 :(得分:0)

没有任何保证,来自1-9的30个随机数会加起来任何特定的N.

你能找到的是一个数字列表,这些数字将加起来为N,并且从1-9开始,但这个数字不一定是30。我相信你需要的最小数字是23,是(22 * 9)+ 2.当然最大数量是200(200 * 1)。所以列表的长度在[23,200]之内。因此,随机列表长度为30的可能性非常低。如果可以获得所有列表长度(我认为它们),那么从长远来看,你的机会大约为0.5%。

答案 10 :(得分:0)

我认为这是最简单的方法,所以它可能缺乏一些复杂性,但它会让你到那里。

        String output = "";
        int sum = 0;
        int result = 200;       //enter the "end number"

        Random r = new Random();

        while (sum != result) {
            int add;
            if ((result - sum) > 10)
            {
                add = r.Next(1, 10);
            }
            else
            {
                add = r.Next(result - sum + 1);
            }

            sum += add;
            output += add.ToString() + " + ";
        }

        output = output.Remove(output.Length - 2);

        Console.WriteLine(output);

希望它有所帮助!

答案 11 :(得分:0)

如果你想要一个无偏见的算法,那么天真的实现就像:

while (true) {
  numbers = [];
  total = 0;
  for (i = 0; i < COUNT; ++i) {
    next = rand(BOUNDS);
    total += next;
    numbers.push(next);
  }

  if (total == TARGET) {
    return numbers;
  }
}

这是非终止且缓慢的,但它没有偏见。如果你想要一个无偏的算法,我不相信这里发布的算法是公正的。

答案 12 :(得分:0)

为了让答案不偏向于较小的数字(或任何其他偏见),理想情况下,您可以生成所有可能的数字组合,这些数字加起来为N.在拥有所有集合后,随机选择其中一组。选择获胜套装后,如果需要,您可以随机调整该套装中数字的顺序。

答案 13 :(得分:0)

我以为我会尝试分而治之的方法。它似乎工作得很好。由于算法的约束元素,我确信结果并不是真正随机的,但它很接近。基本上,我将列表分成两部分,将目标总和分成两半并递归,直到我得到3个或更少元素的列表。然后我使用随机数字的强力迭代,直到获得这些较小的总和。这是在其下面运行示例的代码。

using System;
using System.Collections.Generic;

namespace AddUpClient {
    class Program {

        static void Main() {
            AddUpWorker worker = new AddUpWorker();
            int MinDigit = 1;
            int MaxDigit = 9;
            int ItemsToSum = 30;
            int TargetSum = 150;

            try {
                //Attempt to get a list of pseudo-random list of integers that add up to the target sum
                IList<int> Results = worker.AddUp(MinDigit, MaxDigit, ItemsToSum, TargetSum);

                EvaluateResults(TargetSum, Results);
                Console.ReadLine();
            }
            catch (Exception E) {
                Console.Out.WriteLine("Error: {0}", E.Message);
                return;
            }

        }

        private static void EvaluateResults(int TargetSum, IList<int> Results)
        {
            Console.Out.WriteLine("Results have {0} items.", Results.Count);

            int Sum = 0;
            foreach (int Result in Results) {
                Sum += Result;
                Console.Out.WriteLine("Result: {0} Running total: {1}", Result, Sum);
            }
            Console.Out.WriteLine();
            Console.Out.WriteLine("Result = {0}", (Sum == TargetSum ? "SUCCESS" : "FAIL"));
        }
    }

    internal class AddUpWorker {

        Random RGenerator = new Random();

        public IList<int> AddUp(int MinDigit, int MaxDigit, int ItemsToSum, int TargetSum) {

            Console.Out.WriteLine("AddUp called to sum {0} items to get {1}", ItemsToSum, TargetSum);
            if (ItemsToSum > 3) {

                int LeftItemsToSum = ItemsToSum/2;
                int RightItemsToSum = ItemsToSum - LeftItemsToSum;

                int LeftTargetSum = TargetSum/2;
                int RightTargetSum = TargetSum - LeftTargetSum;


                IList<int> LeftList = AddUp(MinDigit, MaxDigit, LeftItemsToSum, LeftTargetSum);
                IList<int> RightList = AddUp(MinDigit, MaxDigit, RightItemsToSum, RightTargetSum);

                List<int> Results = new List<int>();
                Results.AddRange(LeftList);
                Results.AddRange(RightList);
                return Results;
            }

            // 3 or less

            int MinSumWeCanAchieve = ItemsToSum*MinDigit;
            int MaxSumWeCanAchieve = ItemsToSum*MaxDigit;


            if (TargetSum < MinSumWeCanAchieve)
                throw new ApplicationException("We added up too fast");

            if (TargetSum > MaxSumWeCanAchieve)
                throw new ApplicationException("We added up too slow");

            //Now we know we can achieve the result -- but it may not be too efficient...

            int[] TrialNumbers = new int[ItemsToSum];
            int MaxIteration = 100000;
            int IterationPrintInterval = 1000;
            int TrialSum;
            bool PrintIteration;

            for (int Iteration = 1; Iteration <= MaxIteration; ++Iteration) {

                PrintIteration = ((Iteration % IterationPrintInterval) == 0);

                if (PrintIteration)
                    Console.Out.WriteLine("Iteration {0} attempting to sum {1} numbers to {2}",
                        Iteration, ItemsToSum, TargetSum);

                TrialSum = 0;
                for (int j=0; j < ItemsToSum; ++j) {
                    TrialNumbers[j] = RGenerator.Next(MinDigit, MaxDigit + 1);
                    TrialSum += TrialNumbers[j];
                }
                if (PrintIteration)
                    ShowArray(string.Format("Iteration: {0}", Iteration), TrialNumbers);

                if (TrialSum == TargetSum) {    //Yay
                    ShowArray(string.Format("Success in {0} iterations: ", Iteration), TrialNumbers);
                    return new List<int>(TrialNumbers);
                }
                //try again....
            }

            throw new ApplicationException(string.Format("Maximum of {0} trials exceeded", MaxIteration));
        }

        private void ShowArray(string Prefix, int[] numbers)
        {
            for (int i = 0; i < numbers.Length; ++i) {
                if (i == 0)
                    Console.Write("{0} {1}", Prefix, numbers[i]);
                else
                    Console.Write(", {0}", numbers[i]);
            }
            Console.WriteLine();
        }
    }
}

AddUp called to sum 30 items to get 150
AddUp called to sum 15 items to get 75
AddUp called to sum 7 items to get 37
AddUp called to sum 3 items to get 18
Success in 10 iterations:  7, 2, 9
AddUp called to sum 4 items to get 19
AddUp called to sum 2 items to get 9
Success in 12 iterations:  5, 4
AddUp called to sum 2 items to get 10
Success in 2 iterations:  1, 9
AddUp called to sum 8 items to get 38
AddUp called to sum 4 items to get 19
AddUp called to sum 2 items to get 9
Success in 11 iterations:  4, 5
AddUp called to sum 2 items to get 10
Success in 6 iterations:  8, 2
AddUp called to sum 4 items to get 19
AddUp called to sum 2 items to get 9
Success in 3 iterations:  8, 1
AddUp called to sum 2 items to get 10
Success in 1 iterations:  4, 6
AddUp called to sum 15 items to get 75
AddUp called to sum 7 items to get 37
AddUp called to sum 3 items to get 18
Success in 3 iterations:  4, 6, 8
AddUp called to sum 4 items to get 19
AddUp called to sum 2 items to get 9
Success in 17 iterations:  3, 6
AddUp called to sum 2 items to get 10
Success in 24 iterations:  1, 9
AddUp called to sum 8 items to get 38
AddUp called to sum 4 items to get 19
AddUp called to sum 2 items to get 9
Success in 3 iterations:  2, 7
AddUp called to sum 2 items to get 10
Success in 3 iterations:  1, 9
AddUp called to sum 4 items to get 19
AddUp called to sum 2 items to get 9
Success in 4 iterations:  5, 4
AddUp called to sum 2 items to get 10
Success in 2 iterations:  9, 1
Results have 30 items.
Result: 7 Running total: 7
Result: 2 Running total: 9
Result: 9 Running total: 18
Result: 5 Running total: 23
Result: 4 Running total: 27
Result: 1 Running total: 28
Result: 9 Running total: 37
Result: 4 Running total: 41
Result: 5 Running total: 46
Result: 8 Running total: 54
Result: 2 Running total: 56
Result: 8 Running total: 64
Result: 1 Running total: 65
Result: 4 Running total: 69
Result: 6 Running total: 75
Result: 4 Running total: 79
Result: 6 Running total: 85
Result: 8 Running total: 93
Result: 3 Running total: 96
Result: 6 Running total: 102
Result: 1 Running total: 103
Result: 9 Running total: 112
Result: 2 Running total: 114
Result: 7 Running total: 121
Result: 1 Running total: 122
Result: 9 Running total: 131
Result: 5 Running total: 136
Result: 4 Running total: 140
Result: 9 Running total: 149
Result: 1 Running total: 150

Result = SUCCESS

答案 14 :(得分:0)

这是一个老问题,但我发现它正在寻找这个问题的解决方案,以便在随机数据生成测试应用程序中实际使用。

根据这篇文章中的想法,我想出了两个可能的解决方案。

第一种方法:

  1. 计算出可以重复的最小整数值,以加到所需总数附近的数字。基本上,只做整数除法。
  2. 初始化一个数组,其中所有值等于步骤1中找到的数字。
  3. 如果有一个余数(通常会有),则随机地向数组中的项添加一个并减少余数,直到余数为0.此时我们有一个数组,它将等于所需的总数,但它会非常随意。
  4. 对于多次迭代,从数组中的两个位置随机添加和减去。示例:将1添加到位置0并从位置4减去1.这样做,进行边界检查(所有数字应至少为0,所有数字不应大于上限)。
  5. 第二种方法更简单,但随机分布更少:

    1. 初始化一个具有所需长度的0的数组。
    2. 在数组中选择一个随机索引并添加1.如果该索引处的值超过上限,请忽略它并选择另一个索引。
    3. 重复步骤2所需总数所指示的次数。
    4. 以下是代码:

      public static int[] getRandomsWithTotalA(int desiredTotal, int desiredNumbers, int upperBound)
      {
          Random r = new Random();
      
          // Is this even a possible feat?
          if (desiredNumbers * upperBound < desiredTotal) throw new ArgumentException("This is not possible!", "desiredTotal");
      
          // Start by figuring out the closest number we can get to by repeating the initial number.
          int lowestRepeating = desiredTotal / desiredNumbers;
      
          // Determine the remainder
          int lowestRepeatingRemainder = desiredTotal % desiredNumbers;
      
          // Initialize and populate an array of numbers with the lowest repeater.
          int[] results = Enumerable.Repeat(lowestRepeating, desiredNumbers).ToArray();
      
          // We will perform (n*desiredTotal) shuffles.
          int shuffles = (desiredTotal * desiredNumbers);
      
          while (shuffles > 0)
          {
              int a = r.Next(desiredNumbers);
              int b= r.Next(desiredNumbers);
              if (a==b) continue; // do nothing if they're equal - try again.
      
              // Test bounds.
              if (results[a]+1>upperBound) continue;
              if (results[b]-1<0) continue;
      
              // Add one to the first item.
              results[a]++;
      
              // Do we still have a remainder left? If so, add one but don't subtract from
              // somewhere else.
              if (lowestRepeatingRemainder>0)
              {
                  lowestRepeatingRemainder--;
                  continue;
              }
              // Otherwise subtract from another place.
              results[b]--;
              // decrement shuffles
              shuffles--;
          }
      
          return results;
      }
      
      public static int[] getRandomsWithTotalB(int desiredTotal, int desiredNumbers, int upperBound)
      {
          Random r = new Random();
      
          // Is this even a possible feat?
          if (desiredNumbers * upperBound < desiredTotal) throw new ArgumentException("This is not possible!", "desiredTotal");
      
          // Initialize and populate an array of numbers with the lowest repeater.
          int[] results = new int[desiredNumbers];
      
          while (desiredTotal > 0)
          {
              int a = r.Next(desiredNumbers);
      
              // Test bounds.
              if (results[a] + 1 > upperBound) continue;
      
              // Add one to the first item.
              results[a]++;
      
              desiredTotal--;
          }
      
          return results;
      }
      

      示例运行:

      static void Main(string[] args)
      {
          foreach (int i in getRandomsWithTotalA(200, 30, 9))
          {
              Console.Write("{0}, ", i);
          }
          Console.WriteLine("\n");
          foreach (int i in getRandomsWithTotalB(200, 30, 9))
          {
              Console.Write("{0}, ", i);
          }
      }
      
      3, 8, 7, 5, 9, 9, 8, 9, 9, 6, 8, 7, 4, 8, 7, 7, 8, 9, 2, 7, 9, 5, 8, 1, 4, 5, 4, 8, 9, 7,
      
      6, 8, 5, 7, 6, 9, 9, 8, 5, 4, 4, 6, 7, 7, 8, 4, 9, 6, 6, 5, 8, 9, 9, 6, 6, 8, 7, 4, 7, 7, 
      

      这些方法可以理解为不像人们想要的那样均匀分布。特别是第二种方法是有意义的;如果你有一个真正均匀分布的随机源,那么你选择要增加的项应该在所有可能的值上具有相同的概率。第一个也可能会好一点,但它仍然受到随机源也理想地均匀分布的事实的影响。

      我觉得有可能通过在索引选择中引入某种形式的偏差来改进至少第一种方法,或者可能随机化我们添加和减去多少(不总是1),或随机化是否我们实际上是否进行加法/减法。只是调整迭代次数似乎会改变分布,但过了一段时间似乎我们开始偏爱外部边界。 (也许它不可能得到真正均匀的分发!)

      无论如何,你去......至少是一个开始的好地方。

答案 15 :(得分:-1)

public static List<int> getNumbers(int n)
    {
        Random random = new Random(DateTime.Now.Millisecond);
        List<int> obtainedNumbers = new List<int>(); 
        do
        {
            obtainedNumbers.Add(random.Next(1, 9));
        }
        while (n - obtainedNumbers.Sum() > 0);
        return obtainedNumbers;
    }

JaredPar代码喜欢我,但它很慢,它就像扔硬币并希望得到n值。尼斯代码