Если увеличить число итераций до 1000, то можно увидеть как может выглядеть денежный баланс игрока в долгосрочной перспективе:

Понятно, что при шансе выигрыша каждой игры менее 50%, результирующая сумма денег на счету игрока будет постепенно уменьшаться, а сумма прибыли казино постепенно увеличиваться. На графике кстати, видны всплески и падения, и может возникнуть резонный вопрос - можно ли их предсказать? Увы нет, т.к. бросания кубика - это независимые друг от друга события, и предыдущие результаты никак не влияют на следующие. Можно еще раз повторить главную мысль - можно выиграть один или даже несколько раз, но в долгосрочном периоде остаться в плюсе у казино невозможно, правила игры составлены так что баланс будет не в пользу игрока.
В игре крэпс есть и другие виды ставок, желающие могут проанализировать их самостоятельно.
Американская рулетка
Следующий популярный вид азартных игр - рулетка, рассмотрим ее американский вариант.

Игровое поле рулетки делится на 38 ячеек: 36 зон с цифрами + 2 зоны “zero” и “double zero”. Брошенный на рулетку шарик очевидно, остановится в одной из зон. Игрок может делать разнообразные ставки, видов которых более 10, рассмотрим некоторые из них.
Черное-белое (или чет-нечет)
Игрок выигрывает, если названная им ставка совпала. Очевидно, что вероятность черного или белого была бы 50/50, если бы не два поля zero - при попадании на них ставка проигрывает. Как и в случае с крэпсом, это делает вероятность выигрыша лишь чуть менее 50% - но этого “чуть” достаточно, чтобы быть в минусе.
Напишем функцию симуляции хода с помощью случайных чисел от 1 до 38, последние 2 цифры будем считать за “зеро”.
def move_roulette1():
val = random.randint(1, 38)
if val == 37 or val == 38:
return False
return val % 2 != 0
Запустим симуляцию для 100 игр, код тот же самый что и в симуляции крэпса, поменяем только вызов функции.
money_total = 100
win = 0
loose = 0
for p in range(100):
bet = 1
step = move_roulette1()
if step is True:
money_total += bet
win += 1
else:
money_total -= bet
loose += 1
print("Win", win, "Loose", loose, "Money", money_total)
Результат: игрок выиграл 46 раз и проиграл 54 раза. На графике видно, что у игрока были и “взлеты” и “падения”, но итоговый баланс все равно негативный.

Чем больше мы играем, тем глубже мы уходим в минус, а казино соответственно, в плюс:

Ставка на конкретный номер
Игрок также может поставить на определенный номер, ставка при выигрыше составляет 35:1. Это кажется большим, но нетрудно догадаться, что шанс выпадения определенного номера рулетки 1:38, т.е. опять же, чуть меньше.
Допишем функцию ставки на конкретный номер:
def move_roulette2(num):
val = random.randint(1,38)
return val == num
Симуляция, будем считать что игрок ставит на число 10:
money_total = 100
win = 0
loose = 0
for p in range(100):
bet = 1
step = move_roulette2(10)
if step is True:
money_total += 35*bet
win += 1
else:
money_total -= bet
loose += 1
print("Win", win, "Loose", loose, "Money", money_total)
В результате, игрок выиграл 2 раза и проиграл 98 раз, итоговый баланс -28у.е.

Ставка на два номера
Можно поставить на два номера - шанс выигрыша выше, но зато ставка меньше и составляет 17:1.
Напишем функцию:
def move_roulette3(num1, num2):
val = random.randint(1,38)
return val == num1 or val == num2
За 100 попыток нашей симуляции игрок выиграл 3 раза и проиграл 97 раз, баланс составил -46у.е.

