我有一个名为“GetValue()”的方法,它应该在每次方法调用时返回值“A”,“B”,“C”或“D”。
我希望这种方法在30%的方法调用中返回值“A”,在14%的方法调用中返回值“B”,值“C”31%..依此类推......
这是顺利分配theese值的最好方法,我不希望该方法连续返回值“A”xxx次,因为值“A”是它所请求的结果百分比中最重要的。
请所有回答者表示赞赏。
答案 0 :(得分:9)
您可以使用Random class来实现此目的:
private static Random Generator = new Random();
public string GetValue()
{
var next = Generator.Next(100);
if (next < 30) return "A";
if (next < 44) return "B";
if (next < 75) return "C";
return "D";
}
<强>更新强>
对于更通用的随机加权值存储,以下可能是一个很好的起点:
public class WeightedValueStore<T> : IDisposable
{
private static readonly Random Generator = new Random();
private readonly List<Tuple<int, T>> _values = new List<Tuple<int, T>>();
private readonly ReaderWriterLockSlim _valueLock = new ReaderWriterLockSlim();
public void AddValue(int weight, T value)
{
_valueLock.EnterWriteLock();
try
{
_values.Add(Tuple.Create(weight, value));
}
finally
{
_valueLock.ExitWriteLock();
}
}
public T GetValue()
{
_valueLock.EnterReadLock();
try
{
var totalWeight = _values.Sum(t => t.Item1);
var next = Random.Next(totalWeight);
foreach (var tuple in _values)
{
next -= tuple.Item1;
if (next < 0) return tuple.Item2;
}
return default(T); // Or throw exception here - only reachable if _values has no elements.
}
finally
{
_valueLock.ExitReadLock();
}
}
public void Dispose()
{
_valueLock.Dispose();
}
}
然后可以这样使用:
public string GetValue()
{
using (var valueStore = new WeightedValueStore<string>())
{
valueStore.AddValue(30, "A");
valueStore.AddValue(14, "B");
valueStore.AddValue(31, "C");
valueStore.AddValue(25, "D");
return valueStore.GetValue();
}
}
答案 1 :(得分:3)
示例:
// You can provide a seed as a parameter of the Random() class.
private static Random RandomGenerator = new Random();
private static string Generate()
{
int value = RandomGenerator.Next(100);
if (value < 30)
{
return "A";
}
else if (value < 44)
{
return "B";
}
else
{
return "C";
}
}
答案 2 :(得分:2)
如果您想要平均分配,您可以选择一个随机数并进行检查。
Random rnd = new Random();
int value = rnd.Next(100); // get a number in the range 0 - 99
if (value < 30) return "A";
if (value < 30+14) return "B";
if (value < 30+14+31) return "C";
return "D";
请注意,您应该创建一次随机生成器,并将其重新用于后续调用。如果每次都创建一个新的,如果两个方法调用过于接近,它们将使用相同的随机序列进行初始化。
如果您希望完全分配100个项目,那么您将创建一个包含100个项目的数组,其中30个为“A”,14个为“B”,依此类推。对数组进行随机播放(查找Fisher-Yates),并为每个方法调用返回数组中的一个项目。
答案 3 :(得分:2)
假设你有阵列
String[] possibleOutcomes = new String[] { "A", "B", "C", "D" }
和
int[] possibleOutcomeProbabilities = new int[] { 30, 14, 31, 25 }
只要您需要输出其中一个结果,就可以使用以下策略:
possibleOutcomeProbabilities
中所有元素的总和。让我们称之为totalProbability
。1
和totalProbability
之间生成一个随机数。让我们称之为随机生成的数字outcomeBucket
。possibleOutcomeProbabilities
以确定outcomeBucket
对应的结果。然后,您从possibleOutcomes
。这个策略肯定不会给你前30%的结果作为A,接下来14%作为B等等。但是,随着概率起作用,在足够多的结果中,这个策略将确保你的可能结果分配为根据他们预期的概率。此策略为您提供的优势是,结果概率不需要加起来达到100%。您甚至可以指定相对概率,例如1:2:3:4等。
如果您真的担心策略的最快实施,可以按如下方式进行调整:
一个。仅计算totalProbability
一次,或者在更改概率时计算totalProbability
。
湾在计算possibleOutcomeProbabilities
之前,请查看{{1}}中的元素是否具有任何公约数,并消除它们。这将为您提供每次遍历的较小概率空间。
答案 4 :(得分:1)
试试这个:
Random r = new Random();
private string GetValue()
{
double d = r.Next();
if(d < 0.3)
return "A";
else if(d < 0.5)
return "B";
...etc.
}
编辑:只需确保在函数外部创建Random变量,或者每次都获得相同的值。
答案 5 :(得分:1)
我不推荐任何硬编码方法(很难维护,这是不好的做法)。我更喜欢更通用的解决方案。
enum PossibleOutcome { A, B, C, D, Undefined }
// sample data: possible outcome vs its probability
static readonly Dictionary<PossibleOutcome, double> probabilities = new Dictionary<PossibleOutcome, double>()
{
{PossibleOutcome.A, 0.31},
{PossibleOutcome.B, 0.14},
{PossibleOutcome.C, 0.30},
{PossibleOutcome.D, 0.25}
};
static Random random = new Random();
static PossibleOutcome GetValue()
{
var result = random.NextDouble();
var sum = 0.0;
foreach (var probability in probabilities)
{
sum += probability.Value;
if (result <= sum)
{
return probability.Key;
}
}
return PossibleOutcome.Undefined; // it shouldn't happen
}
static void Main(string[] args)
{
if (probabilities.Sum(pair => pair.Value) != 1.0)
{
throw new ApplicationException("Probabilities must add up to 100%!");
}
for (var i = 0; i < 100; i++)
{
Console.WriteLine(GetValue().ToString());
}
Console.ReadLine();
}