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

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

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

我不确定这方面的统计数据,但是,这里的问题是你不想随意选择一个数字,这使得无法通过过冲或下冲来将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-9 并且加起来为N.所以我们必须逐个生成每个数字并确定下一个数字的实际边界。

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

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

像(未经测试)的东西:

 public static List RandomList(int digitMin, int digitMax, int targetSum, int numDigits) { List ret = new List(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以便于阅读。

我的原始声明:

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

我想在思考并点击社区后添加一些澄清……

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

我认为的情况是,所述的问题是无法解决的,我相信可以通过Pigeonhole原则certificate(Knuth也使用它来certificate某些“随机”洗牌不是真正随机的),但我没有做了数学。

大家好好谈谈。

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

 public static IEnumerable GetRandom() { var rand = new Random(); while (true) { yield return rand.Next(1, 9); } } public static List GetThirtyThatAddToTwoHundred() { do { var current = GetRandom().Take(30); if (200 == current.Sum()) { return current.ToList(); } } while (true); } 

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

 public static List RandomListByIncrementing(int digitMin, int digitMax, int targetSum, int numDigits) { if(targetSum < digitMin * numDigits || targetSum > digitMax * numDigits) throw new ArgumentException("Impossible!", "targetSum"); List ret = new List(Enumerable.Repeat(digitMin, numDigits)); List indexList = new List(Enumerable.Range(0, numDigits-1)); Random random = new Random(); int index; for(int currentSum=numDigits * digitMin; currentSum 

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

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

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

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

算法:

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

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

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

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

  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); 

希望能帮助到你!

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

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

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

此方法将返回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; } 

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

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

 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 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 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 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 LeftList = AddUp(MinDigit, MaxDigit, LeftItemsToSum, LeftTargetSum); IList RightList = AddUp(MinDigit, MaxDigit, RightItemsToSum, RightTargetSum); List Results = new List(); 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(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 

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

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

第一种方法:

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

第二种方法更简单,但随机分布更少:

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

这是代码:

 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, 

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

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

无论如何,你去……至少是一个好的起点。

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

JaredPar代码喜欢我,但它很慢,它就像扔硬币并希望获得n值.Nice代码片段