给定三个数字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.
我们强烈建议您单击此处并进行实践,然后再继续解决方案。
方法1 :(使用动态编程)
一个简单的解决方案是先计算n C r ,然后计算n C r %p。当n C r的值较小时,此解决方案效果很好。
如果n C r的值很大,该怎么办?
当n C r无法容纳在变量中并导致溢出时,通常需要n C r %p的值来获得较大的n值。因此,计算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循环都使用第(i-1)行建立第i行pascal三角形
扩展以上公式进行模数运算:
我们可以使用模运算符的分布特性,通过上述公式找到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
可以使用使用2D数组的动态编程来实现上述公式。
通过一次构造一行,可以进一步优化基于2D数组的动态编程解决方案。有关详细信息,请参见下面帖子中的空间优化版本。
使用动态规划的二项式系数
以下是基于以上文章中讨论的空间优化版本的实现。
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)
另一种方法是利用Pascal Triangle的概念。而不是计算n C r 从n = 0到n = 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)。