给定三个数字 n、r 和 p,计算n C r mod p 的值。
例子:
Input: n = 10, r = 2, p = 13
Output: 6
Explanation: 10C2 is 45 and 45 % 13 is 6.
我们强烈建议您在继续解决方案之前单击此处进行练习。
方法一:(使用动态规划)
一个简单的解决方案是首先计算n C r ,然后计算n C r % p。当n C r的值很小时,此解决方案工作正常。
如果n C r 的值很大怎么办?
当n C r不能放入变量并导致溢出时,对于较大的 n 值,通常需要n C r %p 的值。因此,计算n C r然后使用模运算符不是一个好主意,因为即使 n 和 r 的值稍大,也会出现溢出。例如,此处和此处讨论的方法会导致 n = 50 和 r = 40 溢出。
这个想法是使用以下公式计算n C r
C(n, r) = C(n-1, r-1) + C(n-1, r)
C(n, 0) = C(n, n) = 1
上述公式和帕斯卡三角形的工作:
让我们看看上面的公式如何适用于 C(4, 3)
1==========>> n = 0, C(0, 0) = 1
1–1========>> n = 1, C(1, 0) = 1, C(1, 1) = 1
1–2–1======>> n = 2, C(2, 0) = 1, C(2, 1) = 2, C(2, 2) = 1
1–3–3–1====>> n = 3, C(3, 0) = 1, C(3, 1) = 3, C(3, 2) = 3, C(3, 3) =1
1–4–6–4–1==>> n = 4, C(4, 0) = 1, C(4, 1) = 4, C(4, 2) = 6, C(4, 3) =4, C(4, 4)=1
所以这里我的每个循环,使用第 (i-1) 行构建第 i 行的帕斯卡三角形
对上述模运算公式的扩展:
我们可以使用模运算符的分布特性使用上述公式找到 nCr % p。
C(n, r)%p = [ C(n-1, r-1)%p + C(n-1, r)%p ] % p
C(n, 0) = C(n, n) = 1
上面的公式可以通过使用二维数组的动态规划来实现。
通过一次构造一行,可以进一步优化基于二维数组的动态规划解决方案。有关详细信息,请参阅以下帖子中的空间优化版本。
使用动态规划的二项式系数
下面是基于上述帖子中讨论的空间优化版本的实现。
C++
// A Dynamic Programming based solution to compute nCr % p
#include
using namespace std;
// Returns nCr % p
int nCrModp(int n, int r, int p)
{
// Optimization for the cases when r is large
if (r > n - r)
r = n - r;
// The array C is going to store last row of
// pascal triangle at the end. And last entry
// of last row is nCr
int C[r + 1];
memset(C, 0, sizeof(C));
C[0] = 1; // Top row of Pascal Triangle
// One by constructs remaining rows of Pascal
// Triangle from top to bottom
for (int i = 1; i <= n; i++) {
// Fill entries of current row using previous
// row values
for (int j = min(i, r); j > 0; j--)
// nCj = (n-1)Cj + (n-1)C(j-1);
C[j] = (C[j] + C[j - 1]) % p;
}
return C[r];
}
// Driver program
int main()
{
int n = 10, r = 2, p = 13;
cout << "Value of nCr % p is " << nCrModp(n, r, p);
return 0;
}
JAVA
// A Dynamic Programming based
// solution to compute nCr % p
import java.io.*;
import java.util.*;
import java.math.*;
class GFG {
// Returns nCr % p
static int nCrModp(int n, int r, int p)
{
if (r > n - r)
r = n - r;
// The array C is going to store last
// row of pascal triangle at the end.
// And last entry of last row is nCr
int C[] = new int[r + 1];
C[0] = 1; // Top row of Pascal Triangle
// One by constructs remaining rows of Pascal
// Triangle from top to bottom
for (int i = 1; i <= n; i++) {
// Fill entries of current row using previous
// row values
for (int j = Math.min(i, r); j > 0; j--)
// nCj = (n-1)Cj + (n-1)C(j-1);
C[j] = (C[j] + C[j - 1]) % p;
}
return C[r];
}
// Driver program
public static void main(String args[])
{
int n = 10, r = 2, p = 13;
System.out.println("Value of nCr % p is "
+ nCrModp(n, r, p));
}
}
// This code is contributed by Nikita Tiwari.
Python3
# A Dynamic Programming based solution to compute nCr % p
# Returns nCr % p
def nCrModp(n, r, p):
# Optimization for the cases when r is large
# compared to n-r
if (r > n- r):
r = n - r
# The array C is going to store last row of
# pascal triangle at the end. And last entry
# of last row is nCr.
C = [0 for i in range(r + 1)]
C[0] = 1 # Top row of Pascal Triangle
# One by constructs remaining rows of Pascal
# Triangle from top to bottom
for i in range(1, n + 1):
# Fill entries of current row
# using previous row values
for j in range(min(i, r), 0, -1):
# nCj = (n - 1)Cj + (n - 1)C(j - 1)
C[j] = (C[j] + C[j-1]) % p
return C[r]
# Driver Program
n = 10
r = 2
p = 13
print('Value of nCr % p is', nCrModp(n, r, p))
# This code is contributed by Soumen Ghosh
C#
// A Dynamic Programming based
// solution to compute nCr % p
using System;
class GFG {
// Returns nCr % p
static int nCrModp(int n, int r, int p)
{
// Optimization for the cases when r is large
if (r > n - r)
r = n - r;
// The array C is going to store last
// row of pascal triangle at the end.
// And last entry of last row is nCr
int[] C = new int[r + 1];
for (int i = 0; i < r + 1; i++)
C[i] = 0;
C[0] = 1; // Top row of Pascal Triangle
// One by constructs remaining rows
// of Pascal Triangle from top to bottom
for (int i = 1; i <= n; i++) {
// Fill entries of current row using
// previous row values
for (int j = Math.Min(i, r); j > 0; j--)
// nCj = (n-1)Cj + (n-1)C(j-1);
C[j] = (C[j] + C[j - 1]) % p;
}
return C[r];
}
// Driver program
public static void Main()
{
int n = 10, r = 2, p = 13;
Console.Write("Value of nCr % p is "
+ nCrModp(n, r, p));
}
}
// This code is contributed by nitin mittal.
PHP
$n - $r)
$r = $n - $r;
// The array C is going
// to store last row of
// pascal triangle at
// the end. And last entry
// of last row is nCr
$C = array();
for( $i = 0; $i < $r + 1; $i++)
$C[$i] = 0;
// Top row of Pascal
// Triangle
$C[0] = 1;
// One by constructs remaining
// rows of Pascal Triangle from
// top to bottom
for ($i = 1; $i <= $n; $i++)
{
// Fill entries of current
// row using previous row values
for ($j = Min($i, $r); $j > 0; $j--)
// nCj = (n-1)Cj + (n-1)C(j-1);
$C[$j] = ($C[$j] +
$C[$j - 1]) % $p;
}
return $C[$r];
}
// Driver Code
$n = 10; $r = 2;$p = 13;
echo "Value of nCr % p is ",
nCrModp($n, $r, $p);
// This code is contributed
// by anuj_67.
?>
C++
// C++ program to find the nCr%p
// based on optimal Dynamic
// Programming implementation and
// Pascal Triangle concepts
#include
using namespace std;
// Returns (a * b) % mod
long long moduloMultiplication(long long a, long long b,
long long mod)
{
// Initialize result
long long res = 0;
// Update a if it is more than
// or equal to mod
a %= mod;
while (b) {
// If b is odd, add a with result
if (b & 1)
res = (res + a) % mod;
// Here we assume that doing 2*a
// doesn't cause overflow
a = (2 * a) % mod;
b >>= 1; // b = b / 2
}
return res;
}
// C++ function for extended Euclidean Algorithm
long long int gcdExtended(long long int a, long long int b,
long long int* x,
long long int* y);
// Function to find modulo inverse of b. It returns
// -1 when inverse doesn't exists
long long int modInverse(long long int b, long long int m)
{
long long int x, y; // used in extended GCD algorithm
long long int g = gcdExtended(b, m, &x, &y);
// Return -1 if b and m are not co-prime
if (g != 1)
return -1;
// m is added to handle negative x
return (x % m + m) % m;
}
// C function for extended Euclidean Algorithm (used to
// find modular inverse.
long long int gcdExtended(long long int a, long long int b,
long long int* x,
long long int* y)
{
// Base Case
if (a == 0) {
*x = 0, *y = 1;
return b;
}
// To store results of recursive call
long long int x1, y1;
long long int gcd = gcdExtended(b % a, a, &x1, &y1);
// Update x and y using results of recursive
// call
*x = y1 - (b / a) * x1;
*y = x1;
return gcd;
}
// Function to compute a/b under modlo m
long long int modDivide(long long int a, long long int b,
long long int m)
{
a = a % m;
long long int inv = modInverse(b, m);
if (inv == -1)
// cout << "Division not defined";
return 0;
else
return (inv * a) % m;
}
// Function to calculate nCr % p
int nCr(int n, int r, int p)
{
// Edge Case which is not possible
if (r > n)
return 0;
// Optimization for the cases when r is large
if (r > n - r)
r = n - r;
// x stores the current result at
long long int x = 1;
// each iteration
// Initialized to 1 as nC0 is always 1.
for (int i = 1; i <= r; i++) {
// Formula derived for calculating result is
// C(n,r-1)*(n-r+1)/r
// Function calculates x*(n-i+1) % p.
x = moduloMultiplication(x, (n + 1 - i), p);
// Function calculates x/i % p.
x = modDivide(x, i, p);
}
return x;
}
// Driver Code
int main()
{
long long int n = 5, r = 3, p = 1000000007;
cout << "Value of nCr % p is " << nCr(n, r, p);
return 0;
}
Value of nCr % p is 6
方法 2(使用 Pascal Triangle 和 Dynamic Pro)
另一种方法在于利用帕斯卡三角形的概念。而不是计算n C r 从 n=0 到 n=n 的每个 n 的值,该方法旨在使用第 n 行本身进行计算。该方法通过找出n C r和n C r-1之间的一般关系来进行。
FORMULA: C(n,r)=C(n,r-1)* (n-r+1)/r
Example:
For instance, take n=5 and r=3.
Input: n = 5, r = 3, p = 1000000007
Output: 6
Explanation: 5C3 is 10 and 10 % 100000007 is 10.
As per the formula,
C(5,3)=5!/(3!)*(2!)
C(5,3)=10
Also,
C(5,2)=5!/(2!)*(3!)
C(5,2)=10
Let's try applying the above formula.
C(n,r)=C(n,r-1)* (n-r+1)/r
C(5,3)=C(5,2)*(5-3+1)/3
C(5,3)=C(5,2)*1
C(5,3)=10*1
上面的例子表明,通过计算 C(n,r-1) 并将结果乘以 (n-r+1)/r 项,可以很容易地计算出 C(n,r)。但是对于较大的 n 值,这种乘法可能会导致整数溢出。要解决这种情况,请使用模乘法和模除法概念以实现整数溢出方面的优化。
让我们找出如何为相同的东西构建 Pascal Triangle。
只需声明一个变量来执行计算,就可以进一步优化一维数组声明。但是,整数溢出还需要其他函数才能最终实现。
下面的帖子提到了二进制系数计算的空间和时间优化实现。
C++
// C++ program to find the nCr%p
// based on optimal Dynamic
// Programming implementation and
// Pascal Triangle concepts
#include
using namespace std;
// Returns (a * b) % mod
long long moduloMultiplication(long long a, long long b,
long long mod)
{
// Initialize result
long long res = 0;
// Update a if it is more than
// or equal to mod
a %= mod;
while (b) {
// If b is odd, add a with result
if (b & 1)
res = (res + a) % mod;
// Here we assume that doing 2*a
// doesn't cause overflow
a = (2 * a) % mod;
b >>= 1; // b = b / 2
}
return res;
}
// C++ function for extended Euclidean Algorithm
long long int gcdExtended(long long int a, long long int b,
long long int* x,
long long int* y);
// Function to find modulo inverse of b. It returns
// -1 when inverse doesn't exists
long long int modInverse(long long int b, long long int m)
{
long long int x, y; // used in extended GCD algorithm
long long int g = gcdExtended(b, m, &x, &y);
// Return -1 if b and m are not co-prime
if (g != 1)
return -1;
// m is added to handle negative x
return (x % m + m) % m;
}
// C function for extended Euclidean Algorithm (used to
// find modular inverse.
long long int gcdExtended(long long int a, long long int b,
long long int* x,
long long int* y)
{
// Base Case
if (a == 0) {
*x = 0, *y = 1;
return b;
}
// To store results of recursive call
long long int x1, y1;
long long int gcd = gcdExtended(b % a, a, &x1, &y1);
// Update x and y using results of recursive
// call
*x = y1 - (b / a) * x1;
*y = x1;
return gcd;
}
// Function to compute a/b under modlo m
long long int modDivide(long long int a, long long int b,
long long int m)
{
a = a % m;
long long int inv = modInverse(b, m);
if (inv == -1)
// cout << "Division not defined";
return 0;
else
return (inv * a) % m;
}
// Function to calculate nCr % p
int nCr(int n, int r, int p)
{
// Edge Case which is not possible
if (r > n)
return 0;
// Optimization for the cases when r is large
if (r > n - r)
r = n - r;
// x stores the current result at
long long int x = 1;
// each iteration
// Initialized to 1 as nC0 is always 1.
for (int i = 1; i <= r; i++) {
// Formula derived for calculating result is
// C(n,r-1)*(n-r+1)/r
// Function calculates x*(n-i+1) % p.
x = moduloMultiplication(x, (n + 1 - i), p);
// Function calculates x/i % p.
x = modDivide(x, i, p);
}
return x;
}
// Driver Code
int main()
{
long long int n = 5, r = 3, p = 1000000007;
cout << "Value of nCr % p is " << nCr(n, r, p);
return 0;
}
Value of nCr % p is 10
复杂度分析:
- 上面的代码需要额外的 O(1) 空间来进行计算。
- 计算 nCr % p 所涉及的时间为 O(n) 级。
如果您希望与专家一起参加现场课程,请参阅DSA 现场工作专业课程和学生竞争性编程现场课程。