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

WebBot

Client
Регистрация
04.04.2015
Сообщения
1 763
Благодарностей
1 391
Баллы
113
Всем привет.

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

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

freeman

Client
Регистрация
31.07.2010
Сообщения
130
Благодарностей
138
Баллы
43
Код:
var rand = new Random();

if (rand.NextDouble() < 0.7)
{
    // action1
}
else
{
    // action2
}
 

WebBot

Client
Регистрация
04.04.2015
Сообщения
1 763
Благодарностей
1 391
Баллы
113
А если задачу усложнить?

Есть 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# не хватает что бы это оформить в код.

Может кто уже сталкивался и и завалялось готовое решение?
 

arhip1985

Client
Регистрация
31.10.2011
Сообщения
2 994
Благодарностей
787
Баллы
113
Код:
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

arhip1985

Client
Регистрация
31.10.2011
Сообщения
2 994
Благодарностей
787
Баллы
113
но сдаётся мне, что так точного количества вы не получите, вероятность - да.
 

arhip1985

Client
Регистрация
31.10.2011
Сообщения
2 994
Благодарностей
787
Баллы
113
Пока вижу такое решение: создать список, 70 раз туда записать строку action1, 30 раз записать строку action2 ... перемешать и брать оттуда случайное значение. Работать будет, но может есть более элегантное решение?
если надо точное количество, то это элегантное решение. мы же когда монетку подкидываем то вероятность 1/2 , но после первого подкидывания она остаётся такая же. когда кидаем 1000 раз, то может быть к примеру 456 решка и 544 орёл, чем больше раз подкидываем тем выше точность, но абсолютной точности не будет - ибо она там где 8 ) и то, в случае монетки, когда два выбора, то бесконечность должна быть чётной, чтобы работала теория вероятности 50/50 в случае орёл-решка - о - вот и парадокс))).
со списком хорошее решение - если надо точно, просто генерьте список в коде от 1 до суммы всех значений, перемешивать не надо, просто берёте случайную строку с удалением, а дальше ифы как я показал. в общем как удобно, можете и по вероятности юзать, просто пользователям тогда не говорите, что это точное количество, а говорите, что это вероятность
 

Dimionix

Moderator
Регистрация
09.04.2011
Сообщения
3 068
Благодарностей
3 122
Баллы
113
А если задачу усложнить?
Может так
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

arhip1985

Client
Регистрация
31.10.2011
Сообщения
2 994
Благодарностей
787
Баллы
113
Может так
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)))
 
Последнее редактирование:

Dimionix

Moderator
Регистрация
09.04.2011
Сообщения
3 068
Благодарностей
3 122
Баллы
113
так не покатит, потому во втором ифе вероятность будет 20 к 251, а надо 30 к сумме всех элементов и в последующих ифах тоже
Да это не важно, цифры можно любые нарисовать, главное сама суть выбора действия без всяких подсчетов общей суммы.
 

arhip1985

Client
Регистрация
31.10.2011
Сообщения
2 994
Благодарностей
787
Баллы
113
Да это не важно, цифры можно любые написать, главное сама суть выбора действия без всяких подсчетов общей суммы.
не понял( в тот то и дело, что важен подсчёт суммы, должна же быть вероятность соответствующая, или я не понял про что ты
 

Dimionix

Moderator
Регистрация
09.04.2011
Сообщения
3 068
Благодарностей
3 122
Баллы
113
не понял( в тот то и дело, что важен подсчёт суммы, должна же быть вероятность соответствующая, или я не понял про что ты
Да я сам из условия не совсем понял, что нужно. Ладно пофиг, не охота считать))
 

WebBot

Client
Регистрация
04.04.2015
Сообщения
1 763
Благодарностей
1 391
Баллы
113
@arhip1985 и @Dimionix , камрады, спасибо за ваши мысли! У самого сейчас после бессонной ночи голова вообще не варит)
Воспользуюсь решением от @arhip1985 ... мне оно видится логичным
 

freeman

Client
Регистрация
31.07.2010
Сообщения
130
Благодарностей
138
Баллы
43
Код:
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

zennomoves

Client
Регистрация
05.12.2011
Сообщения
235
Благодарностей
72
Баллы
28
Всем привет.

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

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

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

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

WebBot

Client
Регистрация
04.04.2015
Сообщения
1 763
Благодарностей
1 391
Баллы
113
Можно организовать аналог без большого количества строк. Сначала записываем все строки в таблицу - в первую ячейку название действия, во вторую число повторов.

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

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

zennomoves

Client
Регистрация
05.12.2011
Сообщения
235
Благодарностей
72
Баллы
28
Такое решение сработает (будет соблюдена вероятность) только в том случае когда таблица будет полностью пуста (все экшены уменьшат свое значения до 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

-
 

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