我想用多个线程计算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个)开始程序时,我得到不同的结果我不知道我错过了什么..
答案 0 :(得分:0)
如果允许您在实施前转换条款,请考虑
(3k)^2/(3k)! = (3k)/(3k-1)! = 1/(3k-2)!+1/(3k-1)!
然后证明你的公式真的计算了欧拉数。
但你需要在(3k)中加入因子3!在阶乘计算中。
使用浮点类型进行因子计算应该是完全正确的,因为在溢出发生之前很久就要达到所需的精度。请注意,错误界限是下一个术语的两倍而不是部分总和。
答案 1 :(得分:0)
由于这个问题的要点已经回答,我只想补充一些内容:
你可以对你的代码进行一些升级,就像计算阶乘一样,每次你可以将divident和divider除以2
或5
来降低所需的位数。如果做得好,这也会提高速度。
但最终你的公式仍将永远收敛到e
!更不用说由于阶乘造成的溢出。我正在使用更适合电脑的东西(不知道配方来自哪里,虽然很久以前)......
e = (1+1/x)^x
其中x -> +inf
当使用数字的二进制表示时,这有许多优点。我使用2
对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
是e
的前100位数字。 my e
是此代码的结果,而method 0
是您的等式产生的,经过200次迭代后,以任意精度进行最后零截断,%2,%5
优化以加快速度。
我的方法花了几毫秒而你的~20
秒才到达那一点......