Не так давно столкнулся с алертом, который работает следующим образом: раз в 10 секунд пробер делает HTTP-запрос до другого сервиса и увеличивает метрику со счетчиком ошибок, в случае провала. Если 6 раз подряд происходят ошибки, алерт активизируется и привлекает внимание человека. В моем конкретном случае за одним DNS именем целевого сервиса скрывается 10 различных IP-адресов, и в какой-то момент 2 из них стали отвечать чуть дольше обычного, приводя к периодическому срабатыванию данного алерта. Вначале я подумал, что вероятность такого срабатывания небольшая, интуитивно оценив ее в . Но алерт срабатывал в среднем раз в сутки, поэтому после нескольких дней я решил починить его. Тем не менее, ради любопытства стало интересно посчитать точную вероятность срабатывания его минимум раз в сутки, ибо она явно отличалась от данной выше оценки.
Сделаем важную оговорку - измерения пробера независимы друг от друга. Пустьвероятность провала измерения, тогдавероятность успеха. Давайте посчитаем обратную вероятность: это будет проще. Обозначим за вероятность того, что цепочка измерений (событий) длиной не содержит подряд идущих ошибочных измерений. Возьмем за очевидную базу:
Теперь рассмотрим ситуацию. Обозначим вероятность последовательности измерений длины не содержащей подряд идущих неудачных измерений заканчивающуюся на успешное измерение и неуспешных за .
Пример для , значения : |
Окончания последовательностей: 0 - успех, 1 - ошибка |
1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
В силу независимости отдельных измерений имеем:
Перебирая же по всем таким , мы можем разбить исходное множество последовательностей, покрываемое вероятностью , на подмножества, не пересекающиеся друг с другом:
Таким образом, имеем рекурсивное определение обратной к интересующей нас вероятности. Давайте имплементируем это на Python:
Отладочный скрипт с brute-force для маленьких n
from itertools import *
q = 0.2
p = 1 - q
k = 6
n = 20
all = list(product([0,1],repeat=n))
positive = 0
negative = 0
for seq in all:
longestK = len(max(''.join(str(x) for x in seq).split('0')))
probability = 1
for item in seq:
if item == 1:
probability *= q
else:
probability *= p
if longestK < k:
positive += probability
else:
negative += probability
print(positive, negative, positive + negative)
q = 0.2 # вероятность ошибочного измеренеия
p = 1 - q # вероятность успешного измерения
k = 6 # количество неуспешных измерений подряд, вызвающее срабатывание алерта
n = 8640 # 60s * 60m * 24h / 10s
# probabilities[n] - не случится для измерений длины n
probabilities = [1] * k # под индексом 0 - игнорируем
probabilities.append(1 - q ** k)
multipliers = [(p * q ** step) for step in range(0, k)]
multipliers.reverse()
for _ in range(k + 1, n + 1):
multiplied = [a * b for a, b in zip(multipliers, probabilities[-k:])]
probabilities.append(sum(multiplied))
print(1 - probabilities[n])
В результате для чисел выше получаем , что выглядит очень и очень много по сравнению с интуитивным предположением, чтобы пренебрегать срабатыванием данного алерта. Для наглядности можно посмотреть на график, который показывает, что при фиксированном и увеличении - количества измерений, вероятность появления цепочки ошибок длиной стремится к .
P.S. Ну и куда без этого: подписывайтесь на мой телеграмм канал, где я галлюцинирую на разные темы, буду рад новым читателям.
aroh
Если вы свои 0,000064 умножите на 8635 (количество серий по 6 замеров в сутках), то получите примерно 0.55. Вполне соответствует "срабатывает примерно 1 раз в сутки"
Остальное это реально какие-то галлюционирования. По крайней мере 1-Q^K это вероятность получить последовательность из K где наступят не только события Q. Т.е. в вашем случае - хотя бы один раз возникнет ошибка. И равно она примерно 0,74.
risentveber Автор
Привел строгий вывод выше в статье, откуда вы взяли свои цифры, я не знаю.