我正在尝试生成一组随机数字,这些数字加起来达到特定的总数,以及这些数字可以达到的特定最大值。
然而,我遇到的每种方法似乎都有一些缺陷,导致其无法使用。
有时结果加起来并不等于正确的总数。
有时随机生成每次都会产生相同的数字。
某些函数会导致迭代次数过多。
我开始认为这在数学上有点不可能?我想知道是否有人可以帮我编写代码来做到这一点。 数字应遵循以下规则:
数字之和必须等于变量 t。
生成数字的最小值为1。
最大值应为变量m。
生成的数字必须尽可能接近正态分布。
正态分布必须以 1 为中心。
正态分布应该足够平坦,几乎可以得到每个数字最大的示例。
所有数字必须是整数。
举个例子,如果 t 是 30,m 是 5,那么结果将是:
1,1,1,1,1,1,1,2,2,2,2,3,3,4,5
另一个结果可能是:
1,1,1,1,1,2,2,2,3,3,4,4,5
这是我为此提供的一个函数,但它使用了我希望避免的 while 循环,因为它通常会导致太多迭代。
public static List<int> GenerateNumbers(int t, int m)
{
double mean = 1.0;
double variance = 10.0;
double[] probabilities = new double[m];
double sumProbabilities = 0.0;
for (int i = 1; i <= m; i++)
{
double exponent = -Math.Pow(i - mean, 2) / (2 * variance);
probabilities[i - 1] = Math.Exp(exponent);
sumProbabilities += probabilities[i - 1];
}
for (int i = 0; i < m; i++)
{
probabilities[i] /= sumProbabilities;
}
double meanValue = 0.0;
for (int i = 0; i < m; i++)
{
meanValue += probabilities[i] * (i + 1);
}
int N = (int)Math.Round(t / meanValue);
int[] frequencies = new int[m];
for (int i = 0; i < m; i++)
{
frequencies[i] = (int)Math.Round(probabilities[i] * N);
}
for (int i = 1; i < m; i++)
{
if (frequencies[i] > frequencies[i - 1])
{
frequencies[i] = frequencies[i - 1];
}
}
int currentSum = 0;
for (int i = 0; i < m; i++)
{
currentSum += frequencies[i] * (i + 1);
}
int maxIterations = 25000;
int iterationCount = 0;
while (currentSum != t && iterationCount < maxIterations)
{
iterationCount++;
if (currentSum < t)
{
for (int i = 0; i < m; i++)
{
if (frequencies[i] < (i == 0 ? frequencies[0] : frequencies[i - 1]))
{
frequencies[i]++;
currentSum += (i + 1);
break;
}
}
}
else
{
for (int i = m - 1; i >= 0; i--)
{
if (frequencies[i] > 0)
{
frequencies[i]--;
currentSum -= (i + 1);
break;
}
}
}
}
if (iterationCount >= maxIterations)
{
Console.WriteLine("Failed to adjust frequencies to match t within max iterations.");
}
List<int> numbers = new List<int>();
for (int i = 0; i < m; i++)
{
for (int j = 0; j < frequencies[i]; j++)
{
numbers.Add(i + 1);
}
}
System.Random rand = new System.Random();
numbers = numbers.OrderBy(x => rand.Next()).ToList();
return numbers;
}
我该怎么办?
由于限制,相当棘手,但可以尝试这个:
将数字四舍五入为整数。 计算 t 与数字之和之间的差。 如果总和小于 t,则对某些数字(最好是小于 m 的数字)加 1,直到总数与 t 匹配。 如果总和大于 t,则从一些数字(大于 1 的数字)中减去 1,直到总和与 t 匹配。
public static List<int> GenerateNumbers(int t, int m)
{
double mean = 1.0;
double stdDev = 1.0;
int N = (int)Math.Round(t / mean);
List<double> numbers = new List<double>();
Random rand = new Random();
for (int i = 0; i < N; i++)
{
double num = NextGaussian(rand, mean, stdDev);
if (num < 1) num = 1;
if (num > m) num = m;
numbers.Add(num);
}
List<int> intNumbers = numbers.Select(x => (int)Math.Round(x)).ToList();
int currentSum = intNumbers.Sum();
int iterationCount = 0;
int maxIterations = 10000;
while (currentSum != t && iterationCount < maxIterations)
{
iterationCount++;
if (currentSum < t)
{
for (int i = 0; i < intNumbers.Count; i++)
{
if (intNumbers[i] < m)
{
intNumbers[i]++;
currentSum++;
break;
}
}
}
else if (currentSum > t)
{
for (int i = 0; i < intNumbers.Count; i++)
{
if (intNumbers[i] > 1)
{
intNumbers[i]--;
currentSum--;
break;
}
}
}
}
if (iterationCount >= maxIterations)
{
Console.WriteLine("Failed to adjust numbers to match t within max iterations.");
}
intNumbers = intNumbers.OrderBy(x => rand.Next()).ToList();
return intNumbers;
}
public static double NextGaussian(Random rand, double mean, double stdDev)
{
double u1 = rand.NextDouble();
double u2 = rand.NextDouble();
double randStdNormal = Math.Sqrt(-2.0 * Math.Log(u1)) * Math.Cos(2.0 * Math.PI * u2);
double randNormal = mean + stdDev * randStdNormal;
return randNormal;
}