0

I want to calculate Euler number with multiple threads using this formula =∑((3k) ^ 2 + 1)/(3k)! , k =0,... ,∞ , but I am not getting the right results so far and once of the problems is that when I use fairly big number I am going out of range for decimal for factorial function here is what I've done so far

static void Main(string[] args)
{
   Console.WriteLine(Program.Calculate(5, 1));
}
public static decimal Calculate(int x, byte taskNumber)
{
   var tasks = new List<Task<decimal>>();

   for (int i = 0; i < x; i += (x / taskNumber))
   {
        int step = i;
        tasks.Add(Task.Run(() =>
        {
            int right = (step + x / taskNumber) > x ? x : (step + x / taskNumber);
            return ChunkE(step + 1, right);
        }));
    }

    Task.WaitAll(tasks.ToArray());

    return tasks.Select(t => t.Result).Aggregate(((i, next) => i + next));
    }

then I have simple factorial and euler functions

public static decimal ChunkFactorial(int left, int right)
{
    //Console.WriteLine("ChunkFactorial Thread ID :" + Thread.CurrentThread.ManagedThreadId);
    if (left == right)
    {
        return left == 0 ? 1 : left;
    }
    else
    {
        return right * ChunkFactorial(left, right - 1);
    }
}

public static decimal ChunkE(int left, int right)
{
    if(left == right)
    {
        return left == 0 ? 1 : left;
    }
    else
    {
        return ((3 * right) * (3 * right) + 1) / ChunkFactorial(left, right) + ChunkE(left, right - 1);
    }
}

What I want to achieve is calculating Euler number up until x precision using different amount of tasks.

What I am getting with this call is 41.01666..7 if I increase x decimal will eventually overflow. How can I fix this problem I tried using BigInteger but then it begin to be mess and I loose precision of the result.. Any ideas?

Also when I start the program with 1 task I get one result and when I start program with 4(or different of 1) I get different result I don't know what I am missing..

kuskmen
  • 3,648
  • 4
  • 27
  • 54
  • You seem to have forgotten the factor 3 in (3k)! in the denominator. – Lutz Lehmann Jun 26 '16 at 08:23
  • Yes, I managed to catch that but I faced another problem.. ChunkFactorial NEEDS to be from BigInteger type , but I cant devide decimal with BigInteger .. any suggestions how should I proceed? – kuskmen Jun 26 '16 at 08:26
  • Note that your chunk factorial is only a partial factorial, `n! = (m-1)!*ChunkFactorial(m,n)`. The factor `1/(m-1)!` is missing in the final summation. – Lutz Lehmann Jun 26 '16 at 08:39
  • Using the [`Parallel.ForEach`](https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.parallel.foreach) method should be preferable to starting tasks manually, because it allows you to control the maximum degree of parallelism. – Theodor Zoulias May 20 '22 at 16:47

2 Answers2

0

If you are allowed to transform the terms before implementation, consider that

(3k)^2/(3k)! = (3k)/(3k-1)! = 1/(3k-2)!+1/(3k-1)!

which then proves that your formula really computes the Euler number.

But you need to include the factor 3 in (3k)! in the factorial computations.

It should be perfectly fine to use the floating point type for the factorial computation, since the required precision should be reached long before overflow happens. Note that an error bound is twice the next term not in the partial sum.

Lutz Lehmann
  • 25,219
  • 2
  • 22
  • 51
  • Formula is actually (3k)^2 + 1 / (3k)! does that change the transformation or you just made a typo? – kuskmen Jun 26 '16 at 08:34
  • No, that just adds the 1/(3k)! that completes the cycle to sum over 1/n!. – Lutz Lehmann Jun 26 '16 at 08:36
  • Okay, what about the type I should be using floating point is not enough since I would like to compute with precision of 10 000 and more .. it will overflow for sure.. – kuskmen Jun 26 '16 at 08:38
  • Then you need an arbitrary precision library (bc, mpfr, gmp). C# decimal is only a 64bit (or 128 bit?) data type interpreted as decimal instead of binary, thus of quite limited/fixed capabilities. – Lutz Lehmann Jun 26 '16 at 08:43
0

As the main point of this question was already answered I just add some stuff:

You can do few upgrades to your code like while computing factorial each time you can divide both divident and divider by 2 or 5 do it to lower the bits needed. This will also boost the speed a lot if done right.

