分配不同结果的最佳方式?

时间:2012-01-03 13:20:05

标签: c# algorithm

我有一个名为“GetValue()”的方法,它应该在每次方法调用时返回值“A”,“B”,“C”或“D”。

我希望这种方法在30%的方法调用中返回值“A”,在14%的方法调用中返回值“B”,值“C”31%..依此类推......

这是顺利分配theese值的最好方法,我不希望该方法连续返回值“A”xxx次,因为值“A”是它所请求的结果百分比中最重要的。

请所有回答者表示赞赏。

6 个答案:

答案 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)

使用Random。 照顾种子。请参阅this link

示例:

    // 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 }

只要您需要输出其中一个结果,就可以使用以下策略:

  1. 查找possibleOutcomeProbabilities中所有元素的总和。让我们称之为totalProbability
  2. 1totalProbability之间生成一个随机数。让我们称之为随机生成的数字outcomeBucket
  3. 迭代possibleOutcomeProbabilities以确定outcomeBucket对应的结果。然后,您从possibleOutcomes
  4. 中选择相应的结果

    这个策略肯定不会给你前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();            
    }