Существуют и другие виды ставок, например, на 4 номера с коэффициентом 1:8, желающие могут поэкспериментировать самостоятельно. Как нетрудно догадаться, все коэффициенты рассчитаны так, чтобы игрок оказался в минусе. Кажется заманчивым поставить 1уе на номер, чтобы выиграть целых 35уе. Но сумма выигрыша увеличивается в 35 раз, а шанс выигрыша уменьшается в 38 раз - итоговый баланс все равно будет в пользу казино.
Лото 6 из 45
Следующее, что интересно проверить, это лото. Принцип игры довольно прост - в барабане находятся 45 шаров, выпадают случайным образом 6 из них. Цена билета составляет 100р, а выигрыш зависит от количества угаданных шаров. Примерный порядок сумм выигрыша таков: 2 угаданных шара дают выигрыш в 100р, 3 угаданных шара дают 300р, 4 шара - 3000р, 5 шаров - 300.000р и 6 шаров - суперприз порядка 10.000.000р.
Для начала напишем программу выбрасывания шаров:
def lottery(values):
balls = range(1, 45+1)
b1 = balls.pop(random.randint(0, len(balls)-1))
b2 = balls.pop(random.randint(0, len(balls)-1))
b3 = balls.pop(random.randint(0, len(balls)-1))
b4 = balls.pop(random.randint(0, len(balls)-1))
b5 = balls.pop(random.randint(0, len(balls)-1))
b6 = balls.pop(random.randint(0, len(balls)-1))
s = [b1,b2,b3,b4,b5,b6]
# print s
res = list(set(s) & set(values))
return len(res)
Из массива balls 6 раз “достается” случайный элемент, затем определяется число элементов пересечений двух множеств. Затем построим график суммарного выигрыша от количества купленных билетов. Для простоты будем считать что игрок ставит на одни и те же числа (тем более, что это ни на что не влияет).
money = []
money_total = 0
for p in xrange(N):
val = lottery([3,7,12,18,33,28])
if val == 2:
money_total += 100
if val == 3:
money_total += 300
if val == 4:
money_total += 3000
if val == 5:
money_total += 300000
if val == 6:
money_total += 3000000
money.append(money_total)
x = range(0, N)
price = map(lambda x: ticket_price*x, x)
from matplotlib import pyplot as plt
%matplotlib inline
plt.plot(price, money)
Интересно проанализировать работу программы. Если купить 100 билетов (суммарная потраченная сумма будет 10.000р), то это дает около 14 угаданных “двойных” шаров и один угаданный “тройной”. Суммарный выигрыш составляет около 2.000р при потраченных 10.000р.
График выигрыша от потраченной суммы получился практически линейным:

