これは 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 です
数値が正確な 10 進数表現を持たない場合でも、BigDecimal の任意の精度を考えると、例外がスローされなければ、結果のエラーは最小限に抑えられます。** float または double の除算の結果が正確な 10 進表現を持たない場合、例外がスローされないのはなぜですか?**
動的計画法のアプローチで 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 を使用せずにこれを行うより良い方法を提案してください