Random с определенной вероятностью выпадения значений

  • Автор темы Автор темы WebBot
  • Дата начала Дата начала

WebBot

Client
Регистрация
04.04.2015
Сообщения
1 834
Реакции
1 418
Баллы
113
Всем привет.

Задача примерно такая: есть некий набор действий ( action1, action2 ). Нужно выбирать случайное действие, но не с вероятностью 50 на 50 ... а допустим 70 на 30. То есть при рандоме action1 должен выпасть с вероятностью 70%, а action2 с вероятностью 30%.

Пока вижу такое решение: создать список, 70 раз туда записать строку action1, 30 раз записать строку action2 ... перемешать и брать оттуда случайное значение. Работать будет, но может есть более элегантное решение?
 
Код:
Развернуть Свернуть Копировать
var rand = new Random();

if (rand.NextDouble() < 0.7)
{
    // action1
}
else
{
    // action2
}
 
А если задачу усложнить?

Есть 5 действий, количество выполнения которых задается пользователем через входные настройки.
Допустим пользователь задал так:
Действие1 - 250
Действие2- 30
Действие3- 50
Действие4- 10
Действие5- 90

Нужно выбирать случайное одно действие, но с вероятностью пропорциональной его вкладу в общую копилку действий.

Всего действий получается: 250+30+50+10+90 = 430

Соответственно если за 100% считаем 430, то можно посчитать долю каждого действия в %;
Действие1 = 250*100/430 = 58,13953%
Действие2 = 30*100/430 = 6,976744%
Действие3 = 50*100/430 = 11,62791%
Действие4 = 10*100/430 = 2,325581%
Действие5 = 90*100/430 = 20,93023%

Вот с такой вероятностью и хотелось бы получать эти действия при выборе одного варианта.

Догадываюсь что тут нужно составить интервалы и соответствующие им действия, а потом смотреть в какой интервал попадет обычный рандом и брать соответствующее действие..... но знаний c# не хватает что бы это оформить в код.

Может кто уже сталкивался и и завалялось готовое решение?
 
Код:
Развернуть Свернуть Копировать
var rand = new Random();
int big_bang=rand.Next(1, 431);
if (big_bang <= 10)
{
    // action4
}
else if (big_bang <= 40)
{
    // action2
}
else if (big_bang <= 90)
{
    // action3
}
else if (big_bang <= 180)
{
    // action5
}
else
{
    // action1
}
 
  • Оценить
Реакции: WebBot
Пока вижу такое решение: создать список, 70 раз туда записать строку action1, 30 раз записать строку action2 ... перемешать и брать оттуда случайное значение. Работать будет, но может есть более элегантное решение?
если надо точное количество, то это элегантное решение. мы же когда монетку подкидываем то вероятность 1/2 , но после первого подкидывания она остаётся такая же. когда кидаем 1000 раз, то может быть к примеру 456 решка и 544 орёл, чем больше раз подкидываем тем выше точность, но абсолютной точности не будет - ибо она там где 8 ) и то, в случае монетки, когда два выбора, то бесконечность должна быть чётной, чтобы работала теория вероятности 50/50 в случае орёл-решка - о - вот и парадокс))).
со списком хорошее решение - если надо точно, просто генерьте список в коде от 1 до суммы всех значений, перемешивать не надо, просто берёте случайную строку с удалением, а дальше ифы как я показал. в общем как удобно, можете и по вероятности юзать, просто пользователям тогда не говорите, что это точное количество, а говорите, что это вероятность
 
А если задачу усложнить?
Может так
C#:
Развернуть Свернуть Копировать
Random rnd = new Random();
int r = rnd.Next(0, 251);

if (r <= 10)
{
    // action4
}
else if (r > 10 && r <= 30)
{
    // action2
}
else if (r > 30 && r <= 50)
{
    // action3
}
else if (r > 50 && r <= 90)
{
    // action5
}
// action1
 
  • Оценить
