这是CodeSprint3的问题 https://cs3.interviewstreet.com/challenges/dashboard/#problem/50877a587c389 基本上问题是计算给定n和r的可能组合的数量nCr。此外,1 <= n <= 1000000000且0 <= r <= n。 以模数142857输出所有答案。
Since 6C4=6!/4! 2!
=6*5/2!
=6*5/2*1
我认为在每一步使用分割都可以避免溢出。也就是说 以n的值开始(在这种情况下n为6)。 递减n并将其与之前的值相乘(因此这将变为6 * 5) 用分母进行除法然后递减(6 * 5/2,分母2变为1) 重复这些步骤,直到n小于最大2个分母并且在相同的迭代次数中除数(分母的最小值将变为1)
int count(int n,int r)
{int maxDen=r>(n-r)?r:n-r; //larger number in the denominator
int minDen=n-maxDen; //the smaller number in denominator
double num=1;
for(int j=n;j>maxDen;j--)
{num=j*num; //for C(6,4) example num=6*5 and so on
// System.out.println("num "+num +" minDen "+minDen);
num=num/minDen; //divide num 6*5 in this case by 2
minDen--;
}
num=num%142875; //output the result modulo 142875
return (int) num;
}
但也许由于丢失精度,因为执行了更多的除法,它会给出错误的值但是它仍然为某些值提供正确的输出。因为它正确的是22 17而不是24 17。
(22 17) = 26334 //gives Correct value
(24 17)= 60353 //wrong value correct value is 60390
(25,17)=81450 //wrong value correct value is 81576
(16 15)= 16 //gives correct value
(87 28)= 54384 //wrong value correct value is 141525
我尝试使用num作为BigDecimal,因此我不得不用BigDecimal替换所有内容来执行操作。输出结果与上面代码中给出正确结果的输入相同。但是输入给出了错误的结果,该程序抛出异常
Exception in thread "main" **java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.**
at java.math.BigDecimal.divide(Unknown Source)
at Combination.NcRcount2.count(NcRcount2.java:16)
at Combination.NcRcount2.main(NcRcount2.java:37)
第16行是num = num.divide(minDen); //代替之前使用过的num / minDen,在这种情况下,num和minDen都是BigDecimal
即使数字没有精确的十进制表示,给定BigDecimal的任意精度,如果没有引发异常,结果中的错误也会被最小化。 ** 如果浮点数或双打的除法结果没有精确的十进制表示,那么为什么不抛出异常?**
我使用BigDecimal和动态编程方法验证了结果
C(n,r)=C(n-1,r-1)+C(n-1,r)
这在所有情况下都能正常使用,因为它在我看来但必须有更好的方法
BigDecimal Comb (int n, int k)
{ if(k>n-k)
k=n-k;
BigDecimal B[][]=new BigDecimal[n+1] [k+1];
for (int i = 0; i <= n; i++)
{ int min;
if(i>=k)
min=k;
else
min=i;
for (int j = 0; j <= min; j++)
{ if (j == 0 || j == i)
B[i][j] =new BigDecimal(1);
else{
if(j>i-j)
B[i][j]=B[i][i-j];
else
B[i][j] = B[i - 1][j - 1].add(B[i - 1] [j]);
}
}
}
BigDecimal div=new BigDecimal(142857);
return B[n][k].remainder(div);
}
请不要使用BigDecimal
,建议我更好的方法答案 0 :(得分:2)
public class Solution {
public static void main(String arg[]) {
Scanner s = new Scanner(System.in);
List<BigInteger> ar = new ArrayList<BigInteger>();
int tot = Integer.parseInt(s.nextLine());
BigInteger max = BigInteger.ZERO;
for (int i = 0; i < tot; i++) {
String str[] = s.nextLine().split(" ");
Long n1 = Long.parseLong(str[0]);
Long r1 = Long.parseLong(str[1]);
Long nr1 = n1 - r1;
BigInteger n = BigInteger.valueOf(n1);
BigInteger r = BigInteger.valueOf(r1);
BigInteger nr = BigInteger.valueOf(nr1);
ar.add(n);
ar.add(r);
ar.add(nr);
if (n.compareTo(max)==1) {
max=n;
}
if (r.compareTo(max)==1) {
max=r;
}
if (nr.compareTo(max)==1) {
max=nr;
}
}
HashMap<BigInteger,BigInteger> m=new HashMap<BigInteger,BigInteger>();
m.put(BigInteger.ZERO, BigInteger.ONE);
BigInteger fact=BigInteger.ONE;
for(BigInteger i=BigInteger.ONE;i.compareTo(max.add(BigInteger.ONE))==-1;i=i.add(BigInteger.ONE)){
fact=fact.multiply(i);
if(ar.contains(i)){
m.put(i, fact);
}
}
for(int i=0;i<ar.size();i=i+3){
BigInteger n=m.get(ar.get(i));
BigInteger r=m.get(ar.get(i+1));
BigInteger nr=m.get(ar.get(i+2));
BigInteger rem=r.multiply(nr);
BigInteger act=n.divide(rem);
BigInteger res=act.remainder(BigInteger.valueOf(142857));
System.out.println(res);
}
}
}
我认为此代码可能会对您有所帮助。
答案 1 :(得分:0)
关于BigDecimal代码异常的问题部分我不清楚,所以我不会对此发表评论。
关于计算nCr的乘法和除法序列,wikipedia显示了一个易于实现的公式。问题的第一部分代码可能等同于它,可能是下面的python代码。它使用64位整数运算计算高达61C30; 62C31需要另外一两个。
def D(n, k):
c, j, k = 1, n, min(k,n-k)
for i in range(1,k+1):
c, j = c*j/i, j-1
return c
这个计算顺序的工作原因是,nC(j+1) = nCj * (n-j)/(j+1)
可以从nCj = n!/j!(n-j)!
和某些代数中轻松验证,所有除法都是精确的除法。也就是说,您可以在整数运算中完全计算大nCr
和n
的{{1}},而无需任何小数位。
假设r
。
注意,减少以K为模的中间项将导致问题并且可能是不可行的。如果分子缩小为mod K,则某些除法在普通算术中不准确。如果K是素数,则扩展的GCD算法可用于找到所有数的逆K k。但由于Bézout's lemma和一些模块化代数,K = 3 * 9 * 11 * 13 * 37且逆变量K将不存在3,11,13或37倍数的数字。
答案 2 :(得分:0)
你不应该分开。
在内存中绘制Pascal triangle。这只需要添加,并且很容易允许应用模运算。
此外,这将持续不长于分裂,因为您无法避免计算因子。
package tests.StackOverflow;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class q13241166 {
public static void main(String[] args) throws IOException {
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
String s;
String[] ss;
int[] n;
int[] r;
int T;
/*
System.out.println("Input T:");
s = in.readLine();
T = Integer.parseInt(s);
if( T < 1 || T > 100000) {
throw new IllegalArgumentException();
}
*/
T = 9;
/*
n = new int[T];
r = new int[T];
System.out.println("Input n r pairs:");
for(int i=0; i<T; ++i) {
s = in.readLine();
ss = s.split("\\s+");
n[i] = Integer.parseInt(ss[0]);
if( n[i] < 1 || n[i] > 1000000000) {
throw new IllegalArgumentException();
}
r[i] = Integer.parseInt(ss[1]);
if( r[i] < 0 || r[i] > n[i]) {
throw new IllegalArgumentException();
}
}
*/
n = new int[] {2, 4, 5, 10, 22, 24, 25, 16, 87};
r = new int[] {1, 0, 2, 3, 17, 17, 17, 15, 28};
int modulobase = 142857;
int[] answers_old, answers = null;
System.out.println("Output");
for(int i=0; i<T; ++i) {
for( int nn=0; nn<=n[i]; ++nn) {
answers_old = answers;
answers = new int[nn+1];
for( int rr=0; rr<=nn; ++rr) {
if( rr == 0 || rr == nn ) {
answers[rr] = 1;
}
else {
answers[rr] = answers_old[rr-1] + answers_old[rr];
}
answers[rr] %= modulobase;
}
}
System.out.println(answers[r[i]]);
}
}
}
输出如下:
Output
2
1
10
120
26334
60390
81576
16
141525
答案 3 :(得分:0)
相当直接的实施:
public long combinations(int n, int k) {
BigInteger factorialN = factorial(n);
BigInteger factorialK = factorial(k);
BigInteger factorialNMinusK = factorial(n - k);
return factorialN.divide(factorialK.multiply(factorialNMinusK)).longValue();;
}
private BigInteger factorial(int n) {
BigInteger ret = BigInteger.ONE;
for (int i = 1; i <= n; ++i) ret = ret.multiply(BigInteger.valueOf(i));
return ret;
}