But in the end Your formula will still take forever to converge to e! Not to mention the overflows due to factorial. I am using something better suited for computers (not sure where the formula comes from though it was ages ago)...

e = (1+1/x)^x

where x -> +inf This has many advantages when using binary representations of numbers. I refined the process by using powers of 2 for x which simplifies things a lot... My computation code (based on my arbnum class) is as this:

    arbnum c,x;
    int bit=512;        // min(int_bits,fract_bits)/2 ... this is just remnant from fixed point code where bitwidth matters
    // e=(1+1/x)^x  ... x -> +inf
    c.one(); c>>=bit; c++;  // c = 1.000...0001b = (1+1/x)          = 2^-bits + 1
//  x.one(); x<<=bit;       // x = 1000...000.0b =    x   = 1/(c-1) = 2^+bits
        for (;bit;bit--)        // c = c^x = c^(2^bits) = e
            {
            c*=c;
            c._normalize(2048); // this just cut off the result to specific number of fractional bits only to speed up the computation instead you should cut of only last zeros !!!
            }

As you can see I am computing target precision from the start (bits) and truncating the result (to manageable bit width for fractional part). If you got fixed point arithmetics then you do not need to do this at all (this was just my fast attempt to port it from my ancient fixed point code to my new arbnum class).

So set the bits constant to what you want and also the truncating size. Both should be based on your target precision. As you can see this is not iterative process... The only thing that is in loop is the power. It is optimized a bit to understand it you need to realize what are you computing:

(1.000...0001b) ^ (1<<bits)

so I just square the c until first signed bit in x is hit. Beware each squaring doubles the needed fractional bit width ... that is the reason for truncation (it lowers accuracy bat boosts performance much much more)

As you can see this approach is quite nice as it does not need any division ... only bit operations and multiplication.

Here comparison:

[e]
reference          2.7182818284590452353602874713526624977572470936999595749669676277240766303535475945713821785251664274
my e               2.7182818284590452353602874713526624977572470936999595749669676277240766303535475945713821785251664274
00000200 method 0: 2.7182818280709941626033162692782994930797533824790948079298224728031965377356362865659816488677135209

The reference is first 100 digits of e. The my e is result from this code and method 0 is result from your equation after 200 iterations in arbitrary precision with last zero truncating and the %2,%5 optimization to speed it up.

Mine approach took just few milliseconds and yours ~20 seconds to get to that point...

Here the same computed in decadic base directly on strings:

Spektre
  • 49,595
  • 11
  • 110
  • 380
  • 1
    Note that `(1+1/n)^n=exp(n*ln(1+1/n))=exp(1-1/(2n)+1/(3n^2)-+...)` has an error of about `1/(2n)` while the partial sum of the exponential series has an error smaller than 2/(n+1)!. The best methods combine both approaches and compute exp(1)=exp(2^-n)^(2^n) with a small degree partial sum inside and using repeated squaring for the external power. – Lutz Lehmann Jun 26 '16 at 15:40
  • @LutzL which is exactly what the code does ... unless I am missing something – Spektre Jun 26 '16 at 15:42
  • 1
    The idea is to use x=2^-n, compute y=1+x+x^2/2+...+x^m/m! and then y^(2^n). Your code is using m=1. The error in y is smaller than 2x^(m+1)/(m+1)! and the error of the power thus about 2*2^n*x^(m+1)/(m+1)! – Lutz Lehmann Jun 26 '16 at 15:52
  • @LutzL got it ... does the error of `y` also account for the numeric error of the division ? – Spektre Jun 26 '16 at 18:59
  • No, that is purely the truncation error. To get the number of bits despite the floating point error, you have to add 1 or 1.5 bits per executed operation, thus all operations have to be with N+3*sqrt(N) bits from the start (assuming Horner evaluation of y with short-integer division). – Lutz Lehmann Jun 26 '16 at 19:53
  • This is soo cool! Do you think that if I use threads I can improve the computation speed even more? – kuskmen Jun 27 '16 at 07:34
  • @kuskmen not easily done for mine approach ... The only thing parallel-is-able (and also the only performance bottleneck) is the squaring and you need to have quite a bit of knowledge to improve it ... see [Fast bignum square computation](http://stackoverflow.com/q/18465326/2521214) for some ideas ... – Spektre Jun 27 '16 at 11:46