0

我想用这个公式计算多线程的欧拉数 =∑((3k) ^ 2 + 1)/(3k)!, k =0,... ,∞ ,但到目前为止我还没有得到正确的结果,其中一个问题是,当我使用相当大的数字时,我将超出阶乘函数的小数范围,这就是我的到目前为止已经完成了

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 个不同)启动程序时,我得到不同的结果,我不知道我错过了什么..

4

2 回答 2

0

由于已经回答了这个问题的要点,我只是添加了一些内容:

您可以对代码进行一些升级,例如每次计算阶乘时,您可以将除数和除数除以25降低所需的位数。如果做得好,这也将大大提高速度。

但最终你的公式仍然需要永远收敛到e!更不用说由于阶乘造成的溢出。我正在使用更适合计算机的东西(虽然它是很久以前的,但不确定公式的来源)......

e = (1+1/x)^x

这在x -> +inf使用数字的二进制表示时有很多优点。2我通过使用for which 简化了很多事情的权力来改进这个过程x......我的计算代码(基于我的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

是的reference前 100 位数字e。这my e是此代码method 0的结果,是您的方程在任意精度下进行 200 次迭代后的结果,最后一个零截断并进行了%2,%5优化以加快速度。

我的方法只用了几毫秒,而你的方法只用了几毫秒~20就达到了这一点......

于 2016-06-26T12:01:55.967 回答
0

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

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

然后证明您的公式确实计算了欧拉数。

但是您需要在 (3k) 中包含因子 3!在阶乘计算中。

使用浮点类型进行阶乘计算应该是非常好的,因为在溢出发生之前很久就应该达到所需的精度。请注意,错误界限是下一项的两倍,而不是部分和。

于 2016-06-26T08:29:42.680 回答