Annual Open Tech Conference - ISsoft Insights 2021. June 19. Learn more.
×Закрыть

Код управляемого псевдо-рандома от новичка (изобрел велосипед?)

Задача была такой.

Есть range от $min до $max. Необходимо создать генератор, выдающий числа в определенных границах в соответствии с настройками пользователя. Если $min == 1, a $max == 80, функция должна иметь возможность генерировать, N чисел от 1 до 10, M от 10 до 20 и так далее.

Старое решение было через Ж..пу.
Новый вариант алгоритма предложил Mike, вот решение на его основе:

<?php
//создаем сколько угодно диапазонов, со счетчиком, частотой появления и верхней границей в отношении к верхнему пределу общего рандома
$counts = array (		
                                //например, до 9% от максимум 1 раз из 100										
				'0' => array ('count' => 0, 'ratio' => 0.01, 'amount' => 0.09),
                                //до 50% - 20 из 100
				'1' => array ('count' => 0, 'ratio' => 0.20, 'amount' => 0.50),
				'2' => array ('count' => 0, 'ratio' => 0.50, 'amount' => 0.75),
	                        //count - счетчик срабатываний
				'3' => array ('count' => 0, 'ratio' => 0.29, 'amount' => 1),
                                //total - общий счетчик
				'total' => 1									
				);

//функция проверки на превышение частоты появления
function check($count_to_check) {		
	global $counts;
	if ($counts[$count_to_check]['count']/$counts['total'] < $counts[$count_to_check]['ratio']) return True;
	else return False;
}

//функция корректировки счетчиков
function plus($count_to_plus) {			
	global $counts;
	$counts[$count_to_plus]['count']++;
	$counts['total']++;
}

//сам генератор теперь такой
function gen_price($min,$max) {
	global $counts;
//выбрали диапазон
	$gen_d = round(rand(0,count($counts)-2),-0.5);
//проверили соответствие нужной частоте появления
	if (check($gen_d)) {									
		plus($gen_d);
//если первый даипазон, генерим от минимума до его предела
		if ($gen_d==0) return round(rand($min,$counts[$gen_d]['amount']*$max),-0.5);
//иначе от предела предыдущего диапазона до своего предела
		return round(rand($counts[$gen_d-1]['amount']*$max,$counts[$gen_d]['amount']*$max),-0.5);
	}	
}

//проверяем
$min = 1; $max = 100;
for ($tries = 0; $tries < 100; $tries++) { 
	$price_gen = 0;
        //без while иногда пропуски были, более быстрого решения не придумал
	while (!$price_gen) $price_gen = gen_price($min,$max);
	echo $price_gen . " ";										
}
?>
👍НравитсяПонравилось0
В избранноеВ избранном0
LinkedIn

Похожие топики

Допустимые теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Допустимые теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter

Вот еще вариант: строите дерево отрезков. Каждый из ваших элементов занимает кусок от 0 до 1 длиной его ratio. Потом просто генерируете каждый раз число от 0 до 1 и выбираете по этому ключу нужный элемент.

Поддерживаю. Если база используется в качестве хранилища, то проще всего сгенерировать готовые списки случайных величин с правильным распределением и выбирать случайное число уже оттуда.
Например, для postgres.

test=# \d user_interval
       Table "public.user_interval"
   Column   |       Type       | Modifiers 
------------+------------------+-----------
 user_id    | integer          | 
 low_value  | double precision | 
 high_value | double precision | 
 num_values | integer          | 

est=# select * from user_interval;
 user_id | low_value | high_value | num_values 
---------+-----------+------------+------------
       1 |         1 |         10 |         50
       1 |        10 |         40 |         35
       1 |        40 |         80 |         14
       1 |        80 |        100 |          1
(4 rows)

test=# \d random_number
          Table "public.random_number"
     Column      |       Type       | Modifiers 
-----------------+------------------+-----------
 user_id         | integer          | 
 num_id          | integer          | 
 interval_num_id | integer          | 
 gen_number      | double precision | 

