avva: (Default)
[personal profile] avva
(эта запись - не про математику, а про программирование. В конце ее я предлагаю соревнование: приз за лучшую программу)

Недавно я несколько раз писал об игре Алисы и Боба с честной монетой:

Алиса и Боб вместе бросают одну честную монету 100 раз. Каждый раз, когда во время бросков выпадают подряд два орла ОО, Алиса получает очко. Когда выпадает ОР, Боб получает очко. Побеждает тот, у кого больше очков после 100 бросков.

Эта запись - не про математику, а про программирование. Чтобы проверить свои мысли об этой игре, я написал код, который проходит по *всем* возможным играм - но не 100 бросков, это было бы 2^100 игр, слишком много - а скажем 26. Для каждой возможной игры, т.е. вариантов бросков, надо посчитать, сколько очков у каждого и кто победил. В конце мы хотим знать, сколько из, скажем, 2^26 возможных игр - победы Боба, сколько победы Алисы, сколько ничьи.

Мой первый вариант был на Питоне. Потом мне стало любопытно, насколько быстро это можно сделать, и я переписал его на C. В этой быстрой версии игра представлена с помощью числа - его биты это результаты бросков - и я проверяю, есть ли 11 (Алиса) или 10 (Боб) в первых двух битах, потом сдвигаю все число вправо на 1, проверяю опять, и так в цикле 25 раз (если длина игры 26). Эта программа перебрала все 2^26 игр за полторы секунды (Питон за много минут, не помню уже сколько).

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

#define TURNS 40
#define MAXN (1ULL << TURNS)
unsigned long long i, ascore, bscore;
for (i = 0; i < MAXN; ++i) {
  ascore = __builtin_popcount(i&(i>>1));
  bscore = __builtin_popcount(i&((~i & (MAXN-1))>>1));
  awins += (ascore > bscore);
  bwins += (ascore < bscore);
}


Для максимальной эффективности ее надо компилировать так: gcc -O3 -march=native, тогда компилятор использует инструкцию popcnt вместо функции __builtin_popcount.
Наверняка для MSVC тоже можно найти правильные параметры и способ использовать эту инструкцию, я не проверял (я компилировал под 64-битным Windows, но gcc).

Эта версия проходит через все 40-бросковые игры за 25 минут. Это примерно триллион вариантов, довольно круто по-моему.

Предположим, я захотел бы таким образом узнать точное число побед Алисы и Боба на игре из 64 бросков. Используя эту программу на одном компьютере, я ждал бы ответа 38 лет. Но ясно, что эту программу тривиально легко разбить на параллельные вычисления. На моем десктопе 28 ядер на двух процессорах; если 25 из них занять этим вычислением, то машина справится уже за полгода, а тысяча машин - за 12 часов.

Я не буду это делать. Во-первых, я уже не работаю в Гугле и у меня нет тривиально легкого и бесплатного доступа к 1000 машин. Во-вторых, на то мы и программисты, чтобы не все и всегда делать в лоб. Найти точное число побед Алисы и Боба в первоначальной версии игры, со 100 бросками, полным перебором - не сможет, думаю, вся наша цивилизация за год или десять лет. Но по-моему, должно быть не слишком тяжело сделать это умнее.

Я предлагаю конкурс: написать программу, которая должна как можно быстрее вычислить и напечатать правильное число побед Алисы и Боба в игре из 100 бросков. Варианты можно предлагать в течение недели. Автор варианта, который делает это быстрее всех (и правильно), получает приз в $100. Для этого надо не только дать ответ, но и исходный код, и в случае если это нетривиально - пояснения, как его скомпилировать или запустить (под стандартным Windows или Линуксом), а также время, за которое программа выдает ответ у вас на компьютере. В случае близких по времени результатов я буду сравнивать их, запуская на своем десктопе (два процессора Intel Xeon [Bad username or site: 2 @ 40GHz] по 14 ядер в каждом, 32GB памяти, стандартная Windows 10, в случае надобности Линукса установлю на ней WSL2). Можно посылать больше одного варианта. Посылать можно в комментариях или почтой на avorobey в GMail. Вроде ничего не забыл, но если непонятно, можно задавать вопросы.

(разумеется, программа не может просто напечатать вычисленный отдельно автором ответ, а должна "честно" его найти)

Date: 2024-04-30 11:49 am (UTC)
epimorphisms_split: (Default)
From: [personal profile] epimorphisms_split
It could be an exercise in dynamic programming for one of them code competition sites. Since the answer is huge, languages with built-in bignum support have an advantage. Perhaps computing the answer mod 1000000007 or something would be more fair.

Edit: sent you a C++ program that computes the answer at compile time. Had to implement a compile-time long addition for that.
Edited Date: 2024-04-30 03:32 pm (UTC)

February 2026

S M T W T F S
1 2 3 4 5 67
891011121314
15161718192021
22232425262728

Most Popular Tags

Style Credit

Expand Cut Tags

No cut tags
Page generated Feb. 7th, 2026 11:20 am
Powered by Dreamwidth Studios