我试图使用这两种方法在C#中解决这个问题:
public double NormalPowerMethod(double x, double toPower)
{
return Math.Pow(x, toPower);
}
public double EfficientPowerMethod(double x, double toPower)
{
return Math.Exp(Math.Log(x) * toPower);
}
EfficientPower ep = new EfficientPower();
Console.WriteLine("Enter x:");
double x = Double.Parse(Console.ReadLine());
Console.WriteLine("Enter power: ");
int toPower = Int32.Parse(Console.ReadLine());
Stopwatch timer = new Stopwatch();
timer.Start();
Console.WriteLine(string.Format("{0} to the power of {1}= {2}", x,toPower, ep.NormalPowerMethod(x,toPower)));
timer.Stop();
Console.WriteLine("Normal power time =" + timer.ElapsedTicks);
//-------------- efficient
timer.Reset();
timer.Start();
Console.WriteLine(string.Format("Efficient power: {0} to the power of {1}= {2}", x, toPower, ep.EfficientPowerMethod(x, toPower)));
timer.Stop();
Console.WriteLine("Efficient power time =" + timer.ElapsedTicks);
Console.ReadLine();
我注意到了:
Math.pow(x,y)
比Math.Exp(Math.log(x) * y)
例如:
Enter x:
15
Enter power:
26
Normal power: 15 to the power of 26= 3.78767524410635E+30
Normal power time =818
Efficient power: 15 to the power of 26= 3.78767524410634E+30
Efficient power time =533
Enter x:
1024
Enter power:
1024
Normal power: 1024 to the power of 1024= Infinity
Normal power time =810
Efficient power: 1024 to the power of 1024= Infinity
Efficient power time =519
为什么会出现这种行为?我认为更多的计算会更慢?
答案 0 :(得分:2)
这是正确的测量脚本,因为我们已经讨论过您的测量结果:
static void Main(string[] args)
{
DateTime start = DateTime.Now;
for (int i = 0; i < 10000000; i++)
{
Math.Pow(10, 100);
}
TimeSpan diff = DateTime.Now - start;
Console.WriteLine("Normal: {0:N0}", diff.TotalMilliseconds);
//-------------- efficient
start = DateTime.Now;
for (int i = 0; i < 10000000; i++)
{
Math.Exp(Math.Log(10) * 100);
}
diff = DateTime.Now - start;
Console.WriteLine("Efficient: {0:N0}", diff.TotalMilliseconds);
Console.ReadLine();
}
输出尝试10.000.000次(经过多次尝试后统计数据保持一致):
Normal: 1.968 ms.
Efficient: 1.813 ms.
所以'高效'的表现要好8%左右。
关于原因:
根据Hans Passants的回答:
它基本上检查了极端情况,然后调用CRT版本的pow()。
所以有一些,虽然是轻微的开销。
答案 1 :(得分:2)
Math.Pow
必须执行一些额外检查,以便在x < 0
和toPower
为整数时返回正确的值。
答案 2 :(得分:2)
汉斯在这里的回答(How is Math.Pow() implemented in .NET Framework?)解释说Math.Pow
没有按Exp
和Log
实施。 Math.Pow()
一旦执行了域验证,就会调用非托管CRT函数pow()
。我认为,这不是那样做的,因为CRT pow()
比Exp/Log
表达式更准确。
因此,Math.Pow
比Exp/Log
变体慢的原因是前者执行的计算时间更长。并且实现者选择了不同的计算,因为它比Exp/Log
版本更准确。从本质上讲,他们更倾向于提高效率。