Обчислення будемо проводити з використанням -розрядних беззнакових цілих чисел (unsigned long long). Очевидно, що
Присвоїмо змінній значення , після чого будемо її множити на {n - i + 1 \over i} для всіх від до . Кожного разу ділення на буде цілочисельним, однак при множенні можна отримати переповнення. Нехай d = НОД(res, i). Тоді операцію
перепишемо через
При такій реалізації переповнення при множенні не відбудеться (відповідь є -розрядним беззнаковим цілим числом). Зауважимо, що спочатку слід виконати ділення (n - i + 1 ) / (i / d), а потім множення на отримане частка.
Для обчислення C_n^k слід виконати ітерацій. Але що робити, якщо слід обчислити C_{2000000000}^{1999999999}? Відповідь не перевищить 2^{64}, тому такі вхідні дані можливі. Оскільки C_n^k = C_n^{n - k}, то при n - k < k слід покласти k = n - k.
Приклад
Розглянемо наступний приклад:
Нехай res = 15, і залишилося виконати множення res \cdot {4 \over 3} = 15 * {4 \over 3}. Обчислимо d = НОД(15, 3) = 3. Тому 15 \cdot {4 \over 3} = (15 / 3) \cdot {4 \over 3 / 3} = 5 \cdot {4 \over 1} = 20.
Реалізація алгоритму
Функція gcd обчислює найбільший спільний дільник чисел і .
unsigned long long gcd(unsigned long long a, unsigned long long b)
{
return (!b) ? a : gcd(b,a % b);
}Функція Cnk обчислює значення біноміального коефіцієнта C_n^k.
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);
}Java реалізація
У Java відсутні unsigned типи, тому слід скористатися класом BigInteger.
import java.util.*;
import java.math.*;
public class Main
{
static BigInteger Cnk(BigInteger n, BigInteger k)
{
BigInteger ONE = BigInteger.ONE, CnkRes = ONE;
if (k.compareTo(n.subtract(k)) > 0) k = n.subtract(k);
for(BigInteger i = ONE; i.compareTo(k) <= 0; i = i.add(ONE))
CnkRes = CnkRes.multiply(n.subtract(i).add(ONE)).divide(i);
return CnkRes;
}
public static void main(String[] args)
{
Scanner con = new Scanner(System.in);
int tests = con.nextInt();
while(tests-- > 0)
{
BigInteger n = con.nextBigInteger();
BigInteger k = con.nextBigInteger();
BigInteger res = Cnk(n,k);
System.out.println(res);
}
}
}Python реалізація – comb
Для обчислення біноміального коефіцієнта скористаємося вбудованою функцією comb(n, k) = C_n^k.
import mathЧитаємо кількість тестів .
t = int(input())
for _ in range(t):Читаємо вхідні дані поточного тесту.
n, k = map(int, input().split())Обчислюємо і виводимо відповідь.
res = math.comb(n, k)
print(res)Python реалізація
Функція Cnk обчислює значення біноміального коефіцієнта C_n^k.
def Cnk(n, k):
res = 1
if k > n - k: k = n – k
for i in range(1, k + 1):
res = res * (n - i + 1) // i
return resОсновна частина програми. Читаємо кількість тестів .
t = int(input())
for _ in range(t):Читаємо вхідні дані поточного тесту.
n, k = map(int, input().split())Обчислюємо і виводимо відповідь.
res = Cnk(n, k)
print(res)