So I have a problem that I'm stuck on it since 3 days ago.
You want to participate at the lottery 6/49 with only one winning variant(simple) and you want to know what odds of winning you have:
-at category I (6 numbers)
-at category II (5 numbers)
-at category III (4 numbers)
Write a console app which gets from input the number of total balls, the number of extracted balls, and the category, then print the odds of winning with a precision of 10 decimals if you play with one simple variant.
Inputs:
40
5
II
Result I must print:
0.0002659542
static void Main(string[] args)
{
int numberOfBalls = Convert.ToInt32(Console.ReadLine());
int balls = Convert.ToInt32(Console.ReadLine());
string line = Console.ReadLine();
int theCategory = FindCategory(line);
double theResult = CalculateChance(numberOfBalls, balls, theCategory);
Console.WriteLine(theResult);
}
static int FindCategory (string input)
{
int category = 0;
switch (input)
{
case "I":
category = 1;
break;
case "II":
category = 2;
break;
case "III":
category = 3;
break;
default:
Console.WriteLine("Wrong category.");
break;
}
return category;
}
static int CalculateFactorial(int x)
{
int factorial = 1;
for (int i = 1; i <= x; i++)
factorial *= i;
return factorial;
}
static int CalculateCombinations(int x, int y)
{
int combinations = CalculateFactorial(x) / (CalculateFactorial(y) * CalculateFactorial(x - y));
return combinations;
}
static double CalculateChance(int a, int b, int c)
{
double result = c / CalculateCombinations(a, b);
return result;
}
Now my problems: I'm pretty sure I have to use Combinations. For using combinations I need to use Factorials. But on the combinations formula I'm working with pretty big factorials so my numbers get truncated. And my second problem is that I don't really understand what I have to do with those categories, and I'm pretty sure I'm doing wrong on that method also. I'm new to programming so please bare with me. And I can use for this problem just basic stuff, like conditions, methods, primitives, arrays.
Let's start from combinatorics; first, come to terms:
a - all possible numbers (40 in your test case)
t - all taken numbers (5 in your test case)
c - category (2) in your test case
So we have
t - c + 1 for numbers which win and c - 1 for numbers which lose. Let's count combinations:
All combinations: take t from a possible ones:
A = a! / t! / (a - t)!
Winning numbers' combinations: take t - c + 1 winning number from t possible ones:
W = t! / (t - c + 1)! / (t - t + c - 1) = t! / (t - c + 1)! / (c - 1)!
Lost numbers' combinations: take c - 1 losing numbers from a - t possible ones:
L = (a - t)! / (c - 1)! / (a - t - c + 1)!
All combinations with category c, i.e. with exactly t - c + 1 winning and c - 1 losing numbers:
C = L * W
Probability:
P = C / A = L * W / A =
t! * t! (a - t)! * (a - t)! / (t - c + 1)! / (c - 1)! / (c - 1)! / (a - t- c + 1)! / a!
Ugh! Not let's implement some code for it:
Code:
// double : note, that int is too small for 40! and the like values
private static double Factorial(int value) {
double result = 1.0;
for (int i = 2; i <= value; ++i)
result *= i;
return result;
}
private static double Chances(int a, int t, int c) =>
Factorial(a - t) * Factorial(t) * Factorial(a - t) * Factorial(t) /
Factorial(t - c + 1) /
Factorial(c - 1) /
Factorial(c - 1) /
Factorial(a - t - c + 1) /
Factorial(a);
Test:
Console.Write(Chances(40, 5, 2));
Outcome:
0.00026595421332263435
Edit:
in terms of Combinations, if C(x, y) which means "take y items from x" we
have
A = C(a, t); W = C(t, t - c + 1); L = C(a - t, c - 1)
and
P = W * L / A = C(t, t - c + 1) * C(a - t, c - 1) / C(a, t)
Code for Combinations is quite easy; the only trick is that we return double:
// Let'g get rid of noisy "Compute": what else can we do but compute?
// Just "Combinations" without pesky prefix.
static double Combinations(int x, int y) =>
Factorial(x) / Factorial(y) / Factorial(x - y);
private static double Chances(int a, int t, int c) =>
Combinations(t, t - c + 1) *
Combinations(a - t, c - 1) /
Combinations(a, t);
You can fiddle the solution
Related
static void Main(string[] args)
{
Console.WriteLine("Enter your number: ");
int number= Convert.ToInt32(Console.ReadLine());
int number2 = Convert.ToInt32(Console.ReadLine());
double factorial = Factorial(number,number2);
Console.WriteLine("Factorial of " + number +" / "+ number2 + " = " + factorial );
Console.ReadKey();
}
//Factorial function added
public static double Factorial(int number, int number2)
{
if (number == 1 && number2 ==1 )
{
return 1;
}
double factorial = 1;
double factorial1 = 1;
double factorial2 = 1;
for (int i = number, j = number2; i >= 1 && j >= 1; i--, j--)
{
factorial1 = (factorial * i);
factorial2 = (factorial * j);
factorial = factorial1 / factorial2;
}
return factorial;
}
Your attempted solution is simply so overcomplicated, I wouldn't know where to begin. This usually happens when you don't stop to think about how you'd resolve this problem by hand:
So, the question is, whats 5!/3!? Ok, lets write it out:
(5 * 4 * 3 * 2 * 1) / (3 * 2 * 1)
Wow, that looks like it can be simplified simply to 5 * 4.
The key insight here is that m! % n! = 0 if n is less or equal to m. In other words, m! is always divisible by n! because there is always an integer r such that r * n! = m!, and you don't need to evaluate m! or n! to figure out what r is, you simple do:
r = m * (m - 1) * (m - 2) * ... * (n + 1); // m >= n
If n > m, r is zero unless you are looking for a real number solution in which case you would simply evaluate r as n! / m! and then return 1.0 / r because m! / n! = 1 / (n! / m!).
How to evaluate r?
public static long DivideFactorials(int m, int n)
{
if (n > m)
return 0;
var r = 1L;
for (var k = m; k > n; k--)
r *= k;
return r;
}
Or the real number solution:
public static double DivideFactorials(int m, int n)
{
if (n > m)
return 1 / DivideFactorials(n, m);
var r = 1.0;
for (var k = m; k > n; k--)
r *= k;
return r;
}
If I had to save your try:
public static double Factorial(int number, int number2)
{
if (number == 1 && number2 == 1)
{
return 1;
}
double facNum = 1;
double facNum2 = 1;
// counting up is easier, we start at 2 as we initialized to 1
// we count up to the max of both numbers
for (int i = 2; i <= Math.Max(number, number2); i++)
{
if (i <= number)
facNum *= i; // we mult this until we reached number
if (i <= number2)
facNum2 *= i; // we mult this until we reach number2
}
// return the devision of both - this wont handle number < number2 well!
return facNum / facNum2; // do this outside the loop
}
If I had to create a solution:
Factorial division of integers has 3 outcomes (I can think of):
N! / O! with N == O:
let N=3, O=3
N! = 1*2*3
O! = 1*2*3
N! / O! = 1*2*3/(1*2*3) == 1
N! / O! with N > O:
let N=5, O=3
N! = 1*2*3*4*5
O! = 1*2*3
N! / O! == 1*2*3*4*5/(1*2*3) == 4*5 == 20
N! / O! with N < O:
let N=3, O=5
N! = 1*2*3
O! = 1*2*3*4*5
N! / O! == 1*2*3/(1*2*3*4*5) == 1/(4*5) == 1/20
Based on this I would model the problem like that:
using System;
using System.Collections.Generic;
using System.Linq;
internal class Program
{
public static decimal CalcFactDivision(int n1, int n2)
{
// calclulate the division of a factorial by another, num1 must be >= num2
IEnumerable<int> getRemaining(int num1, int num2)
{
// special cases: div by 0 and 0 div something
if (num2 == 0)
num2 = 1; // 0! == 1
else if (num1 == 0)
return new[] { 0 };
// get all numbers that make up the factorial in one step
// I can guarantee that num1 will always be bigger then num2
// by how I call this
return Enumerable.Range(num2 + 1, num1 - num2);
}
// calculate the product of an ienumerable of ints
int product(IEnumerable<int> nums) => nums.Aggregate((a, b) => a * b);
if (n1 == n2)
return 1;
else if (n1 > n2) // use product(...) to calc
return product(getRemaining(n1, n2));
else // flip them and use 1/product(...) to calc
return (decimal)1 / product(getRemaining(n2, n1));
}
static void Main(string[] args)
{
foreach (var a in Enumerable.Range(1, 10))
Console.WriteLine($"{a}! / {10 - a}! = {CalcFactDivision(a, 10 - a)} ");
Console.ReadLine();
}
}
Output:
1! / 9! = 0,0000027557319223985890652557
2! / 8! = 0,0000496031746031746031746032
3! / 7! = 0,0011904761904761904761904762
4! / 6! = 0,0333333333333333333333333333
5! / 5! = 1
6! / 4! = 30
7! / 3! = 840
8! / 2! = 20160
9! / 1! = 362880
10! / 0! = 3628800
I'm trying to calculate the cumulative binomial probability of 'n' trials, with 'p' probability and 'r' as the successful outcome of each trial. I have written the following code that works sometimes, but not always:
Console.WriteLine ();
Console.WriteLine ("B~(n, p)");
incorrectN:
Console.WriteLine ("Enter value of 'n': ");
int n = Convert.ToInt32 (Console.ReadLine ());
if (n < 0) {
Console.WriteLine ("ERROR: 'n' must be greater than 0");
goto incorrectN;
}
incorrectP:
Console.WriteLine ();
Console.WriteLine ("Enter value of 'p': ");
double p = Convert.ToDouble (Console.ReadLine ());
if (p > 1) {
Console.WriteLine ();
Console.WriteLine ("ERROR: 'p' must be between 0 and 1");
goto incorrectP;
}
Console.WriteLine ();
incorrectS:
int r = GetR();
int k = r;
double binomTotal = 0;
for (int j = r + 1; j > 0; j--) {
int nCr = Factorial(n) / (Factorial(n - (r - k)) * Factorial(r - k));
binomTotal = binomTotal + nCr * Math.Pow(p, (r - k)) * Math.Pow(1 - p, (n - (r - k)));
k--;
}
Console.WriteLine();
Console.WriteLine(binomTotal);
P.S. I have written the GetR() and Factorial() functions elsewhere within the class, where GetR() asks the user for the value of 'r' and Factorial() is defined as follows:
public static int Factorial(int x)
{
return x <= 1 ? 1 : x * Factorial(x - 1);
}
I tested the code with values n = 10, p = 0.5 and r = 5 and the output is 0.623046875, which is correct. However, when I use n = 13, p = 0.35 and r = 7, I get 0.297403640622647 instead of 0.9538.
Any help would be much appreciated.
In addition to your own answer:
public static double Factorial(double x)
{
return x <= 1 ? 1 : x * Factorial(x - 1);
}
accepts a double parameter, which means that x is not restricted to be an integer.
So you could call your Factorial method like this.
var fac1 = Factorial(1.4);
var fac2 = Factorial(2.7);
However, this does not make sense since the factorial is defined only* for , meaning that
is undefined.
So, instead of using double and allowing for invalid inputs, you should be using long instead, which has a greater range than int.
public static long Factorial(long x)
{
return x <= 1 ? 1 : x * Factorial(x - 1);
}
* there are some cases where factorials can be used with real values as well - e.g. by using the gamma function - but I don't think they're relevant to your use case and therefore you should not allow invalid parameters.
Change:
public static int Factorial(int x)
{
return x <= 1 ? 1 : x * Factorial(x - 1);
}
To:
public static double Factorial(double x)
{
return x <= 1 ? 1 : x * Factorial(x - 1);
}
Because Factorial(13) is too large for Int32.
I'm trying to write a simple quadratic equation solver in C#, but for some reason it's not quite giving me the correct answers. In fact, it's giving me extremely large numbers as answers, usually well into the millions.
Could anyone please shed some light? I get the exact same answer for the positive root and the negative root as well. (Tried two different methods of math)
static void Main(string[] args)
{
int a;
int b;
int c;
Console.WriteLine("Hi, this is a Quadratic Equation Solver!");
Console.WriteLine("a-value: ");
try
{
a = int.Parse(Console.ReadLine());
Console.WriteLine("b-value: ");
b = int.Parse(Console.ReadLine());
Console.WriteLine("c-value: ");
c = int.Parse(Console.ReadLine());
Console.WriteLine("Okay, so your positive root is: " + quadForm(a, b, c, true));
Console.WriteLine("And your negative root is: " + quadForm(a, b, c, false));
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
Console.ReadLine();
}
static int quadForm(int a, int b, int c, Boolean pos)
{
int x = 0;
if (pos)
x = ((-b + (int) (Math.Sqrt((b * b) - (4 * a * c)))) / (2 * a));
else
x = ((-Math.Abs(b) - (int) (Math.Sqrt(Math.Pow(b,2) - (4 * a * c)))) / (2 * a));
return x;
}
Try this version of quadForm:
static double quadForm(int a, int b, int c, bool pos)
{
var preRoot = b * b - 4 * a * c;
if (preRoot < 0)
{
return double.NaN;
}
else
{
var sgn = pos ? 1.0 : -1.0;
return (sgn * Math.Sqrt(preRoot) - b) / (2.0 * a);
}
}
I have 3 very large signed integers.
long x = long.MaxValue;
long y = long.MaxValue - 1;
long z = long.MaxValue - 2;
I want to calculate their truncated average. Expected average value is long.MaxValue - 1, which is 9223372036854775806.
It is impossible to calculate it as:
long avg = (x + y + z) / 3; // 3074457345618258600
Note: I read all those questions about average of 2 numbers, but I don't see how that technique can be applied to average of 3 numbers.
It would be very easy with the usage of BigInteger, but let's assume I cannot use it.
BigInteger bx = new BigInteger(x);
BigInteger by = new BigInteger(y);
BigInteger bz = new BigInteger(z);
BigInteger bavg = (bx + by + bz) / 3; // 9223372036854775806
If I convert to double, then, of course, I lose precision:
double dx = x;
double dy = y;
double dz = z;
double davg = (dx + dy + dz) / 3; // 9223372036854780000
If I convert to decimal, it works, but also let's assume that I cannot use it.
decimal mx = x;
decimal my = y;
decimal mz = z;
decimal mavg = (mx + my + mz) / 3; // 9223372036854775806
Question: Is there a way to calculate the truncated average of 3 very large integers only with the usage of long type? Don't consider that question as C#-specific, just it is easier for me to provide samples in C#.
This code will work, but isn't that pretty.
It first divides all three values (it floors the values, so you 'lose' the remainder), and then divides the remainder:
long n = x / 3
+ y / 3
+ z / 3
+ ( x % 3
+ y % 3
+ z % 3
) / 3
Note that the above sample does not always work properly when having one or more negative values.
As discussed with Ulugbek, since the number of comments are exploding below, here is the current BEST solution for both positive and negative values.
Thanks to answers and comments of Ulugbek Umirov, James S, KevinZ, Marc van Leeuwen, gnasher729 this is the current solution:
static long CalculateAverage(long x, long y, long z)
{
return (x % 3 + y % 3 + z % 3 + 6) / 3 - 2
+ x / 3 + y / 3 + z / 3;
}
static long CalculateAverage(params long[] arr)
{
int count = arr.Length;
return (arr.Sum(n => n % count) + count * (count - 1)) / count - (count - 1)
+ arr.Sum(n => n / count);
}
NB - Patrick has already given a great answer. Expanding on this you could do a generic version for any number of integers like so:
long x = long.MaxValue;
long y = long.MaxValue - 1;
long z = long.MaxValue - 2;
long[] arr = { x, y, z };
var avg = arr.Select(i => i / arr.Length).Sum()
+ arr.Select(i => i % arr.Length).Sum() / arr.Length;
Patrick Hofman has posted a great solution. But if needed it can still be implemented in several other ways. Using the algorithm here I have another solution. If implemented carefully it may be faster than the multiple divisions in systems with slow hardware divisors. It can be further optimized by using divide by constants technique from hacker's delight
public class int128_t {
private int H;
private long L;
public int128_t(int h, long l)
{
H = h;
L = l;
}
public int128_t add(int128_t a)
{
int128_t s;
s.L = L + a.L;
s.H = H + a.H + (s.L < a.L);
return b;
}
private int128_t rshift2() // right shift 2
{
int128_t r;
r.H = H >> 2;
r.L = (L >> 2) | ((H & 0x03) << 62);
return r;
}
public int128_t divideby3()
{
int128_t sum = {0, 0}, num = new int128_t(H, L);
while (num.H || num.L > 3)
{
int128_t n_sar2 = num.rshift2();
sum = add(n_sar2, sum);
num = add(n_sar2, new int128_t(0, num.L & 3));
}
if (num.H == 0 && num.L == 3)
{
// sum = add(sum, 1);
sum.L++;
if (sum.L == 0) sum.H++;
}
return sum;
}
};
int128_t t = new int128_t(0, x);
t = t.add(new int128_t(0, y));
t = t.add(new int128_t(0, z));
t = t.divideby3();
long average = t.L;
In C/C++ on 64-bit platforms it's much easier with __int128
int64_t average = ((__int128)x + y + z)/3;
You can calculate the mean of numbers based on the differences between the numbers rather than using the sum.
Let's say x is the max, y is the median, z is the min (as you have). We will call them max, median and min.
Conditional checker added as per #UlugbekUmirov's comment:
long tmp = median + ((min - median) / 2); //Average of min 2 values
if (median > 0) tmp = median + ((max - median) / 2); //Average of max 2 values
long mean;
if (min > 0) {
mean = min + ((tmp - min) * (2.0 / 3)); //Average of all 3 values
} else if (median > 0) {
mean = min;
while (mean != tmp) {
mean += 2;
tmp--;
}
} else if (max > 0) {
mean = max;
while (mean != tmp) {
mean--;
tmp += 2;
}
} else {
mean = max + ((tmp - max) * (2.0 / 3));
}
Patching Patrick Hofman's solution with supercat's correction, I give you the following:
static Int64 Avg3 ( Int64 x, Int64 y, Int64 z )
{
UInt64 flag = 1ul << 63;
UInt64 x_ = flag ^ (UInt64) x;
UInt64 y_ = flag ^ (UInt64) y;
UInt64 z_ = flag ^ (UInt64) z;
UInt64 quotient = x_ / 3ul + y_ / 3ul + z_ / 3ul
+ ( x_ % 3ul + y_ % 3ul + z_ % 3ul ) / 3ul;
return (Int64) (quotient ^ flag);
}
And the N element case:
static Int64 AvgN ( params Int64 [ ] args )
{
UInt64 length = (UInt64) args.Length;
UInt64 flag = 1ul << 63;
UInt64 quotient_sum = 0;
UInt64 remainder_sum = 0;
foreach ( Int64 item in args )
{
UInt64 uitem = flag ^ (UInt64) item;
quotient_sum += uitem / length;
remainder_sum += uitem % length;
}
return (Int64) ( flag ^ ( quotient_sum + remainder_sum / length ) );
}
This always gives the floor() of the mean, and eliminates every possible edge case.
Because C uses floored division rather than Euclidian division, it may easier to compute a properly-rounded average of three unsigned values than three signed ones. Simply add 0x8000000000000000UL to each number before taking the unsigned average, subtract it after taking the result, and use an unchecked cast back to Int64 to get a signed average.
To compute the unsigned average, compute the sum of the top 32 bits of the three values. Then compute the sum of the bottom 32 bits of the three values, plus the sum from above, plus one [the plus one is to yield a rounded result]. The average will be 0x55555555 times the first sum, plus one third of the second.
Performance on 32-bit processors might be enhanced by producing three "sum" values each of which is 32 bits long, so that the final result is ((0x55555555UL * sumX)<<32) + 0x55555555UL * sumH + sumL/3; it might possibly be further enhanced by replacing sumL/3 with ((sumL * 0x55555556UL) >> 32), though the latter would depend upon the JIT optimizer [it might know how to replace a division by 3 with a multiply, and its code might actually be more efficient than an explicit multiply operation].
If you know you have N values, can you just divide each value by N and sum them together?
long GetAverage(long* arrayVals, int n)
{
long avg = 0;
long rem = 0;
for(int i=0; i<n; ++i)
{
avg += arrayVals[i] / n;
rem += arrayVals[i] % n;
}
return avg + (rem / n);
}
You could use the fact that you can write each of the numbers as y = ax + b, where x is a constant. Each a would be y / x (the integer part of that division). Each b would be y % x (the rest/modulo of that division). If you choose this constant in an intelligent way, for example by choosing the square root of the maximum number as a constant, you can get the average of x numbers without having problems with overflow.
The average of an arbitrary list of numbers can be found by finding:
( ( sum( all A's ) / length ) * constant ) +
( ( sum( all A's ) % length ) * constant / length) +
( ( sum( all B's ) / length )
where % denotes modulo and / denotes the 'whole' part of division.
The program would look something like:
class Program
{
static void Main()
{
List<long> list = new List<long>();
list.Add( long.MaxValue );
list.Add( long.MaxValue - 1 );
list.Add( long.MaxValue - 2 );
long sumA = 0, sumB = 0;
long res1, res2, res3;
//You should calculate the following dynamically
long constant = 1753413056;
foreach (long num in list)
{
sumA += num / constant;
sumB += num % constant;
}
res1 = (sumA / list.Count) * constant;
res2 = ((sumA % list.Count) * constant) / list.Count;
res3 = sumB / list.Count;
Console.WriteLine( res1 + res2 + res3 );
}
}
I also tried it and come up with a faster solution (although only by a factor about 3/4). It uses a single division
public static long avg(long a, long b, long c) {
final long quarterSum = (a>>2) + (b>>2) + (c>>2);
final long lowSum = (a&3) + (b&3) + (c&3);
final long twelfth = quarterSum / 3;
final long quarterRemainder = quarterSum - 3*twelfth;
final long adjustment = smallDiv3(lowSum + 4*quarterRemainder);
return 4*twelfth + adjustment;
}
where smallDiv3 is division by 3 using multipliation and working only for small arguments
private static long smallDiv3(long n) {
assert -30 <= n && n <= 30;
// Constants found rather experimentally.
return (64/3*n + 10) >> 6;
}
Here is the whole code including a test and a benchmark, the results are not that impressive.
This function computes the result in two divisions. It should generalize nicely to other divisors and word sizes.
It works by computing the double-word addition result, then working out the division.
Int64 average(Int64 a, Int64 b, Int64 c) {
// constants: 0x10000000000000000 div/mod 3
const Int64 hdiv3 = UInt64(-3) / 3 + 1;
const Int64 hmod3 = UInt64(-3) % 3;
// compute the signed double-word addition result in hi:lo
UInt64 lo = a; Int64 hi = a>=0 ? 0 : -1;
lo += b; hi += b>=0 ? lo<b : -(lo>=UInt64(b));
lo += c; hi += c>=0 ? lo<c : -(lo>=UInt64(c));
// divide, do a correction when high/low modulos add up
return hi>=0 ? lo/3 + hi*hdiv3 + (lo%3 + hi*hmod3)/3
: lo/3+1 + hi*hdiv3 + Int64(lo%3-3 + hi*hmod3)/3;
}
Math
(x + y + z) / 3 = x/3 + y/3 + z/3
(a[1] + a[2] + .. + a[k]) / k = a[1]/k + a[2]/k + .. + a[k]/k
Code
long calculateAverage (long a [])
{
double average = 0;
foreach (long x in a)
average += (Convert.ToDouble(x)/Convert.ToDouble(a.Length));
return Convert.ToInt64(Math.Round(average));
}
long calculateAverage_Safe (long a [])
{
double average = 0;
double b = 0;
foreach (long x in a)
{
b = (Convert.ToDouble(x)/Convert.ToDouble(a.Length));
if (b >= (Convert.ToDouble(long.MaxValue)-average))
throw new OverflowException ();
average += b;
}
return Convert.ToInt64(Math.Round(average));
}
Try this:
long n = Array.ConvertAll(new[]{x,y,z},v=>v/3).Sum()
+ (Array.ConvertAll(new[]{x,y,z},v=>v%3).Sum() / 3);
I asked a question about having the Excel's BetaInv function ported to .NET: BetaInv function in SQL Server
now I managed to write that function in pure dependency less C# code and I do get the same results than in MS Excel up to 6 or 7 digits after comma, results are fine for us, the problem is that such code is embedded in a SQL CLR Function and gets called thousands of time from a stored procedure and makes the execution of the whole procedure about 50% slower, from 30 seconds up to a minute if I use that function or not.
here some code of it, I am not asking a deep analysis but is there anybody who sees any major performance issue in the way I am doing this calculations? like for example usage of other data types instead of doubles or whatsoever... ?
private static double betacf(double a, double b, double x)
{
int m, m2;
double aa, c, d, del, h, qab, qam, qap;
qab = a + b;
qap = a + 1.0;
qam = a - 1.0;
c = 1.0; // First step of Lentz’s method.
d = 1.0 - qab * x / qap;
if (System.Math.Abs(d) < FPMIN)
{
d = FPMIN;
}
d = 1.0 / d;
h = d;
for (m = 1; m <= MAXIT; ++m)
{
m2 = 2 * m;
aa = m * (b - m) * x / ((qam + m2) * (a + m2));
d = 1.0 + aa * d; //One step (the even one) of the recurrence.
if (System.Math.Abs(d) < FPMIN)
{
d = FPMIN;
}
c = 1.0 + aa / c;
if (System.Math.Abs(c) < FPMIN)
{
c = FPMIN;
}
d = 1.0 / d;
h *= d * c;
aa = -(a + m) * (qab + m) * x / ((a + m2) * (qap + m2));
d = 1.0 + aa * d; // Next step of the recurrence (the odd one).
if (System.Math.Abs(d) < FPMIN)
{
d = FPMIN;
}
c = 1.0 + aa / c;
if (System.Math.Abs(c) < FPMIN)
{
c = FPMIN;
}
d = 1.0 / d;
del = d * c;
h *= del;
if (System.Math.Abs(del - 1.0) < EPS)
{
// Are we done?
break;
}
}
if (m > MAXIT)
{
return 0;
}
else
{
return h;
}
}
private static double gammln(double xx)
{
double x, y, tmp, ser;
double[] cof = new double[] { 76.180091729471457, -86.505320329416776, 24.014098240830911, -1.231739572450155, 0.001208650973866179, -0.000005395239384953 };
y = xx;
x = xx;
tmp = x + 5.5;
tmp -= (x + 0.5) * System.Math.Log(tmp);
ser = 1.0000000001900149;
for (int j = 0; j <= 5; ++j)
{
y += 1;
ser += cof[j] / y;
}
return -tmp + System.Math.Log(2.5066282746310007 * ser / x);
}
The only thing that stands out for me, and is usually a performance hit, is memory allocation. I don't know how often gammln is called but you might want to move the double[] cof = new double[] {} to a static one time only allocation.
double is usually the best type. Especially since the functions in Math take doubles. Unfortunately I see no obvious improvements to make on your code.
It might be possible to use look up tables to get a better first estimate on which you iterate, but since I don't know the Math behind what you're doing I don't know if that's possible in this specific case.
Obviously larger epsilons will improve performance. So choose it as large as possible while fulfilling your accuracy demands.
If the function gets called repeatedly with the same parameters you might be able to cache results.
One thing that looks odd is the way you force small values for c, d,... to FPMIN. My instinct is that this might lead to suboptimal step sizes.
All I've got is unrolling the j loop in gammln, but it'll make at most a tiny difference.
A more radical thought would be to rewrite in pure T-SQL, since it has everything you use: + - * / abs log are all available.