Вначале определим вспомогательную функцию для получения случайного целого числа в заданном диапазоне. Такую функцию легко написать на чистом SQL, но здесь представлен вариант на PL/pgSQL:
=> CREATE FUNCTION rnd_integer(min_value integer, max_value integer) RETURNS integer AS $$ DECLARE retval integer; BEGIN IF max_value <= min_value THEN RETURN NULL; END IF; retval := floor((max_value+1 - min_value)*random())::integer + min_value; RETURN retval; END; $$ STRICT LANGUAGE plpgsql;
CREATE FUNCTION
Проверяем работу:
=> SELECT rnd_integer(0,1) as "0 - 1", rnd_integer(1,365) as "1 - 365", rnd_integer(-30,30) as "-30 - +30" FROM generate_series(1,10);
0 - 1 | 1 - 365 | -30 - +30 -------+---------+----------- 1 | 284 | 25 0 | 330 | 26 1 | 112 | -21 0 | 179 | -21 1 | 168 | -25 1 | 140 | 9 0 | 307 | 17 0 | 48 | -14 1 | 89 | 2 0 | 69 | -14 (10 rows)
Функция гарантирует равномерное распределение случайных значений по всему диапазону, включая граничные значения:
=> SELECT rnd_value, count(*) FROM ( SELECT rnd_integer(1,5) as rnd_value FROM generate_series(1,100000) ) as t GROUP by rnd_value ORDER BY rnd_value;
rnd_value | count -----------+------- 1 | 19942 2 | 20000 3 | 19983 4 | 19933 5 | 20142 (5 rows)
Теперь можно приступить к функции для получения случайной строки заданного размера. Будем использовать функцию rnd_integer для получения случайного символа из списка.
=> CREATE FUNCTION rnd_text( len int, list_of_chars text DEFAULT 'АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдеёжзийклмнопрстуфхцчшщъыьэюяABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_0123456789' ) RETURNS text AS $$ DECLARE len_of_list CONSTANT integer = length(list_of_chars); i integer; retval text = ''; BEGIN FOR i IN 1 .. len LOOP -- добавляем к строке случайный символ retval := retval || substr(list_of_chars, rnd_integer(1,len_of_list),1); END LOOP; RETURN retval; END; $$ STRICT LANGUAGE plpgsql;
CREATE FUNCTION
Проверяем:
=> SELECT rnd_text(rnd_integer(1,30)) FROM generate_series(1,10);
rnd_text -------------------------------- yыQdфКЩсTCдjИmТUВС6шh0NS4XEЁлц а_ 9йhЩdхcДЫmqныtAшзгп12 Pэп80хЮqНЮЦ ч0жЦ4D8lyщTу_8Yё0QQFПГDЖщix ГЙшъАhR4ёPgЪЛфPВуи й65wЛ9хУшЪ ГЭ ьYРЯл G8s (10 rows)
Для загадывания и угадывания наперстка используем rnd_integer(1,3)
=> DO $$ DECLARE x integer; choice integer; new_choice integer; remove integer; total_games integer := 1000; old_choice_win_counter integer = 0; new_choice_win_counter integer = 0; BEGIN FOR i IN 1 .. total_games LOOP -- Загадываем выигрышный наперсток x := rnd_integer(1,3); -- Игрок делает выбор choice := rnd_integer(1,3); -- Убираем один неверный ответ, кроме выбора игрока FOR i IN 1 .. 3 LOOP IF i NOT IN (x, choice) THEN remove := i; EXIT; END IF; END LOOP; -- Нужно ли игроку менять свой выбор? -- Что лучше оставить choice или заменить его на оставшийся? -- Измененный выбор FOR i IN 1 .. 3 LOOP IF i NOT IN (remove, choice) THEN new_choice := i; EXIT; END IF; END LOOP; -- Или начальный, или новый выбор обязательно выиграют IF choice = x THEN old_choice_win_counter := old_choice_win_counter + 1; ELSIF new_choice = x THEN new_choice_win_counter := new_choice_win_counter + 1; END IF; END LOOP; RAISE NOTICE 'Выиграл начальный выбор : % из %', old_choice_win_counter, total_games; RAISE NOTICE 'Выиграл измененный выбор: % из %', new_choice_win_counter, total_games; END; $$;
NOTICE: Выиграл начальный выбор : 332 из 1000 NOTICE: Выиграл измененный выбор: 668 из 1000 DO
В начале мы выбираем 1 из 3, поэтому вероятность начального выбора 1/3. Если же выбор изменить, то изменится и вероятность на противоположные 2/3.
Таким образом, вероятность выиграть при смене выбора выше. Поэтому есть смысл выбор поменять.