Получается, что если купить билетов на миллион, выигрыш составит 250тыс. “Суперприз” в симуляции так не разу и не выпал, хотя теоретически, он конечно возможен. Кстати, как написано в правилах, призовой фонд составляет 50% с проданных билетов, но “суперприз” выпадает далеко не всегда, так что как и в случае с казино, организаторы разумеется, в выигрыше.
В завершении этой главы, интересно сравнить принципиально разный психологический подход к играм. В лотерее выигрыши потенциально велики, но весьма редки. В казино подход обратный — ставки настроены так, что человек будет выигрывать максимально часто. Условно, сделав 10 игр в казино, человек выиграет 4 раза и проиграет 6 раз. Это позволяет игроку не терять интерес к игре, но в любом случае общий баланс остается негативным — человек будет много раз выигрывать, но и чуть-чуть больше проигрывать.
В итоге, может ли существовать «выигрышная стратегия» в таких случайных играх? Очевидно нет, т.к. ни кость, ни шарик, ни лотерейные билеты, не имеют памяти, и их поведение не зависит от предыдущих итераций. Кстати, этот момент важно помнить - интуитивно, проиграв несколько раз, человек может решить, что вот сейчас-то он “точно” выиграет. Увы нет - рулетка или кубик не имеют памяти, и “не знают” о количестве предыдущих попыток, каждая игра по сути начинается с чистого листа. Можно ли выиграть в азартные игры? Как показывает, симуляция, в принципе можно, теория вероятности допускает. Но недолго — стоит начать играть 2й, 3й,… Nй раз, как баланс пойдет вниз. В долгосрочной перспективе выиграть у казино невозможно.
28. Теорема Пифагора
Теорему Пифагора, можно надеяться, знает каждый школьник. Она была известна еще до нашей эры, и говорит о том, что для прямоугольного треугольника со сторонами a,b,c выполняется равенство a2 + b2 = c2.
Ничего сложного, в этом разумеется, нет. Интерес тут представляет другое - так называемые “пифагоровы тройки” - наборы из целых чисел, удовлетворяющие теореме Пифагора. Если например, мы возьмем треугольник со сторонами a=1 и b=2, то с будет равна 2.236, что не является целым числом. А вот треугольник со сторонами a=3 и b=4 удовлетворяет данному условию (с=5).
Таких наборов достаточно много, например (3,4,5), (5,12,13), (8,15,17) и пр. Интересно посмотреть на них графически - для чего отметим соответствующие точки (3,4, 5,12 и пр) на плоскости. Для вывода напишем простую программу на языке Python - просто переберем все пары X,Y, и отметим точками те, для которых выполняется целочисленное равенство.
import math
import matplotlib.pyplot as plt
r_max = 20
pt_x, pt_y = [], []
cnt = 0
for x in range(1, r_max):
for y in range(1, r_max):
d = math.sqrt(x*x + y*y)
if d.is_integer():
print x, y, d
pt_x.append(x)
pt_y.append(y)
cnt += 1
print("CNT:", cnt)
plt.plot(pt_x, pt_y, 'ro', markersize=1)
plt.axhline(0, color='black')
plt.axvline(0, color='black')
plt.show()
Полученная картинка для чисел от 1 до 20 показана на рисунке. Ей соответствуют треугольники со сторонами 3,4,5, 6,8,10, 5,12,13, 9,12,15, 8,15,17 и 12,16,20. Некоторые треугольники, например 6,8,10 являются умножением на 2 предыдущей тройки 3,4,5. Те тройки, которые не имеют таких пар, называются примитивными.

Если увеличить количество точек по оси, то мы увидим вполне красивый периодический узор:

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

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

Кстати, для более высоких степеней N > 2 выражение an + bn = cn уже не имеет решений для целых чисел. Это так называемая “Великая теорема Ферма”, которая занимала умы математиков не одну сотню лет. Теорема была полностью доказана лишь в 1995г Эндрю Уайлсом, а доказательство занимает 130 страниц.
29. ABC-гипотеза
Еще один интересный пример из теории чисел - так называемая ABC-гипотеза. Она была сформулирована в 1985г, и до сих пор является одной из нерешенных задач.
Гипотеза формируется довольно-таки просто: для любого числа s>1 существует ограниченное множество троек взаимно простых чисел A+B = C, таких что (rad(A)*rad(B)*rad(C))s < C.
Операция rad(N) называется радикалом, и определяется как произведение всех простых чисел, из которых можно составить число N:
rad(7) = 7 (7 = 1*7)
rad(8) = 2 (8 = 2*2*2)
rad(10) = 10 (10 = 5*2)
rad(17) = 17 (17 = 17*1)
rad(30) = 30 (30 = 2*3*5)
Числа называются взаимно простыми, если они не имеют одинаковых простых множителей, например 3 и 20 взаимно-простые (3=3, 20=2*2*5), а 12 и 15 нет (12=2*2*3, 15=3*5).
Так вот, ABC-гипотеза говорит о том, что если взять некое число S>1, например 1.2, то количество равенств A,B,C, удовлетворяющих описанному условию, будет ограниченным. Пример такой тройки чисел: 1+8=9.
Действительно, rad(1)=1, rad(8)=2, rad(9)=3, и 3 > (1*2)1.2.
Интерес гипотезы, во-первых, в том, что таких равенств мало, для большинства чисел неравенство не выполняется. Например, если взять наугад, равенство 10+6=16, то (rad(10)*rad(6)*rad(16))1.2 = (10*6*2)1.2 = 312.6, > 16.