Вначале определим вспомогательную функцию для получения случайного целого числа в заданном диапазоне. Такую функцию легко написать на чистом 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 -------+---------+----------- 0 | 343 | -9 0 | 229 | -13 1 | 301 | -18 0 | 273 | 15 0 | 1 | -28 1 | 315 | 15 1 | 337 | -10 1 | 318 | 27 0 | 90 | -14 0 | 70 | 30 (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 | 19993 2 | 20102 3 | 20086 4 | 19856 5 | 19963 (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 ------------------------------- TDcЬrу5MУвДчjЛИzzmЩEp Ъ33SWХ4ZsNЮKrКАmYФЙcMupWj ю8гйnюдhТ63НVmb0сIБтпZH чЦкгtПВнОёY2m8a 5TЗRъkHtоJвfеqЬeВHГwYЁЯoл3hФ2 ОzСЧFM8ОымYeО9K eM_hэOneшХX йQYщErR шQЫ А_WП_выTvсцоВЯОъхnXgшрЫ9ЭuЛX (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: Выиграл начальный выбор : 330 из 1000 NOTICE: Выиграл измененный выбор: 670 из 1000 DO
В начале мы выбираем 1 из 3, поэтому вероятность начального выбора 1/3. Если же выбор изменить, то изменится и вероятность на противоположные 2/3.
Таким образом, вероятность выиграть при смене выбора выше. Поэтому есть смысл выбор поменять.