2012-01-03 2 views
2

У меня есть метод под названием GetValue(), который должен возвращать значения «A», «B», «C» или «D» для каждого вызова метода.Лучший способ распространения различных результатов?

Я хочу, чтобы этот метод возвращал значение «А» в 30% вызовов метода и значение «В» в 14% вызовов метода, значение «С» 31% .. и так далее.

Wich - лучший способ распределить эти значения плавно, я не хочу, чтобы метод возвращал значение «A» xxx раз в строке, потому что значение «A» далеки от его запрошенного процента результата.

Пожалуйста, все ответы оцениваются.

ответ

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

Хотя если вы новичок в «Случайном», это может быть сложно сделать правильно. http://csharpindepth.com/Articles/Chapter12/Random.aspx – Oded

+0

Случайный не будет работать, если он хочет, чтобы результаты соответствовали приблизительной ставке «попадания» 30%, 14%, 31% и т. д. (?) - У вас нет решения, но Random не является ответом на проблему. –

+4

@ K.Bob Да, это - Random генерирует числа, которые обычно распространяются. –

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"; 
     } 
    } 
+0

Почему парни? 'Random' может генерировать int. Почему бы не использовать это? – spender

+0

Вы правы, Дальше (n) достаточно. Ред. – ken2k

+0

Если вы засеете случайный генератор со значением константы, он всегда будет генерировать одну и ту же последовательность, поэтому он не является случайным вообще. – Guffa

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. 

} 

EDIT: просто убедитесь, что случайная переменная создается за пределами функции или вы получите то же значение каждый раз.

+0

Он возвращает число. что-то вроде. это действительно не имеет значения, я не пытался его написать, я пытался указать ему в правильном направлении, и, за исключением возможного броска, код правильный. – LoveMeSomeCode

+0

снова, все в порядке. У него есть документация.Ему не нужно, чтобы кто-то держал его за руку и выбирал каждую перегрузку и каждый параметр для него. Он может вводить код и видеть, что есть следующие методы для этого класса для парных, ints и т. Д. Если бы я боролся с этой концепцией, и кто-то дал мне это решение, он закрыл бы 99% пробела, и у меня было бы что Мне было нужно. Я действительно не рассматриваю этот сайт как место, где люди могут написать свой код для меня. Я вижу все образцы кода в виде псевдокода, чтобы указать людям в правильном направлении. – LoveMeSomeCode

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) и верните один элемент из массива для каждого вызова метода.

2

Допустим, у вас есть массивы
String[] possibleOutcomes = new String[] { "A", "B", "C", "D" }
и
int[] possibleOutcomeProbabilities = new int[] { 30, 14, 31, 25 }

Вы можете использовать следующую стратегию всякий раз, когда вам необходимо вывести один из результатов:

  1. Найти сумму все элементы в possibleOutcomeProbabilities. Позволяет называть эту сумму totalProbability.
  2. Введите произвольное число от 1 и totalProbability. Позволяет называть это случайно сгенерированное число outcomeBucket.
  3. Итерат над possibleOutcomeProbabilities, чтобы определить, какой результат outcomeBucket соответствует. Затем вы выбираете соответствующий результат из possibleOutcomes.

Эта стратегия, конечно же, не даст вам первых 30% результатов, таких как A, следующие 14% как B и т. Д. Однако, по мере того, как вероятность работает над достаточно большим количеством результатов, эта стратегия гарантирует, что ваши возможные результаты распределяются по ожидаемым вероятностям. Эта стратегия дает вам преимущество в том, что вероятности результата не должны составлять до 100%. Вы даже можете указать относительные вероятности, например, 1: 2: 3: 4 и т.д.

Если вы действительно беспокоитесь о самой быстрой реализации для стратегии, вы можете настроить его следующим образом:

, Вычислить totalProbability только один раз, или когда вероятность изменена. b. Перед вычислением totalProbability проверьте, имеют ли элементы в possibleOutcomeProbabilities общие делители и устраняют их. Это даст вам пространство с меньшей вероятностью, которое будет проходить каждый раз.

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();    
    } 
+0

Interresting, но, возможно, слишком сложный ... Во всяком случае, вы можете использовать целые числа для вероятностей, если добавить кучу значений с плавающей запятой, вы, скорее всего, получите то, что не соответствует 1.0. – Guffa

Смежные вопросы