How do I generate 30 random numbers between 1-9, that all add up to 200 (or some arbitrary N), in C#?
如何在1-9之间生成30个随机数,在C#中总共加起来200(或任意N)?
I'm trying to generate a string of digits that can add together to be N.
我正在尝试生成一串可以加起来为N的数字。
16 个解决方案
#1
8
I'm not sure what the statistics are on this but, the issue here is that you don't want to randomly select a number that makes it impossible to sum N with M number of entries either by overshooting or undershooting. Here's how I would do it:
我不确定这方面的统计数据,但是,这里的问题是你不想随意选择一个数字,这使得无法通过过冲或下冲来将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;
}
I didn't have a lot of time to test this so apologies if there's a flaw in my logic somewhere.
我没有太多时间来测试这个如此道歉,如果我的逻辑中有一个缺陷。
EDIT:
I did some testing and everything seems solid. If you want a nice pretty spread it looks like you want something along the lines of Total = Count * ((UPPER + LOWER) / 2)
. Although I'm fairly certain that as the difference between UPPER
and LOWER
increases the more flexible this becomes.
我做了一些测试,一切看起来都很稳固。如果你想要一个漂亮的漂亮传播,看起来你想要的东西就是Total = Count *((UPPER + LOWER)/ 2)。虽然我很确定UPPER和LOWER之间的差异越大,但它变得越灵活。
#2
7
The problem is we want all numbers to be bounded 1-9 and add up to N. So we have to generate each number one by one and determine the real bounds for the next number.
问题是我们希望所有数字都是1-9并且加起来为N.所以我们必须逐个生成每个数字并确定下一个数字的实际边界。
This will of course generate statistical bias toward the end of the list, so I recommend shuffling the array once after generating.
这当然会在列表末尾产生统计偏差,因此我建议在生成后将数组洗牌一次。
To determine the next number's bounds, do the following: Upper bound = take the remaining sum minus (the number of elements remaining * min). Lower bound = take the remaining sum minus (the number of elements remaining * max).
要确定下一个数字的边界,请执行以下操作:上限=取剩余的总和减去(剩余的元素数* min)。下限=取剩余的总和减去(剩余的元素数量* max)。
Something like (untested):
像(未经测试)的东西:
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;
}
The idea here is as you generate numbers, the range of possible values for the remaining numbers gets smaller, like a limit function zeroing in on a target sum. Sort of.
这里的想法是在生成数字时,剩余数字的可能值范围变小,就像在目标总和上调零的限制函数一样。有点。
Edit: I had to change the for loop to be 1-based, because we want the number of elements left AFTER generating this one.
编辑:我必须将for循环更改为从1开始,因为我们希望生成此元素后剩余的元素数量。
Edit2: Put it in a method for completeness and changed length
to be numDigits
for readability.
Edit2:将其放在一个完整性的方法中,并将长度更改为numDigits以便于阅读。
#3
4
My Original Statement:
我的原始声明:
You can only generate 29 random numbers. The 30th number will be defined by the other 29 and the sum. This is statistically important...
您只能生成29个随机数。第30个数字将由其他29和总和定义。这在统计上很重要......
I wanted to add some clarification after thinking about it and pinging the community...
我想在思考并点击社区后添加一些澄清......
I now believe my original statement to be false. It was too lenient(which lc pointed out). You can't even generate 29 truly random numbers. As you get closer and closer to 30, the final digits aren't random the same way that rnd[1..9] is random. lc tried to mitigate this in order to come up with a solution, but I believe the solution he came up with (and Spencer) answers a very different question. That question is "Of all the sets of 30 digits between 1 and 9 that add up to 200, construct one randomly".
我现在相信我的原始陈述是错误的。它过于宽松(lc指出)。你甚至不能生成29个真正随机的数字。当你越来越接近30时,最后的数字不是随机的,就像rnd [1..9]是随机的一样。 lc试图缓解这个问题以便提出解决方案,但我相信他提出的解决方案(和Spencer)回答了一个非常不同的问题。那个问题是“在1到9之间的所有30个数字的集合中,加起来为200,随机构造一个”。
What I believe to be the case is that the question as stated is unsolvable which I believe can be proved with the Pigeonhole Principle (also used by Knuth to show that certain "random" shuffles weren't really random), but I haven't done the math.
我认为的情况是,所述的问题是无法解决的,我相信可以通过Pigeonhole原则证明(Knuth也使用它来证明某些“随机”洗牌不是真正随机的),但我没有做了数学。
Good talk everyone.
大家好好谈谈。
#4
3
This program will attempt to give you the answer. But because you are dealing with random numbers, there is the possibility that this will never give you the answer.
该程序将尝试为您提供答案。但是因为你正在处理随机数,所以有可能永远不会给你答案。
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);
}
#5
1
After all the discussions here, there's one other way to generate a list that doesn't introduce bias. Yes, it does differ from what the question is asking, but instead of randomly choosing digits, you can randomly increment digits until you reach the sum. Like the following (again untested):
在这里进行了所有讨论之后,还有另外一种方法可以生成一个不会引入偏差的列表。是的,它确实与问题的内容不同,但您可以随机增加数字,直到达到总和为止,而不是随机选择数字。如下(再次未经测试):
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;
}
The idea here is you keep a list of references to your number list. Choose a reference at random, and increment the corresponding number. If you can't increment it anymore, remove the reference so you don't choose it next time.
这里的想法是保留一个对您的号码列表的引用列表。随机选择一个参考,并增加相应的数字。如果您不能再增加它,请删除该引用,以便下次不要选择它。
Now there's no shuffling business to be done at the end of the day, although arguably this will still produce one of the available sets of answers to the question and it's a question of which one "feels better" or is faster to run.
现在没有任何改组业务可以在一天结束时完成,尽管可以说这仍然会产生问题的一组可用答案,而这是一个“感觉更好”或运行速度更快的问题。
#6
1
So I have to ask: Is there an actual purpose for this, or is it just an exercise or homework assignment? There is a lot of work going on to prevent "bias". Is this an actual requirement, or will any fairly random solution do? Without knowing the requirements it's really easy to waste a lot of time. If this is a real problem, please explain what the actual requirements are.
所以我不得不问:这是否有实际目的,还是只是一项练习或家庭作业?为防止“偏见”,还有很多工作要做。这是一个实际要求,还是任何相当随机的解决方案呢?在不了解要求的情况下,很容易浪费大量时间。如果这是一个真正的问题,请解释实际要求是什么。
#7
0
If statistical bias from true randomness is acceptable, you can add numbers up to N - [max random number], then select the last number as N - sum(selected so far).
如果来自真随机性的统计偏差是可接受的,您可以添加最多N - [最大随机数]的数字,然后选择最后一个数字作为N - 总和(到目前为止选择)。
#8
0
Algorithm:
- Set total = 200 (or whatever)
- Generate Random number between 1-9
- Check if (total - newRandomNumber >= 0), if no goto 6
- total -= newRandomNumber
- Add newRandomNumber to array, goto 2.
- newRandomNumber = total
- Add newRandomNumber to array if newRandomNumber != 0
- End
总计= 200(或其他)
生成1-9之间的随机数
检查是否(total - newRandomNumber> = 0),如果没有goto 6
total - = newRandomNumber
将newRandomNumber添加到数组,转到2。
newRandomNumber =总计
如果newRandomNumber!= 0,则将newRandomNumber添加到数组
#9
0
There is no guarrentee that 30 random numbers from 1-9 would add up to any specific N.
没有任何保证从1-9的30个随机数加起来任何特定的N.
What you can find is a list of numbers which will add up to N and are bounded from 1-9 but the number will not be 30 necessarily. I believe the minimum number of numbers you need is 23, being (22*9) + 2. The maximum of course will be 200 (200*1). So the length of the list is somewhere inside [23,200]. The chances that a random list may be length 30 is thus quite low. If all list lengths are obtainable (i think they are) your chances in the long run at about 0.5%.
您可以找到的是一个数字列表,它们将加起来为N,并且从1-9开始,但数字不一定是30。我相信你需要的最小数字是23,是(22 * 9)+ 2.当然最大数量是200(200 * 1)。所以列表的长度在[23,200]之内。随机列表可能长度为30的可能性非常低。如果可以获得所有列表长度(我认为它们),那么从长远来看,你的机会大约为0.5%。
#10
0
I think this is the simplest way to do it, so it may lack some sophistication however it will get you there.
我认为这是最简单的方法,所以它可能缺乏一些复杂性,但它会让你到那里。
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);
Hope it helps!
希望能帮助到你!
#11
0
If you want an unbiased algorithm then the naive implementation is something like:
如果你想要一个无偏见的算法,那么天真的实现是这样的:
while (true) {
numbers = [];
total = 0;
for (i = 0; i < COUNT; ++i) {
next = rand(BOUNDS);
total += next;
numbers.push(next);
}
if (total == TARGET) {
return numbers;
}
}
This is non-terminating and slow but it is not biased. If you want a unbiased algorithm I'm not convinced the algorithms posted here are unbiased.
这是非终止和缓慢但它没有偏见。如果你想要一个无偏的算法,我不相信这里发布的算法是公正的。
#12
0
This method will return 30 random numbers that add up to an arbitraryN. It is possible do, to have some 0 values. if that is not feasible, just initialize the array all to one's and if the sum is greater to the arbitraryN, set vals[nextIdx] to 1 instead of 0. Hope this helps.
此方法将返回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;
}
#13
0
In order to have an answer not biased towards smaller numbers (or any other bias), you would ideally generate all possible sets of numbers that add up to N. After you have all the sets, randomly select one of the sets. After you've selected the winning set, you can randomly shake up the order of the numbers within that set, if needed.
为了使答案不偏向于较小的数字(或任何其他偏差),理想情况下,您将生成所有可能的数字组,这些数字加起来为N.在拥有所有集合后,随机选择其中一个集合。选择获胜组后,如果需要,您可以随机改变该组中数字的顺序。
#14
0
I thought I'd try a divide and conquer approach. It seems to work pretty well. I'm sure the results aren't truly random due to the constraining elements of the algorithm, but it comes close. Essentially, I split the list in two and the target sum in half and recurse until I get lists of 3 elements or less. Then I use a brute force iteration of random digits until these smaller sums are attained. Here's the code with a sample run below it.
我以为我会尝试分而治之的方法。它似乎工作得很好。由于算法的约束元素,我确信结果并不是真正随机的,但它很接近。基本上,我将列表分成两部分,将目标总和分成两半并递归,直到我得到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
#15
0
This is an old question but I discovered it looking for a solution to this problem for a practical use in a random data generation testing application.
这是一个老问题,但我发现它正在寻找这个问题的解决方案,以便在随机数据生成测试应用程序中实际使用。
Based on the ideas in this post I came up with two potential solutions.
基于这篇文章中的想法,我想出了两个可能的解决方案。
The first method:
第一种方法:
- Figure out the lowest integer value that can be repeated to add up to a number near the desired total. Essentially, just do integer division.
- Initialize an array with all of the values equaling the number found in step 1.
- If there is a remainder (there usually will be), randomly add one to items in the array and decrement the remainder until the remainder is 0. At this point we have an array which will equal the desired total, but it will be very un-random.
- For a number of iterations, randomly add and subtract from two locations in the array. Example: add 1 to position 0 and subtract 1 from position 4. In so doing, do bounds checks (all numbers should be at least 0 and all numbers should be no greater than an upper bound).
计算出可以重复的最小整数值,以便加到所需总数附近的数字。基本上,只做整数除法。
初始化一个数组,其中所有值等于步骤1中找到的数字。
如果有一个余数(通常会有),则随机地向数组中的项添加一个并减少余数,直到余数为0.此时我们有一个数组将等于所需的总数,但它将非常不合适-随机。
对于多次迭代,从数组中的两个位置随机添加和减去。示例:将1添加到位置0并从位置4减去1.这样做,进行边界检查(所有数字应至少为0,所有数字不应大于上限)。
The second method is much simpler but results in a less random distribution:
第二种方法更简单,但随机分布更少:
- Initialize an array of 0's of the desired length.
- Select a random index in the array and add 1. If the value at that index would exceed the upper bound, ignore it and select another index.
- Repeat step 2 the number of times indicated by the desired total.
初始化一个所需长度为0的数组。
在数组中选择一个随机索引并添加1.如果该索引处的值超过上限,请忽略它并选择另一个索引。
重复步骤2所需总数所指示的次数。
Here is the code:
这是代码:
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;
}
A sample run:
示例运行:
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,
These methods are understandably not as evenly distributed as one would like. It would make sense especially with the second method; if you have a random source that is truly evenly distributed, then your selection of the items to increment should have equal probability across all the possible values. The first one could potentially be a bit better also, but it still suffers from the fact that the random source is also ideally evenly distributed.
可以理解,这些方法不像人们希望的那样均匀分布。特别是第二种方法,这是有道理的;如果你有一个真正均匀分布的随机源,那么你选择要增加的项应该在所有可能的值上具有相同的概率。第一个也可能有点好一点,但它仍然受到随机源也理想地均匀分布的事实的影响。
I feel like it might be possible to improve at least the first method by introducing some form of bias into the index selection, or possibly a randomization of how much we add and subtract (not always 1), or a randomization of whether we actually do the addition/subtraction or not. Just tweaking the number of iterations seems to change the distribution, but after a while it seems that we start favoring the outer boundaries. (Perhaps it's not possible to get a truly even distribution!)
我觉得有可能通过在索引选择中引入某种形式的偏差来改进至少第一种方法,或者可能随机化我们添加和减去多少(不总是1),或随机化我们是否真的做是否加/减。只是调整迭代次数似乎会改变分布,但过了一段时间似乎我们开始偏向外部边界。 (也许不可能真正均匀分配!)
In any case, there you go...A good place to start at least.
无论如何,你去......至少是一个好的起点。
#16
-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 code likes me but its slow, it's like to throw a coin and hope to get the n value.Nice pieces of codes
JaredPar代码喜欢我,但它很慢,它就像扔硬币并希望获得n值.Nice代码片段
#1
8
I'm not sure what the statistics are on this but, the issue here is that you don't want to randomly select a number that makes it impossible to sum N with M number of entries either by overshooting or undershooting. Here's how I would do it:
我不确定这方面的统计数据,但是,这里的问题是你不想随意选择一个数字,这使得无法通过过冲或下冲来将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;
}
I didn't have a lot of time to test this so apologies if there's a flaw in my logic somewhere.
我没有太多时间来测试这个如此道歉,如果我的逻辑中有一个缺陷。
EDIT:
I did some testing and everything seems solid. If you want a nice pretty spread it looks like you want something along the lines of Total = Count * ((UPPER + LOWER) / 2)
. Although I'm fairly certain that as the difference between UPPER
and LOWER
increases the more flexible this becomes.
我做了一些测试,一切看起来都很稳固。如果你想要一个漂亮的漂亮传播,看起来你想要的东西就是Total = Count *((UPPER + LOWER)/ 2)。虽然我很确定UPPER和LOWER之间的差异越大,但它变得越灵活。
#2
7
The problem is we want all numbers to be bounded 1-9 and add up to N. So we have to generate each number one by one and determine the real bounds for the next number.
问题是我们希望所有数字都是1-9并且加起来为N.所以我们必须逐个生成每个数字并确定下一个数字的实际边界。
This will of course generate statistical bias toward the end of the list, so I recommend shuffling the array once after generating.
这当然会在列表末尾产生统计偏差,因此我建议在生成后将数组洗牌一次。
To determine the next number's bounds, do the following: Upper bound = take the remaining sum minus (the number of elements remaining * min). Lower bound = take the remaining sum minus (the number of elements remaining * max).
要确定下一个数字的边界,请执行以下操作:上限=取剩余的总和减去(剩余的元素数* min)。下限=取剩余的总和减去(剩余的元素数量* max)。
Something like (untested):
像(未经测试)的东西:
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;
}
The idea here is as you generate numbers, the range of possible values for the remaining numbers gets smaller, like a limit function zeroing in on a target sum. Sort of.
这里的想法是在生成数字时,剩余数字的可能值范围变小,就像在目标总和上调零的限制函数一样。有点。
Edit: I had to change the for loop to be 1-based, because we want the number of elements left AFTER generating this one.
编辑:我必须将for循环更改为从1开始,因为我们希望生成此元素后剩余的元素数量。
Edit2: Put it in a method for completeness and changed length
to be numDigits
for readability.
Edit2:将其放在一个完整性的方法中,并将长度更改为numDigits以便于阅读。
#3
4
My Original Statement:
我的原始声明:
You can only generate 29 random numbers. The 30th number will be defined by the other 29 and the sum. This is statistically important...
您只能生成29个随机数。第30个数字将由其他29和总和定义。这在统计上很重要......
I wanted to add some clarification after thinking about it and pinging the community...
我想在思考并点击社区后添加一些澄清......
I now believe my original statement to be false. It was too lenient(which lc pointed out). You can't even generate 29 truly random numbers. As you get closer and closer to 30, the final digits aren't random the same way that rnd[1..9] is random. lc tried to mitigate this in order to come up with a solution, but I believe the solution he came up with (and Spencer) answers a very different question. That question is "Of all the sets of 30 digits between 1 and 9 that add up to 200, construct one randomly".
我现在相信我的原始陈述是错误的。它过于宽松(lc指出)。你甚至不能生成29个真正随机的数字。当你越来越接近30时,最后的数字不是随机的,就像rnd [1..9]是随机的一样。 lc试图缓解这个问题以便提出解决方案,但我相信他提出的解决方案(和Spencer)回答了一个非常不同的问题。那个问题是“在1到9之间的所有30个数字的集合中,加起来为200,随机构造一个”。
What I believe to be the case is that the question as stated is unsolvable which I believe can be proved with the Pigeonhole Principle (also used by Knuth to show that certain "random" shuffles weren't really random), but I haven't done the math.
我认为的情况是,所述的问题是无法解决的,我相信可以通过Pigeonhole原则证明(Knuth也使用它来证明某些“随机”洗牌不是真正随机的),但我没有做了数学。
Good talk everyone.
大家好好谈谈。
#4
3
This program will attempt to give you the answer. But because you are dealing with random numbers, there is the possibility that this will never give you the answer.
该程序将尝试为您提供答案。但是因为你正在处理随机数,所以有可能永远不会给你答案。
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);
}
#5
1
After all the discussions here, there's one other way to generate a list that doesn't introduce bias. Yes, it does differ from what the question is asking, but instead of randomly choosing digits, you can randomly increment digits until you reach the sum. Like the following (again untested):
在这里进行了所有讨论之后,还有另外一种方法可以生成一个不会引入偏差的列表。是的,它确实与问题的内容不同,但您可以随机增加数字,直到达到总和为止,而不是随机选择数字。如下(再次未经测试):
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;
}
The idea here is you keep a list of references to your number list. Choose a reference at random, and increment the corresponding number. If you can't increment it anymore, remove the reference so you don't choose it next time.
这里的想法是保留一个对您的号码列表的引用列表。随机选择一个参考,并增加相应的数字。如果您不能再增加它,请删除该引用,以便下次不要选择它。
Now there's no shuffling business to be done at the end of the day, although arguably this will still produce one of the available sets of answers to the question and it's a question of which one "feels better" or is faster to run.
现在没有任何改组业务可以在一天结束时完成,尽管可以说这仍然会产生问题的一组可用答案,而这是一个“感觉更好”或运行速度更快的问题。
#6
1
So I have to ask: Is there an actual purpose for this, or is it just an exercise or homework assignment? There is a lot of work going on to prevent "bias". Is this an actual requirement, or will any fairly random solution do? Without knowing the requirements it's really easy to waste a lot of time. If this is a real problem, please explain what the actual requirements are.
所以我不得不问:这是否有实际目的,还是只是一项练习或家庭作业?为防止“偏见”,还有很多工作要做。这是一个实际要求,还是任何相当随机的解决方案呢?在不了解要求的情况下,很容易浪费大量时间。如果这是一个真正的问题,请解释实际要求是什么。
#7
0
If statistical bias from true randomness is acceptable, you can add numbers up to N - [max random number], then select the last number as N - sum(selected so far).
如果来自真随机性的统计偏差是可接受的,您可以添加最多N - [最大随机数]的数字,然后选择最后一个数字作为N - 总和(到目前为止选择)。
#8
0
Algorithm:
- Set total = 200 (or whatever)
- Generate Random number between 1-9
- Check if (total - newRandomNumber >= 0), if no goto 6
- total -= newRandomNumber
- Add newRandomNumber to array, goto 2.
- newRandomNumber = total
- Add newRandomNumber to array if newRandomNumber != 0
- End
总计= 200(或其他)
生成1-9之间的随机数
检查是否(total - newRandomNumber> = 0),如果没有goto 6
total - = newRandomNumber
将newRandomNumber添加到数组,转到2。
newRandomNumber =总计
如果newRandomNumber!= 0,则将newRandomNumber添加到数组
#9
0
There is no guarrentee that 30 random numbers from 1-9 would add up to any specific N.
没有任何保证从1-9的30个随机数加起来任何特定的N.
What you can find is a list of numbers which will add up to N and are bounded from 1-9 but the number will not be 30 necessarily. I believe the minimum number of numbers you need is 23, being (22*9) + 2. The maximum of course will be 200 (200*1). So the length of the list is somewhere inside [23,200]. The chances that a random list may be length 30 is thus quite low. If all list lengths are obtainable (i think they are) your chances in the long run at about 0.5%.
您可以找到的是一个数字列表,它们将加起来为N,并且从1-9开始,但数字不一定是30。我相信你需要的最小数字是23,是(22 * 9)+ 2.当然最大数量是200(200 * 1)。所以列表的长度在[23,200]之内。随机列表可能长度为30的可能性非常低。如果可以获得所有列表长度(我认为它们),那么从长远来看,你的机会大约为0.5%。
#10
0
I think this is the simplest way to do it, so it may lack some sophistication however it will get you there.
我认为这是最简单的方法,所以它可能缺乏一些复杂性,但它会让你到那里。
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);
Hope it helps!
希望能帮助到你!
#11
0
If you want an unbiased algorithm then the naive implementation is something like:
如果你想要一个无偏见的算法,那么天真的实现是这样的:
while (true) {
numbers = [];
total = 0;
for (i = 0; i < COUNT; ++i) {
next = rand(BOUNDS);
total += next;
numbers.push(next);
}
if (total == TARGET) {
return numbers;
}
}
This is non-terminating and slow but it is not biased. If you want a unbiased algorithm I'm not convinced the algorithms posted here are unbiased.
这是非终止和缓慢但它没有偏见。如果你想要一个无偏的算法,我不相信这里发布的算法是公正的。
#12
0
This method will return 30 random numbers that add up to an arbitraryN. It is possible do, to have some 0 values. if that is not feasible, just initialize the array all to one's and if the sum is greater to the arbitraryN, set vals[nextIdx] to 1 instead of 0. Hope this helps.
此方法将返回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;
}
#13
0
In order to have an answer not biased towards smaller numbers (or any other bias), you would ideally generate all possible sets of numbers that add up to N. After you have all the sets, randomly select one of the sets. After you've selected the winning set, you can randomly shake up the order of the numbers within that set, if needed.
为了使答案不偏向于较小的数字(或任何其他偏差),理想情况下,您将生成所有可能的数字组,这些数字加起来为N.在拥有所有集合后,随机选择其中一个集合。选择获胜组后,如果需要,您可以随机改变该组中数字的顺序。
#14
0
I thought I'd try a divide and conquer approach. It seems to work pretty well. I'm sure the results aren't truly random due to the constraining elements of the algorithm, but it comes close. Essentially, I split the list in two and the target sum in half and recurse until I get lists of 3 elements or less. Then I use a brute force iteration of random digits until these smaller sums are attained. Here's the code with a sample run below it.
我以为我会尝试分而治之的方法。它似乎工作得很好。由于算法的约束元素,我确信结果并不是真正随机的,但它很接近。基本上,我将列表分成两部分,将目标总和分成两半并递归,直到我得到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
#15
0
This is an old question but I discovered it looking for a solution to this problem for a practical use in a random data generation testing application.
这是一个老问题,但我发现它正在寻找这个问题的解决方案,以便在随机数据生成测试应用程序中实际使用。
Based on the ideas in this post I came up with two potential solutions.
基于这篇文章中的想法,我想出了两个可能的解决方案。
The first method:
第一种方法:
- Figure out the lowest integer value that can be repeated to add up to a number near the desired total. Essentially, just do integer division.
- Initialize an array with all of the values equaling the number found in step 1.
- If there is a remainder (there usually will be), randomly add one to items in the array and decrement the remainder until the remainder is 0. At this point we have an array which will equal the desired total, but it will be very un-random.
- For a number of iterations, randomly add and subtract from two locations in the array. Example: add 1 to position 0 and subtract 1 from position 4. In so doing, do bounds checks (all numbers should be at least 0 and all numbers should be no greater than an upper bound).
计算出可以重复的最小整数值,以便加到所需总数附近的数字。基本上,只做整数除法。
初始化一个数组,其中所有值等于步骤1中找到的数字。
如果有一个余数(通常会有),则随机地向数组中的项添加一个并减少余数,直到余数为0.此时我们有一个数组将等于所需的总数,但它将非常不合适-随机。
对于多次迭代,从数组中的两个位置随机添加和减去。示例:将1添加到位置0并从位置4减去1.这样做,进行边界检查(所有数字应至少为0,所有数字不应大于上限)。
The second method is much simpler but results in a less random distribution:
第二种方法更简单,但随机分布更少:
- Initialize an array of 0's of the desired length.
- Select a random index in the array and add 1. If the value at that index would exceed the upper bound, ignore it and select another index.
- Repeat step 2 the number of times indicated by the desired total.
初始化一个所需长度为0的数组。
在数组中选择一个随机索引并添加1.如果该索引处的值超过上限,请忽略它并选择另一个索引。
重复步骤2所需总数所指示的次数。
Here is the code:
这是代码:
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;
}
A sample run:
示例运行:
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,
These methods are understandably not as evenly distributed as one would like. It would make sense especially with the second method; if you have a random source that is truly evenly distributed, then your selection of the items to increment should have equal probability across all the possible values. The first one could potentially be a bit better also, but it still suffers from the fact that the random source is also ideally evenly distributed.
可以理解,这些方法不像人们希望的那样均匀分布。特别是第二种方法,这是有道理的;如果你有一个真正均匀分布的随机源,那么你选择要增加的项应该在所有可能的值上具有相同的概率。第一个也可能有点好一点,但它仍然受到随机源也理想地均匀分布的事实的影响。
I feel like it might be possible to improve at least the first method by introducing some form of bias into the index selection, or possibly a randomization of how much we add and subtract (not always 1), or a randomization of whether we actually do the addition/subtraction or not. Just tweaking the number of iterations seems to change the distribution, but after a while it seems that we start favoring the outer boundaries. (Perhaps it's not possible to get a truly even distribution!)
我觉得有可能通过在索引选择中引入某种形式的偏差来改进至少第一种方法,或者可能随机化我们添加和减去多少(不总是1),或随机化我们是否真的做是否加/减。只是调整迭代次数似乎会改变分布,但过了一段时间似乎我们开始偏向外部边界。 (也许不可能真正均匀分配!)
In any case, there you go...A good place to start at least.
无论如何,你去......至少是一个好的起点。
#16
-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 code likes me but its slow, it's like to throw a coin and hope to get the n value.Nice pieces of codes
JaredPar代码喜欢我,但它很慢,它就像扔硬币并希望获得n值.Nice代码片段