Реакции: karkun15, demin и alekwuy
Может так
C#:
Развернуть Свернуть Копировать
Random rnd = new Random();
int r = rnd.Next(0, 251);

if (r <= 10)
{
    // action4
}
else if (r > 10 && r <= 30)
{
    // action2
}
else if (r > 30 && r <= 50)
{
    // action3
}
else if (r > 50 && r <= 90)
{
    // action5
}
// action1
так не покатит, потому во втором ифе вероятность будет 20 к 251, а надо 30 к сумме всех элементов и в последующих ифах тоже, поэтому я их просуммировал а потом брал не пересекающиеся диапазоны в рамках требуемого количества.
иф для третьего экшена, который должен быть 50 раз из 250 у тебя вообще от 30 до 50 , то бишь 20 раз из 250)))
 
Последнее редактирование:
так не покатит, потому во втором ифе вероятность будет 20 к 251, а надо 30 к сумме всех элементов и в последующих ифах тоже
Да это не важно, цифры можно любые нарисовать, главное сама суть выбора действия без всяких подсчетов общей суммы.
 
Да это не важно, цифры можно любые написать, главное сама суть выбора действия без всяких подсчетов общей суммы.
не понял( в тот то и дело, что важен подсчёт суммы, должна же быть вероятность соответствующая, или я не понял про что ты
 
не понял( в тот то и дело, что важен подсчёт суммы, должна же быть вероятность соответствующая, или я не понял про что ты
Да я сам из условия не совсем понял, что нужно. Ладно пофиг, не охота считать))
 
@arhip1985 и @Dimionix , камрады, спасибо за ваши мысли! У самого сейчас после бессонной ночи голова вообще не варит)
Воспользуюсь решением от @arhip1985 ... мне оно видится логичным
 
Код:
Развернуть Свернуть Копировать
int act1 = int.Parse(project.Variables["action1"].Value); // 250
int act2 = int.Parse(project.Variables["action2"].Value); // 30
int act3 = int.Parse(project.Variables["action3"].Value); // 50
int act4 = int.Parse(project.Variables["action4"].Value); // 10
int act5 = int.Parse(project.Variables["action5"].Value); // 90

int total = act1 + act2 + act3 + act4 + act5;

int[] variants = new int[total];

for (int i = 0; i < act1; i++)
    variants[i] = 1;

for (int i = act1; i < (act1 + act2); i++)
    variants[i] = 2;

for (int i = (act1 + act2); i < (act1 + act2 + act3); i++)
    variants[i] = 3;

for (int i = (act1 + act2 + act3); i < (act1 + act2 + act3 + act4); i++)
    variants[i] = 4;

for (int i = (act1 + act2 + act3 + act4); i < variants.Length; i++)
    variants[i] = 5;

var rand = new Random();
int randNumber = variants[rand.Next(variants.Length)];

switch (randNumber)
{
    case 1:
        // action1
        break;
    case 2:
        // action2
        break;
    case 3:
        // action3
        break;
    case 4:
        // action4
        break;
    case 5:
        // action5
        break;
}
 
  • Оценить
Реакции: Sanekk, alekwuy и WebBot
Всем привет.

Задача примерно такая: есть некий набор действий ( action1, action2 ). Нужно выбирать случайное действие, но не с вероятностью 50 на 50 ... а допустим 70 на 30. То есть при рандоме action1 должен выпасть с вероятностью 70%, а action2 с вероятностью 30%.

Пока вижу такое решение: создать список, 70 раз туда записать строку action1, 30 раз записать строку action2 ... перемешать и брать оттуда случайное значение. Работать будет, но может есть более элегантное решение?

Можно организовать аналог без большого количества строк. Сначала записываем все строки в таблицу - в первую ячейку название действия, во вторую число повторов.

В работе берём случайную строку в переменные с удалением её из таблицы. Название действия используем для определения какое именно действие совершать, а число повторов сравниваем с нулём. Если число повторов не равно 0, то действие выполняется и из переменной с числом повторов вычитается 1. Затем строка возвращается в таблицу, только теперь в количестве повторов для этого действия будет на 1 меньше.

