带有任务的欧拉数

时间:2016-06-25 23:08:18

标签: c# multithreading math multiprocessing eulers-number

我想用多个线程计算Euler数,使用这个公式=Σ((3k)  ^ 2 + 1)/(3k)! ,k = 0,...,∞,但到目前为止我没有得到正确的结果,一旦出现问题,当我使用相当大的数字时,我会超出因子函数的十进制范围,这就是我&# 39;到目前为止已经完成了

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));
    }

然后我有简单的阶乘和欧拉函数

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);
    }
}
  

我想要实现的是使用不同数量的任务计算欧拉数,直到x精度。

     

我通过此调用得到的是41.01666..7如果我增加x十进制最终会溢出。我怎么能解决这个问题我尝试使用BigInteger然后它开始变得混乱而且我的结果精确度松散了......有什么想法吗?

     

当我用1个任务启动程序时,我得到一个结果,当我用4个(或不同的1个)开始程序时,我得到不同的结果我不知道我错过了什么..

2 个答案:

答案 0 :(得分:0)

如果允许您在实施前转换条款,请考虑

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

然后证明你的公式真的计算了欧拉数。

但你需要在(3k)中加入因子3!在阶乘计算中。

使用浮点类型进行因子计算应该是完全正确的,因为在溢出发生之前很久就要达到所需的精度。请注意,错误界限是下一个术语的两倍而不是部分总和。

答案 1 :(得分:0)

由于这个问题的要点已经回答,我只想补充一些内容:

你可以对你的代码进行一些升级,就像计算阶乘一样,每次你可以将divident和divider除以25来降低所需的位数。如果做得好,这也会提高速度。

但最终你的公式仍将永远收敛到e!更不用说由于阶乘造成的溢出。我正在使用更适合电脑的东西(不知道配方来自哪里,虽然很久以前)......

e = (1+1/x)^x

其中x -> +inf当使用数字的二进制表示时,这有许多优点。我使用2x的权限来改进过程,这简化了很多事情......我的计算代码(基于我的arbnum类)是这样的:

    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 !!!
            }

正如您所看到的,我从一开始就计算目标精度(bits)并截断结果(到小数部分的可管理位宽)。如果你有定点算术,那么你根本不需要这样做(这只是我快速尝试将它从我的古老定点代码移植到我的新arbnum类)。

因此,将位设置为您想要的值以及截断大小。两者都应该基于您的目标精度。正如你所看到的,这不是迭代过程......循环中唯一的东西就是power。它有一点优化,以了解它你需要实现你的计算:

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

所以我只是调整c,直到x中的第一个有符号位被击中。注意每个平方都需要将所需的分数位宽加倍...这就是截断的原因(它会降低精确度,使得性能提升得更多)

正如你所看到的,这种方法非常好,因为它不需要任何除法......只有位操作和乘法。

这里比较:

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

referencee的前100位数字。 my e是此代码的结果,而method 0是您的等式产生的,经过200次迭代后,以任意精度进行最后零截断,%2,%5优化以加快速度。

我的方法花了几毫秒而你的~20秒才到达那一点......