Биномиальный коэффициент
Сочетанием из элементов по называется набор из элементов, выбранных из заданных элементов. При этом наборы, которые отличаются только порядком следования элементов (но не составом), считаются одинаковыми. Именно благодаря этому свойству сочетаний они отличаются от размещений.
Например, рассмотрим множество из элементов: . Наборы и являются одинаковыми как сочетания (хотя различны как размещения), поскольку составлены из тех же самых элементов .
Формула для вычисления количества сочетаний из по равна
Числа называются биномиальными коэффициентами.
Когда мы выбираем ноль элементов из множества из элементов, мы фактически делаем “пустой” выбор. Пустое множество, в данном контексте, считается допустимым сочетанием. Представьте, что у вас есть коробка с шарами, и вы хотите выбрать из них. Даже если Вы ничего не выбираете, это всё равно является допустимым вариантом выбора. Таким образом, означает количество способов выбрать элементов из , что равно .
Мы можем рассматривать это как один единственный способ не выбирать ни один элемент из множества, что и дает результат .
Когда мы выбираем один элемент из множества из элементов, у нас есть вариантов выбора: мы можем выбрать любой из элементов. Например, если у нас есть множество из чисел от до (), то мы можем выбрать одно число из этого множества, и у нас есть вариантов для этого выбора.
Значение равно , потому что есть способов выбрать один элемент из множества из элементов.
При выборе элементов из — элементного множества, мы формируем комбинации пар. Для построения такой пары мы сначала выбираем один элемент, а затем второй.
Первый элемент можно выбрать из элементов.
После выбора первого элемента, у нас остается элемент для выбора второго элемента (поскольку мы не можем выбрать повторно тот же элемент).
Таким образом, общее количество комбинаций пар элементов, которые можно выбрать из — элементного множества, равно произведению числа способов выбрать первый и второй элементы: .
Однако, каждая пара учтена дважды, так как порядок элементов в паре не имеет значения. Например, и считаются одной и той же парой. Чтобы учесть это, следует разделить общее количество комбинаций на . Таким образом, равно .
Свойство симметрии: .
Количество способов выбрать элементов из равно количеству способов не выбрать элементов из . Но не выбрать элементов из мы можем способами. Следовательно .
Упражнение. Вычислить значения следующих биномиальных коэффициентов:
1) | 3) | 5) |
2) | 4) | 6) |
Формула сложения: .
Доказательство. Вычислим значение правой части равенства:
Для вычисления биномиального коэффициента можно воспользоваться следующим соотношением:
Объявим переменную и инициализируем ее значением . Затем умножим на и разделим результат на . После этого умножим на и разделим на . Процесс умножений и делений будем продолжать раз (числитель и знаменатель после сокращения на содержит множителей).
Функция Cnk вычисляет значение биномиального коэффициента итеративным методом.
int Cnk(int n, int k) { int res = 1; for(int i = 1; i <= k; i++) res = res * (n - i + 1) / i; return res; }
Основная часть программы. Читаем входные данные.
scanf("%d %d",&n,&k);
Вычисляем и выводим ответ.
res = Cnk(n,k); printf("%d\n",res);
Для рекурсивной реализации биномиального коэффициента можно воспользоваться соотношением:
Функция Cnk вычисляет значение биномиального коэффициента рекурсивным методом.
int Cnk(int n, int k) { if (n == k) return 1; if (k == 0) return 1; return Cnk(n - 1, k - 1) + Cnk(n - 1, k); }
Основная часть программы. Читаем входные данные.
scanf("%d %d",&n,&k);
Вычисляем и выводим ответ.
res = Cnk(n,k); printf("%d\n",res);
Сколькими способами среди ЛКШат можно выбрать таких, которым достанется кефир? Ответ выведите по модулю .
Входные данные. Целые числа и .
Выходные данные. Выведите искомое количество способов по модулю .
6 3
20
Ответом к задаче будет значение . Поскольку следует найти биномиальный коэффициент по модулю, постараемся при вычислениях избежать деления. Для этого воспользуемся соотношением .
Поскольку , то используем технику мемоизации.
Объявим константы и массив , для которого .
#define MAX 510 #define MOD 9929 int cnk[MAX][MAX];
Функция c вычисляет значение биномиального коэффициента .
int c(int n, int k) { if (cnk[n][k] > 0) return cnk[n][k]; if (n - k < k) return c(n,n-k); if (!k) return cnk[n][k] = 1; return cnk[n][k] = (c(n-1,k) + c(n-1,k-1)) % MOD; }
Основная часть программы. Инициализируем массив нулями. Читаем входные данные.
memset(cnk,0,sizeof(cnk)); scanf("%d %d",&n,&k);
Вычисляем и выводим ответ.
printf("%d\n",c(n,k));
Биномиальные коэффициенты появляются в биноме Ньютона:
Рассмотрим примеры:
По заданному неотрицательному целому числу найдите сумму биномиальных коэффициентов
Входные данные. Одно неотрицательное целое число .
Выходные данные. Выведите значение суммы.
2
4
Формула бинома Ньютона имеет вид:
Если положить , то данное соотношение принимает следующий вид:
или
Таким образом, указанная сумма равна .
Пример
При :
При :
При :
Читаем входное значение .
scanf("%lld", &n);
Вычисляем и выводим ответ — значение .
res = 1LL << n; printf("%lld\n", res);
По заданному неотрицательному целому числу найдите сумму биномиальных коэффициентов
Входные данные. Одно неотрицательное целое число .
Выходные данные. Выведите значение суммы.
3
20
Рассмотрим объектов и найдем количество способов выбрать объектов из .
Разобьём множество на две части: . Чтобы выбрать объектов из , нам нужно выбрать объектов из левой половины (то есть из множества и объектов из правой (из множества . Количество способов сделать такую выборку в соответствии с правилом умножения равно . Поскольку может принимать значения от до , то равно количеству способов выбрать объектов из , то есть . Таким образом
Пример
Для ответ равен
В то же время .
Функция Cnk вычисляет значение биномиального коэффициента .
long long Cnk(long long n, long long k) { long long res = 1; if (k > n - k) k = n - k; for (long long i = 1; i <= k; i++) res = res * (n - i + 1) / i; return res; }
Основная часть программы. Читаем входное значение .
scanf("%lld", &n);
Вычисляем и выводим ответ.
res = Cnk(2*n, n); printf("%lld\n", res);
По заданным значениям и найдите количество натуральных решений уравнения
Входные данные. Два натуральных числа и .
Выходные данные. Выведите количество натуральных решений для заданного уравнения. Известно, что ответ не превышает .
3 4
3
Рассмотрим последовательность из единиц: . Между любыми двумя единицами можно вставить знак ‘’. Например . Такая запись будет обозначать сумму , где каждое слагаемое – количество единиц, стоящих рядом. Количество позиций, куда можно вставить знак ‘’, равно . Поскольку необходимо получить сумму из слагаемых, то следует вставить знак ‘’.
Нам следует вставить плюс на место. Это можно сделать способами.
Пример
Рассмотрим уравнение . Оно имеет положительных целочисленных решения:
Например, единицы на слагаемых можно разбить способами:
Функция Cnk вычисляет значение биномиального коэффициента .
long long Cnk(long long k, long long n) { long long res = 1; if (k > n - k) k = n - k; for (long long i = 1; i <= k; i++) res = res * (n - i + 1) / i; return res; }
Основная часть программы. Читаем входные данные.
scanf("%lld %lld", &k, &n);
Вычисляем и выводим ответ .
res = Cnk(k - 1, n - 1); printf("%lld\n", res);
По заданным значениям и найдите количество неотрицательных целочисленных решений уравнения
Входные данные. Два натуральных числа и .
Выходные данные. Выведите количество неотрицательных целочисленных решений для заданного уравнения. Известно, что ответ не превышает .
3 4
15
Рассмотрим последовательность из позиций. В позициях следует поставить . В позицию следует поставить символ '' (чтобы получить слагаемых).
Любой расстановке единиц и знаков '' по позициям будет соответствовать некоторое решение заданного уравнения. Например, рассмотрим некоторые решения уравнения единицы и плюса):
Нам следует вставить плюс на позиций. Это можно сделать способами.
Пример
Рассмотрим уравнение . Его решениями будут:
и ее перестановки
и ее перестановок
и ее перестановки
и ее перестановки
Всего имеется решений.
Функция Cnk вычисляет значение биномиального коэффициента .
long long Cnk(long long k, long long n) { long long res = 1; if (k > n - k) k = n - k; for (long long i = 1; i <= k; i++) res = res * (n - i + 1) / i; return res; }
Основная часть программы. Читаем входные данные.
scanf("%lld %lld", &k, &n);
Вычисляем и выводим ответ .
res = Cnk(k - 1, n + k - 1); printf("%lld\n", res);
Пусть — целое неотрицательное число. Обозначим
Для заданных и вычислите значение .
Входные данные. Первая строка содержит количество тестов . Каждая из следующих строк содержит два целых числа и .
Выходные данные. Выведите строк, каждая из которых содержит значение для соответствующего теста.
6 0 0 1 0 1 1 2 0 2 1 2 2
1 1 1 1 2 1
Вычисления будем проводить с использованием -разрядных беззнаковых целых чисел (unsigned long long). Очевидно, что
Присвоим переменной значение , после чего будем ее умножать на для всех от до . Каждый раз деление на будет целочисленным, однако при умножении можно получить переполнение. Пусть . Тогда операцию
перепишем через
При такой реализации переполнения при умножении не произойдет (ответ является -разрядным беззнаковым целым числом). Заметим, что сначала следует выполнить деление , а потом умножение на полученное частное.
Для вычисления следует выполнить итераций. Но что делать, если следует вычислить ? Ответ не превысит , поэтому такие входные данные возможны. Поскольку , то при следует положить .
Пример
Рассмотрим следующий пример:
Пусть , и осталось выполнить умножение . Вычислим . Поэтому .
Функция gcd вычисляет наибольший общий делитель чисел и .
unsigned long long gcd(unsigned long long a, unsigned long long b) { return (!b) ? a : gcd(b,a % b); }
Функция Cnk вычисляет значение биномиального коэффициента .
unsigned long long Cnk(unsigned long long n, unsigned long long k) { unsigned long long CnkRes = 1, t, i; if (k > n - k) k = n - k; for(i = 1; i <= k; i++) { t = gcd(CnkRes, i); CnkRes = (CnkRes / t) * ((n - i + 1) / (i / t)); } return CnkRes; }
Основная часть программы. Читаем входные данные. Последовательно обрабатываем тесты.
scanf("%d",&t); while(t--) { scanf("%llu %llu",&n,&k); res = Cnk(n,k); printf("%llu\n",res); }
Дано натуральное число . Найдите значение следующей суммы
Входные данные. Одно натуральное число .
Выходные данные. Выведите значение искомой суммы.
3
20
Перепишем заданную сумму в виде:
Сумма в скобках равна . Если в формуле бинома Ньютона
положить , то получим соотношение: , или
Вычислим оставшуюся сумму:
Таким образом, ответ равен .
Пример
Вычислим ответ для . При непосредственном вычислении:
При вычислении по формуле: .
Читаем входное значение .
scanf("%lld", &n);
Вычисляем и выводим ответ.
res = (1LL << (n - 1)) * (n + 2); printf("%lld\n", res);
Реализация алгоритма – функция
Объявим массив для запоминания результатов: .
int dp[31][31];
Функия Cnk вычисляет значение биномиального коэффициента .
int Cnk(int n, int k) { if (n == k) return 1; if (k == 0) return 1; if (dp[n][k] != -1) return dp[n][k]; return dp[n][k] = (Cnk(n - 1, k - 1) + Cnk(n - 1, k)) % 9929; }
Основная часть программы. Читаем входное значение .
scanf("%d", &n); memset(dp, -1, sizeof(dp));
Вычисляем указанную сумму.
res = 0; for (i = 0; i <= n; i++) res += (i + 1) * Cnk(n, i);
Выводим ответ.
printf("%d\n", res);
У Вас есть лист бумаги, и Вы выбираете на нем прямоугольник размером . Назовем этот прямоугольник вместе с линиями, на которых он находится, сеткой. Начиная с левого нижнего угла сетки, Вы перемещаете карандаш в правый верхний угол, следя за тем, чтобы он оставался на линиях и двигался только вправо или вверх. Результат показан слева:
Действительно шедевр, не так ли? Повторяя процедуру еще раз, Вы получите картинку, показанную справа. Теперь возникает вопрос: сколько разных произведений искусства можно таким образом создать?
Входные данные. Два натуральных числа и .
Выходные данные. Выведите количество различных произведений искусства, которые можно создать, используя описанную выше процедуру. Можно утверждать, что это число соответствует — битовому знаковому целому числу.
3 4
35
1 1
2
Искомый путь представляет собой ломанную, состоящую из звеньев. Из них звеньев должны быть вертикальными, а остальные — горизонтальными. Количество вариантов выбрать вертикальных звеньев из равно .
Пример
Для первого примера . Ответ равен .
Для второго примера . Ответ равен .
Функция Cnk вычисляет значение биномиального коэффициента .
long long Cnk(long long n, long long k) { long long res = 1; if (k > n - k) k = n - k; for (long long i = 1; i <= k; i++) res = res * (n - i + 1) / i; return res; }
Основная часть программы. Читаем входные данные.
scanf("%lld %lld", &n, &m);
Вычисляем и выводим ответ .
res = Cnk(n + m, n); printf("%lld\n", res);
Гуннар — достаточно старый и забывчивый исследователь. Сейчас он пишет статью о безопасности в социальных сетях, которая включает в себя некоторые элементы комбинаторики. Он написал программу для вычисления биномиальных коэффициентов, чтобы проверить некоторые расчеты.
Биномиальный коэффициент — это число
где и — неотрицательные числа.
Гуннар использует свою программу для вычисления и получил число в качестве результата. К несчастью, поскольку он забывчивый, он забыл числа и , которые он использовал в качестве входных данных. Эти два числа были результатом долгих вычислений, и они были записаны на одном из многочисленных листков, лежащих на его столе. Вместо того чтобы поискать в бумагах, он решил восстановить числа и по полученному ответу. Можете ли Вы помочь ему найти все такие возможные значения?
Входные данные. Первая строка содержит количество тестов, не большее . Каждый тест задается в одной строке и содержит целое число — результат программы Гуннара.
Выходные данные. Для каждого теста выведите две строки. Первая строка должна содержать количество способов выразить при помощи биномиального коэффициента. Во второй строке должны располагаться все пары , удовлетворяющие . Пары следует расположить по возрастанию , а в случае равенства по возрастанию . Формат вывода пар приведен в примере.
2 2 15
1 (2,1) 4 (6,2) (6,4) (15,1) (15,14)
Если , то . Достаточно найти решение и вместе с парой вывести также пару . При эти две пары совпадают.
Пусть — наименьшее число, для которого . Тогда очевидно что .
Зафиксируем такое что и рассмотрим функцию . Тогда при функция является монотонно возрастающей. Следовательно можно решить уравнение бинарным поиском.
Таким образом для решения задачи следует перебрать значения и для каждого такого решить уравнение относительно бинарным поиском. Найденное значение должно быть целочисленным.
Пример
Рассмотрим уравнение . Учитывая что , а , то нам достаточно перебрать .
Пусть , рассмотрим уравнение или , . Бинарным поиском в промежутке находим целочисленное решение . Учитывая что , имеем два решения: .
Искомые пары будем хранить в векторе пар .
vector<pair<long long,long long> > res;
Функция Cnk вычисляет значение биномиального коэффициента .
long long Cnk(long long n, long long k) { long long i, Res = 1; if (k > n - k) k = n - k; for(i = 1; i <= k; i++) {
Если на очередной итерации результат превосходит (мы ищем решение уравнения ), то останавливаемся. Таким выходом из функции мы также избежим переполнения.
if (1.0 * Res * (n - i + 1) / i > m) return m + 1; Res = Res * (n - i + 1) / i; } return Res; }
Основная часть программы. Читаем входные данные.
scanf("%d",&tests); while (tests--) { res.clear(); scanf("%lld",&m);
Перебираем значение от до тех пор пока .
for(k = 0; Cnk(2*k,k) <= m; k++) {
Ищем значение бинарным поиском.
long long lo = 2*k, hi = m; while (lo < hi) { long long n = (lo + hi) / 2; if (Cnk(n,k) < m) lo = n + 1; else hi = n; }
Если , то решение найдено. Заносим в результат одну или две пары решений.
if (Cnk(lo,k) == m) { res.push_back(make_pair(lo,k)); if (lo != 2*k) res.push_back(make_pair(lo,lo - k)); } }
Сортируем пары.
sort(res.begin(),res.end());
Выводим ответ — количество найденных пар и сами пары.
printf("%d\n",res.size()); for(i = 0; i < res.size(); i++) printf("(%lld,%lld) ", res[i].first,res[i].second); printf("\n"); }
Асимптотические оценки для Биномиального коэффициента
Теорема 1. Между биномиальными коэффициентами имеет место соотношение:
Теорема 2. . Следует из того, что
Теорема 3. . Следует из того, что , а самих коэффициентов . Поэтому больший из коэффициентов будет больше .
Теорема 4. Формула Стирлинга. .
Более точное приближение имеет вид:
Теорема 5.
Теорема 6.