Если при очередном заборе строки оказывается, что число повторов = 0, то действие не совершается и строка в таблицу не возвращается, а просто будет возврат к взятию строки из таблицы. Соответственно это действие уже больше не будет принимать участие в выборе.
 
Последнее редактирование:
Можно организовать аналог без большого количества строк. Сначала записываем все строки в таблицу - в первую ячейку название действия, во вторую число повторов.

В работе берём случайную строку в переменные с удалением её из таблицы. Название действия используем для определения какое именно действие совершать, а число повторов сравниваем с нулём. Если число повторов не равно 0, то действие выполняется и из переменной с числом повторов вычитается 1. Затем строка возвращается в таблицу, только теперь в количестве повторов для этого действия будет на 1 меньше.

Если при очередном заборе строки оказывается, что число повторов = 0, то действие не совершается и строка в таблицу не возвращается, а просто будет возврат к взятию строки из таблицы. Соответственно это действие уже больше не будет принимать участие в выборе.
Такое решение сработает (будет соблюдена вероятность) только в том случае когда таблица будет полностью пуста (все экшены уменьшат свое значения до 0 и не будут возвращены в таблицу). А если нужно взять значение с нужной вероятность всего 1 раз, то это будет по сути обычный рандом.
 
Такое решение сработает (будет соблюдена вероятность) только в том случае когда таблица будет полностью пуста (все экшены уменьшат свое значения до 0 и не будут возвращены в таблицу). А если нужно взять значение с нужной вероятность всего 1 раз, то это будет по сути обычный рандом.

А, так тебе вон чего нужно :-), тогда так:

1. Считаем сумму всех действий. Затем рандомно выбираем из этой суммы число (что можно сопоставить со случайной строчкой из списка в твоём примере).
2. Из всех чисел повторов действий определяем наибольшее, потому что у него максимальная вероятность перекрыть диапазон, в которое попадёт число, полученное в п.1. Сравниваем число из п.1 с нашим наибольшим. Если число из п.1 меньше нашего наибольшего, это будет означать, что вероятность уже сработала, и мы выполняем действие, соответствующее этому числу повторов. Затем вычитаем из большего числа повторов 1 и возвращаем в массив данных.
--------------------------------------------------------------------------------------------------------------------------
Пояснение:

Например у нас для простоты всего 2 числа: 90 и 10. Сумма будет 100. Рандомно генерим число от 0 до 100. Вероятность получения числа больше 90 - 10% из 100%. Т.е. у нас прямое соответствие между числами и их процентным отношением в общей сумме. При таком подходе мы чётко попадаем в заданную вероятность, исходя из числа заданных нами повторов.

Если у нас большее кол-во чисел, то соотношение тоже соблюдается. В примере использую 100 для простоты сопоставления. Всё это же будет соблюдаться при любых значениях чисел. Например: 48, 92, 589 и т.д. Сумма - 729. Каждое отдельное число будет занимать процент от суммы, который напрямую соотносится с его значением.
-------------------------------------------------------------------------------------------------------------------------

В случае, если рандомное число не меньше нашего большего мы переходим к следующему этапу - вычитаем из общей суммы повторов действий большее число, затем генерим от 0 до полученного и опять сравниваем его с наибольшим из оставшихся. И так до бесконечности - пока не закончатся все числа и станется одно последнее, которое не с чем будет сравнивать.

Также придётся обработать вариант, когда несколько чисел в массиве повторов будут одинаковыми.

Всё это делать в цикле до тех пор пока все числа повторов не превратятся в нули за счёт вычитание по 1 из одного из них при каждом повторе общей петли цикла.

P.S. Получение большего из нескольких чисел можно тут посмотреть попробовать: http://www.cyberforum.ru/csharp-beginners/thread1381018.html

-
 

Кто просматривает тему: (Всего: 0, Пользователи: 0, Гости: 0)