test=# with t as (select user_id, low_value, high_value, num_values from user_interval where user_id=1)
test-# insert into random_number(user_id, num_id, interval_num_id, gen_number)
test-# select user_id, row_number() over(partition by user_id order by user_id) as num_id, interval_num_id, random()*(high_value-low_value) + low_value as gen_number from (
test(# select generate_series(1, t.num_values) as interval_num_id, low_value, high_value, user_id from t) as series;
INSERT 0 100
test=# 
test=# select num_id, interval_num_id, gen_number from random_number where user_id=1 and num_id=(
test(# select trunc(random()*gen_number_cnt + 1)  from (
test(# select user_id, count(*) as gen_number_cnt from random_number where user_id=1
test(# group by user_id) as rn
test(# );
 num_id | interval_num_id |    gen_number    
--------+-----------------+------------------
     16 |              16 | 3.43868916714564
(1 row)

test=# select num_id, interval_num_id, gen_number from random_number where user_id=1 and num_id=(
select trunc(random()*gen_number_cnt + 1)  from (
select user_id, count(*) as gen_number_cnt from random_number where user_id=1
group by user_id) as rn
);
 num_id | interval_num_id |    gen_number    
--------+-----------------+------------------
     65 |              15 | 19.8226240836084

Сгенерировать можно либо для 1 пользователя, либо для всех сразу.
Если требуется при этом, чтобы выдавались числа ровно в такой же пропорции, как сгенерированы, то можно записывать в отдельную таблицу уже выданные числа и не выдавать в следующий раз, запрос тогда изменится соответствующим образом.
Такую же шнягу или подобную можно написать и на пхп, но я последние лет 10 на нем не писал точно, поэтому даже не буду пытаться.
Да, если требуется случайная величина с большим периодом, то Mersenne Twister или вихрь Мерсенна, как его называют, работает намного лучше. Если особых требований к генератору нет, то и обычный рандом пойдет.

Решение понятно. Для решения моего задания тут пойдет, но для серьезного реального проекта. У меня на сайте 100 товаров, и пока что 1000-2000 человек в день, генерировать заранее для всех товаров я не могу. Хотя, могу, например при добавлении товара будет формироваться таблица его цен, но я тогда не смогу на лету добавлять новые условия. Например пришел доход 300грн и я хочу чтоб генератор пропускал к выводу цену в минус до 300/3=100грн, кто-то купил товар в минус — генератор должен перестать выдавать такие цены.

Решение уже найдено, но я пока остался на первоначальном своем варианте с кучей if — иначе там не перекрутишь, логика такая.

А у Вас на каждый товар и для каждого пользователя своя скидка что ли?

Нет. Но у каждого товара есть минимум 1грн общий, а есть личный вход и розничная цена. В минус надо уходить только при деньгах на балансе, и людям приятно и мы не умрем. А так генерировать надо от входа до розницы. Как тут может помочь заранее сгенерированный массив? Может я и туплю в час ночи.

Я ничего не понял, если честно. Скидка генерируется для товара в зависимости от цены и от чего-то еще?

это уже новая задача. вам точно интересно?
положим товар мышка, вход 80 грн, розница 100грн
общий рандом работает от 1 грн до розницы на товар. если сгенерилось больше входа 80 грн, то проверяет насколько больше, на какую часть от полной наценки, 90грн это 0,5 от наценки. полная наценка у каждого товара своя и == розница-вход, на чем-то 5%, на чем-то 30%, тут правила нет. если сгенерило 60грн — меньше входа, коэффициентом считать не часть от наценки, а часть от входа на товар. для вашего алгоритма это неважно? разве не прийдется генерировать массив из 1000 цен для каждого товара и потом все-равно проверять, можно ли отдавать товар в минус в реальном времени и или пропускать или не пропускать такую цену как 60грн? да и насколько четко ваш вариант будет соответствовать правилам? даже если в сгенерированной 1000 цен все четко по весам, то во время выборки рандомом из них можно два раза попасть на N-ный элемент. Или если выдало цену то исключить ее из этой 1000, а по истечению генерировать еще одну тысячу? моего опыта не хватает чтобы понять ваш код сходу, но мне кажется мы делаем одно и тоже с разных сторон и ускорить код это не поможет.

То есть, пользователи по боку, в исходных данных цена, по которой товар оприходовался, и норма наценки, исходя из этих двух величин и генерируются диапазоны для скидки, я правильно понимаю?
Эти величины известны (или должны быть известны) в момент загрузки товара в базу, правильно? В зависимости от того сколько предполагается товаров и сколько «диапазонов» (если других параметров нет), то размер таблицы (максимальный) будет равен максимальному количеству товаров, умноженному на максимальное количество «диапазонов».
Udate: не «диапазонов», конечно, а число случайных чисел, входящих в максимальный набор.

даже если в сгенерированной 1000 цен все четко по весам, то во время выборки рандомом из них можно два раза попасть на N-ный элемент. Или если выдало цену то исключить ее из этой 1000, а по истечению генерировать еще одну тысячу? моего опыта не хватает чтобы понять ваш код сходу, но мне кажется мы делаем одно и тоже с разных сторон и ускорить код это не поможет.
Если требуется при этом, чтобы выдавались числа ровно в такой же пропорции, как сгенерированы, то можно записывать в отдельную таблицу уже выданные числа и не выдавать в следующий раз, запрос тогда изменится соответствующим образом.
test=# drop table random_number;
DROP TABLE
Time: 24.053 ms
test=# create table random_number(product_id integer, 
test(# num_id integer, 
test(# interval_num_id integer, 
test(# gen_number float);
CREATE TABLE
Time: 42.615 ms
test=# create table selected_number(product_id integer, 
test(# num_id integer, 
test(# used_date date default current_date, 
test(# gen_number float
test(# );
CREATE TABLE
Time: 80.660 ms

test=# with t as (select d.a as product_id, low_value, high_value, num_values from user_interval,  generate_series(100,200) AS d(a))
test-# insert into random_number(product_id, num_id, interval_num_id, gen_number)
test-# select product_id, row_number() over(partition by product_id order by product_id) as num_id, interval_num_id, random()*(high_value-low_value) + low_value as gen_number from (
test(# select generate_series(1, t.num_values) as interval_num_id, low_value, high_value, product_id from t) as series;
INSERT 0 10100
Time: 264.654 ms
test=# truncate table random_number;
TRUNCATE TABLE
Time: 2.276 ms
test=# with t as (select d.a as product_id, low_value, high_value, num_values from user_interval,  generate_series(100,2000) AS d(a))
test-# insert into random_number(product_id, num_id, interval_num_id, gen_number)
test-# select product_id, row_number() over(partition by product_id order by product_id) as num_id, interval_num_id, random()*(high_value-low_value) + low_value as gen_number from (
test(# select generate_series(1, t.num_values) as interval_num_id, low_value, high_value, product_id from t) as series;
INSERT 0 190100
Time: 3691.419 ms

3 секунды — время генерации для 1900 товаров, 4 диапазонов и 100 сгенерированных случайных величин.
Запрос на исключение уже выбранных случайных величин, который записываются в selected_number, можете написать сами.

Пока это три диапазона минусовых — ниже входа, 5 плюсовых. И все-таки если переносить отработанные цены в еще один массив, читай одну строку базы, а потом генерировать новую 1000 каждые 1000 кликов и очищать массив использованных цен это будет увеличивать базу. При каждом клике все равно лезть в базу и удалять одну цену из 1000, и вносить ее в базу отработанных цен. А если администратор решил поменять настройки диапазонов, их размеры или частоты, снова генерить тысячи для всех товаров? Поменялась розница или вход на товар снова генерить 1000? Звучит эффективнее моего решения в случае с большими нагрузками, но на сколько?

Я все равно не очень понимаю и саму бизнес-задачу, и подход к решению тоже, прямо скажем.
На всякий случай скажу, что удалять ничего не нужно. Только если закончились все случайные числа, считай, все числа из набора находятся в базе отработанных за текущий период. И то можно подумать, как сделать это эффективнее.
Всю эту хренотень можно сделать независимой от цены, используя только норму рентабельности.
Например, базовая рентабельность 30%. Все случайные числа в этом случае — процент от рентабельности, три диапазона в отрицательную сторону, пять в положительную. В этом случае это надо учитывать дополнительным множителем (1, −1). Расчет конечной цены, равно как и скидки по отношению цены тогда ведется в процессе выдачи этой информации покупателю.
Вообще я думал, все эти скидки имеют смысл в процессе покупки. Если при каждой демонстрации товара генерируется, по сути, случайная цена и типа каждый раз немного другая, то это удивительный бизнес-процесс. Но даже если это так, то смысла регенерирования я вообще не вижу. Частота показа тех или иных цен будет стремиться со временем к той вероятности, которая задается типа таблицей распределения.

Рентабельность у всех товаров разная. А генерировать не цены, а коэффициенты тут уже было предложено, к тому и пришли что генерировать 100 или 1000 коэффициентов (смотря как точно нужно), а при выдаче клиенту только множить на них верхний предел цены. Вместо удаления отработанных просто сохранять индекс последнего использованного элемента и перемешивать диапазоны в конце круга, по истечению этих 100 или 1000 элементов.

Странность бизнес-процессов характерна всему новому, пока не станет популярным. И за нее хорошо платят.

Еще раз, я предложил в данном случае использовать коэффициенты для рентабельности, чтобы убрать зависимость от себестоимости и конечной цены. То есть, считать «процент от процента» для положительных диапазонов, и «процент от себестоимости» для отрицательных.

Вместо удаления отработанных просто сохранять индекс последнего использованного элемента и перемешивать диапазоны в конце круга, по истечению этих 100 или 1000 элементов.
Я в этом не вижу смысла, так как на то оно и случайное число чтобы выдаваться случайным образом. Если есть набор из 100, 1000 или неважно сколько, созданных в соответствии с Вашим «бакетами», по сути, эти диапазоны — это бакеты, которые задают неравномерность распределения, и вся такая таблица задает свою функцию распределения с уже сгенерированными значениями. Дальше если брать рандомом оттуда значения, то частота использования того или иного элемента будет стремиться к заданной со временем.

Если удалять отработанные и генерить заново, тогда особо смысла не вижу генерировать 1000 чисел за 1 раз раз в тысячу кликов или при каждом клике но 100% случайно. Или есть?

Я там выше ответил. Вообще не вижу смысла генерировать что-то) Задаете интервалы, количество значений в каждом интервале (шаг считается автоматически), генерируете случайное число в диапазоне от 1 до «сумма всех чисел во всех интервалах», по этому индексу рассчитываете в какой интервал оно попадает и какое число ему должно соответствовать. Что это будет за число, процент скидки от общей цены, от себестоимости, от процента рентабельности, это уже решать Вам.

Значит я вас неправильно понял, это таки то что я и имел в виду в начале этой ветки :)

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

Изначально я думал что эта задача — что-то типа «корзины скидок». У покупателя есть набор скидок, скидка «выбирается» случайным образом при покупке, и тогда важно выбирать и журналировать выбор, чтобы все скидки были использованы ровно один раз.
Я может чего-то не понимаю, но в Вашей постановке задачи случайность внутри бакетов вообще фиолетова, почему бы тогда просто не генерировать, к примеру, 10 чисел с шагом 2 на диапазоне 1-40, 20 числе с шагом 1 на диапазоне 40-60, 5 чисел с шагом 8 на диапазоне 60-100 и просто выбирать случайным образом 1 число из этого массива?
От того что Вы внутри диапазона генерируете набор с нормальным распределением и от того что Вы периодически «перемешиваете», случайность не увеличивается. Скорее всего, характеристика набора, генерируемого по такому принципу, будут наоборот ухудшаться. Если нужен псевдослучайный генератор с хорошим периодом, используйте вихрь Мерсенна, значения внутри диапазонов можно готовить алгоритмическим способом, я считаю. Кстати, в этом случае набор значений вообще не нужен, так как нужен индекс, генерируемый случайным образом, а само число дальше можно рассчитать по формуле. И да, если хочется чтобы оно было «еще более случайным», можно еще случайным образом генерировать флуктуацию в размере до 5% процентов от самого числа.

Так будут много много раз повторяться что не есть красиво, а генерировать флуктуацию это все-равно генерировать. Решений уже достаточно и остановился пока на своем, от ифов никуда не деться просто по логике задания, в каком порядке и что проверять без разницы. Упростил свой код добавив лишний for вместо ручного перебора с повтором кода, чтобы сберечь глаза неравнодушных, но смысл тот же остался.

Так если генерировать случайные числа, они тоже могут «повторяться», особенно с учетом округлений.
Вот сейчас посмотрел на то что сгенерировалось в базе.
На нтервале от 1 до 10, так как там предполагалось 50 значений, есть повторения после округления до двух значащих знаков.

А если посмотреть еще более внимательно, то повторения есть и на других интервалах и даже после округления то трех значащих знаков после запятой.
В общем, задача Ваша, конечно, ничего не хочу сказать. Но сдается мне, это будет велосипед, построенный или переделанный там где можно дойти пешком в три шага)

Я же написал что тема очень полезная и я ОБЯЗАТЕЛЬНО буду проверять ваш вариант, как самый эффективный на зеркале сайта. Огромное вам всем спасибо, искренне.

Сейчас вот прочитал на трезвую голову и начал понимать.
Тем более, отрицательные диапазоны тогда тогда формируют цену по отношению к себестоимости. При этом, если товар закупается партиями, то соответственно должен реализовываться один из подходов расчета себестоимости для такого вида продукции.
Числа из положительных диапазонов считаются по отношению к рентабельности или наценке.

Это не совсем то, что я имел в виду.

Ну сорри тогда, значит я понял по-своему.

Кстати а обязательно M, N? ~N ~M не подойдет? Если подойдет то «Генерация случайных чисел с заданной функцией распределения, ступенчатая аппроксимация». Выдает попадание в диапазон с заданной вероятностью, ну а дальше генерация rnd внутри диапазона.
lib2.podelise.ru/...ex-4706-27.html

Если у вас все (ratio * 100) являются целыми — сгенерьте массив из 100 значений по нужным вам правилам, перемешайте. подавайте значения охлаждёнными по одному.
Как закончатся — сгенерируйте заново.

Т.е. вот как-то так:
ideone.com/ZnVuDU (вариант на питоне. пхп не знаю)

ваш вариант интересный, но выводит 4 числа меньше 10 вместо положенных двух при повторении 200 раз. ну и есть желание некоторые вероятности делать точнее сотых, впрочем никто не мешает генерить сразу 1000 и подавать по одному, только не уверен что это эффективнее варианта предложенного Васей Петечкиным и Mike Gorchak.

ну, мой вариант не требует счётчиков и их контроля.

Две продажи в минус вместо одной, или два выпадания zero вместо одного в казино это критично, имхо.

там 4 числа меньше 10 потому что такая у вас логика — может выпадать 9 при выпадении диапазона «1» .
вот вам то же самое на PHP: ideone.com/xFMyju

ну не так же работает... 4 числа до 10 из 100 это не 1% от всех цен а целых 4%, это критично

Именно так как у вас и реализовано. Там выборка из 200 чисел а не 100. Это раз. 2 числа попали из диапазона «0» . И еще две девятки из «1», в котором по вашей логике генерируются числа от 9 до 50 и у которого вероятность 0.2

По ссылке с ПХП вариантом там два числа меньше 10 — 5 и 9.
5 — вот ваше «одно из ста» , 9ка — уже из диапазона 9-50

Это капец! ideone рубит в stdout строки разрывая числа пополам и 97 выглядит как 9 и 7, жесть! Все у вас правильно работает и выдает одно число до 9 — это 5, так что 9 это не второй диапазон — все правильно работает.

58 79 44 56 87 20 63 95 78 34 45 62 12 90 67 21 99 75 32 68 78 62 12 54 74 27 74 68 68 64 33 76 94 61 83 18 68 97 50 72 61 97 58 64 86 75 71 84 50 60 13 78 72 80 83 71 36 57 63 59 97 77 27 65 89 69 71 81 65 96 13 11 93 61 65 54 85 86 64 85 71 67 5 68 72 97 30 63 53 10 72 50 53 58 33 68 51 85 75 67

и вывод кода основанного на подходе Mike Gorchak тоже правильный:
24 97 8 95 74 67 88 22 67 70 88 27 56 79 67 54 59 68 50 97 20 69 64 54 58 49 94 52 83 75 69 90 94 21 76 18 69 94 54 58 65 67 28 72 69 38 67 93 81 64 70 87 49 70 53 77 77 62 83 64 15 46 71 96 66 36 87 73 64 58 49 95 93 60 61 76 17 70 59 66 90 34 64 53 80 67 97 26 60 75 99 14 74 54 93 66 38 93 52 63

Тем не менее в том выводе по моей ссылке всё-таки есть и 9 и 5 ... и 9ка действительно из второго диапазона.

33 30 81 65 98 54 73 90 60 97 100 51 60 64 53 63 75 42 59 52 81 82 56 61 64 59 69 91 72 20 81 26 70 30 9 67 67 73 81 52 70 81 78 54 96 56 93 96 11 11 98 83 59 78 40 46 63 87 88 59 41 94 70 64 82 95 80 74 68 29 51 62 68 16 67 89 27 59 24 5 86 81 43 70 60 33 92 73 12 54 57 71 74 51 25 55 72 65 54 64

ну будет ($counts[$gen_d-1][’amount’ ]*$max)+1 нижняя граница да и все

Я понял как у вас все работает и это круто. По идее должно быстрее работать, все-таки просто выдача готовых данных. Но выходит, нужно при добавлении товара,изменении входа или розницы на товар генерировать массив из 1000 цен с правильным распределением (такая точность нужна) и еще хранить последний использованный индекс из этой 1000, чтобы шагать по нему и шафлить в конце круга. Попробую на зеркале сайта.

ну я вам ещё предложил вместо списка использовать дерево отрезков.

так и прийдется, потому что у меня должна вероятность быть общей для всего сайта, а не каждого товара в отдельности. сделать тот же список из 100 (или 1000) элементов, но не из готовых цен для каждого товара, а с разными ратио с правильным распределением и тупо множить на них розницу каждого товара и смещать индекс. как-то так. надо тестировать, опять нужна проверка при генерировании цены в минус, буду пробовать. но не могу понять где там дерево?

Я его предлагал для выбора вашего gen_d :
Пусть каждый элемент из вашего списка counts в качестве ключа имеет диапазон
(0 , randmax()*0.1]
(randmax()*0.1 ,randmax()*0.1 + randmax()*0.29]
и т.д.

Таким образом для выбора gen_d берётся простой rand() и проверяется в какой из диапазонов он попал — тот и берётся.

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

Но такой подход не гарантирует вам того, например, что если вы сгенерируете 100 чисел у вас будет ровно одно меньше 10. Т.е. у вас просто будет взвешенный random

Надо точная выдача. Пока оставил то что было еще до совета Майка, с другими вариантами буду экспериментировать. Очень полезная тема и советы. Спасибо!

решение вам уже придумали, теперь зал должен отрефакторить?

никто не просит ничего рефакторить если вам самому не интересно. Форум — место для обмена опытом, этим и занимаемся.

Боже мой, весь код 1na100 такой?

stackoverflow.com/...y-weight-in-php — выбирайте любое решение.

Ознакомлюсь, спасибо. 1na100 еще хуже, но свое дело уже сделал.

Так не проще?

#define PRICES_COUNT 5 //Кол-во диапазонов
const unsigned int Prices[] = {10,20,30,40,50,100};//Диапазоны
unsigned int PricesRange[] = {8, 7, 10, 15, 3};//Сколько в каком диапазоне генерить
unsigned int N = PRICES_COUNT;

unsigned int Rand(min, max); 

int GetPrice(){
	if(N){
		int i = Rand(1, N);
		for(int j = 0; j < PRICES; COUNT; j++){
			if(PricesRange[j]){
				if(--i == 0){
					PricesRange[j]--;
					N--;
					return(Rand(Prices[j], Prices[j+1] - 1));
				}
			}	
		}
	}
	return -1;
}

void PrintPrices(){
	while((int price = GetPrice()) >= 0){
		printf("%d ", price);
	}
}

кроме нее там еще есть ошибки, но это не важно. где вы получаете min и max,

PRICES; COUNT
вместо PRICES_COUNT, и объявлен он в начале с ошибкой?
ну ладно, что я понял:
1) проверяем указано ли количество диапазонов
2) случайно выбираем диапазон (или PRICES_COUNT это уже общее количество цен необходимых)
3) проверяем не закончились ли появления каждого диапазона
4) if(-PricesRange[j] == 0)N—; допустим проверка на ноль, но зачем минус? имели ввиду «—» ? но тогда проверка не сработает ни в каком из случаев
5) дальше устал и понял, что все равно не то и например никтогда не сгенерирует число 200

Это будет работать только N раз, потому что отсчет обратный, надо генерировать бесконечно долго.

вместо PRICES_COUNT
да
, и объявлен он в начале с ошибкой?
нет. пять диапазонов 10 — 19, 20 — 29, 30 — 39, 40 — 49, 50 — 99
1) проверяем указано ли количество диапазонов
проверяем остались ли еще диапазоны в которых нужно что-то генерить.
2) случайно выбираем диапазон
да
3) проверяем не закончились ли появления каждого диапазона
находим в каком именно из оставшихся диапазонов будем генерить.
if(-PricesRange[j] == 0)N—; допустим проверка на ноль, но зачем минус? имели ввиду «—» ? но тогда
Не «-» а именно «—» шрифт тут такой...
никтогда не сгенерирует число 200
сгенерирует, с определенной вероятностью если добавить этот диапазон.
Это будет работать только N
Это будет работать ровно 8 + 7 + 10 + 15 + 3 раз.

Т.е. если я правильно понял код, вам в одном диапазоне надо вероятность 0.65? Напишите обертку, где будете генерировать случайное число из большего диапазона, а потом домножить на коэффициент чтоб обратно попасть в нужный вам диапазон.

сгенерирую в большем диапазоне от 1 до 80 число 70, потом проверять не нарушена ли частота появления и если оно не подходит не генерировать заново а в тупую в писать в другой диапазон уменьшив его? а откуда я знаю что тот другой диапазон не переполнен и в него можно дописывать?

Я ПыхПыхе не разбираюсь, что у вас написано — я хз. Я сделал предположение относительно того что вам нужно получить. Какой диапазон?
Если я вас понял, вам нужно генерировать цену в диапазоне [a, b], причем в диапазоне [a ,a1], где а1 < b, вероятность появления должна быть выше. Например задан диапазон от 0 до 100, в диапазоне от 0 до 10 вероятность должна быть 0.9, в остальном диапазоне 0.1. Если имеем одинаковое распределение вероятностей в диапазоне от 0 до 100, то число в диапазоне от 0 до 90 будет появляться с вероятностью 0.9, а в диапазоне 90-100 — с вероятностью 0.1, тогда условие, если число полученное меньше 90, то делим его на 9, если больше 90, то делим на 9 и умножаем на 10. Если я ничего не перепутал.
Если же у вас сложная таблица вероятностей, то тогда нужно будет подумать.

Про переполнение диапазона не понял.

да тут неважно на каком языке — все формулировки же простейшие.
у меня сложная таблица вероятностей и задача еще значительно сложнее.
50000 генераций в день, все должны подчиняться определенным правилам. Грубо говоря, давать большую наценку 30% случаев, половину наценки 50%, еще меньше 19%, отдавать в ноль 1%, отдавать в минус тоже 1% при соблюдении некоторых условий кроме учета частоты появления цен. Это упрощенно.
Но смысл моего поста в другом. Можно ли управлять выдачей чисел другим способом, не разделяя на поддиапазоны с последующей проверкой?

Вам случайно не распределение по Гауссу надо?

$gen_p = rand($min,$max);
rand — это встроенный в пхп генератор?
Если да то не понятно что вы делали. Если нет, то где она определена?
//например, до 10 — 65 раз из 100
...
//до 20 — 5 из 100
Его вообще можно заменить чем-то более адекватным?
Если я правильно понял задачу, то надо искать по словам «случайные числа с заданным распределением».

я из результата rand пропускаю на выход только те цены, которые принадлежат тем диапазонам, которые еще не исчерпали свою частоту появления, заданную в настройках. как-то так

Заданное распределение — это немного не то. Вот типичный пример, игральная кость, каждое число может выпасть не более 2 раз за раунд, раунд — 12 бросков. Если за раунд по два раза уже выпали 1,2,3,4,5, то следующие два кидка будут две шестёрки. Затем начинается новый раунд.

А если будет не 1-80, а 1-200000, то во что превратится функция gen_price()?

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

буду рад подсказкам.

Не вникая в алгоритм:
вот вы девушки всегда так...

я использую функции даже в примере, ну добавлю еще одну. я не про читабельность, а про эффективность и скорость работы. есть варианты выполнить задачу проще?

я не про читабельность
нафига постить нечитабельный код на разбор? Никакого стыда и уважения к времени читателей.

Что там нечитабельного? Там и школьник разберется. По делу есть что сказать? Как эффективнее сделать?

Что там нечитабельного?
Копипаст.
По делу есть что сказать? Как эффективнее сделать?
Если задача такая как описал Mike Gorchak ( dou.ua/...ic/8267/#364855 ), то можно обойтись мапой/ассоциативным массивов [диапазон : количество оставшихся попыток], то есть перед началом пересчитать относительные числа (ratio) в абсолютные (в раунде из 100 попыток 65 должны быть в диапазоне 0-10), и на каждой итерации просто уменьшить количество доступных попаданий.
Так же в коде есть проблемы:
— Глобальные переменные.
— Неопределенное поведение в случае, когда не смогли зайти в диапазон (например выпало 9, но все попадания в 0-10 уже исчерпаны). Надо или перезапустить генерацию внутри функции или бросать исключение.

зачем, если функция перезапускается с помощью while пока не даст ответ

Ну очень неоптимально, а не проще ли сразу генерировать рандом по маленькому диапазону а не по всему? %)

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

Mike, вы мужик!
Добавил правильное решение в текст поста.

Так вроде Васёк Петечкин раньше меня запостил подобное, но в виде кода?

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

Я тебе просто обьяснил что функции тормозят, и в некоторых местах вроде криптографии их избегание вполне имеет смысл.

Вложенные if/else тормозят больше, чем функции. В том же gcc можно добиться того, что вызов функии будет практически приравнен к сравнению и переходу. Макросы я могу понять и даже одобрить, а вот явная свалка кода обычно того не стоит.

Ну вот есть же классический пример который мы тут уже 100500 раз обсуждали — сортировка с заинлайненой функцией и с обычной, и видно что вызов функции хорошо просаживает производительность. А там вполне есть и ифы и другие операции.

Если брать классический вызов функций с кучей барахла на входе и выходе — то бесспорно, но всё это можно сильно минимизировать ценой потери отладки, __attribute__((fastcall)) для чисто внутренних функций, которые не есть часть API и т.д. Приёмов много.

Ну вот возвращаясь к упомянутой сортировке, у функции там всего два параметра, и никакие приемы не помогают.

fastcall — это передача через регистры, в принципе в идеальном случае ничем от инлайна отличаться не будет, кроме вызова функции, в худшем несколько медленее, но врядли сильно критично. Есть готовый benchmark на C/C++? Мои замеры показывают, что часто можно пренебречь инлайном.

Мне лень писать бенчмарк самому, но в интернетах обсудили все кому не лень уже: radiospiel.org/...-faster-than-c

Но я так понимаю что в описанном случае как и во многих других fastcall в принципе применить нельзя

Это всё выглядит как журналистский заголовок, qsort() не такая уж трудновоспроизводимая функция, чтобы использовать медленную библиотечную взамен быстрой своей. Что-то мне помнится, что мы даже это обсуждали на ДОУ и пришли к тому мнению, что на C будет так же быстро, но не универсально, код сортировки будет писаться конкретно под нужный тип.

Ну да, тоже самое перцы делают генеря кучи кода, о чем и речь

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

Так от свичей скорее всего не избавишься в любом случае, если это часть логики.

пруф
на то он и пых,что бы не пыжиться

от аниме глаза не вытекают? или очки придерживают?

ждём эффективный руби-код от руби-кот.
предвкушаю решение в одно строчку

Подписаться на комментарии