我想找到数据的几何平均值,而性能确实很重要 我应该选择哪一个
将乘法保持在单个变量上,并在计算结束时取Nth-root
X = MUL(x[i])^(1/N)
因此,O(N) x Multiplication Complexity + O(1) x Nth-root
使用对数
X = e ^ { 1/N * SUM(log(x[i])) }
因此,O(N) x Logarithm Complexity + O(1) x Nth-division + O(1) Exponential
几何平均的专业算法。请告诉我是否有。
答案 0 :(得分:1)
我以为我会尝试对此进行基准测试并进行比较,这是我的尝试。
比较很困难,因为数字列表需要足够大才能使时间合理,所以N很大。在我的测试中,N = 50,000,000个元素。
然而,将大量数字乘以大于1的数字会溢出存储产品的双倍数。但是,将小于1的数字相乘得到的总产品非常小,除以元素数就得零。
还有几件事:确保你的元素都不为零,而Log方法对负面元素不起作用。
(如果C#有一个带有第N个根函数的BigDecimal类,乘法就会没有溢出。)
无论如何,在我的代码中,每个元素都介于1和1.00001之间
另一方面,日志方法没有溢出或下溢的问题。
以下是代码:
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Starting...");
Console.WriteLine("");
Stopwatch watch1 = new Stopwatch();
Stopwatch watch2 = new Stopwatch();
List<double> list = getList();
double prod = 1;
double mean1 = -1;
for (int c = 0; c < 2; c++)
{
watch1.Reset();
watch1.Start();
prod = 1;
foreach (double d in list)
{
prod *= d;
}
mean1 = Math.Pow(prod, 1.0 / (double)list.Count);
watch1.Stop();
}
double mean2 = -1;
for (int c = 0; c < 2; c++)
{
watch2.Reset();
watch2.Start();
double sum = 0;
foreach (double d in list)
{
double logged = Math.Log(d, 2);
sum += logged;
}
sum *= 1.0 / (double)list.Count;
mean2 = Math.Pow(2.0, sum);
watch2.Stop();
}
Console.WriteLine("First way gave: " + mean1);
Console.WriteLine("Other way gave: " + mean2);
Console.WriteLine("First way took: " + watch1.ElapsedMilliseconds + " milliseconds.");
Console.WriteLine("Other way took: " + watch2.ElapsedMilliseconds + " milliseconds.");
Console.WriteLine("");
Console.WriteLine("Press enter to exit");
Console.ReadLine();
}
private static List<double> getList()
{
List<double> result = new List<double>();
Random rand = new Random();
for (int i = 0; i < 50000000; i++)
{
result.Add( rand.NextDouble() / 100000.0 + 1);
}
return result;
}
}
我的计算机输出描述两种几何平均值相同,但是:
Multiply way took: 466 milliseconds
Logarithm way took: 3245 milliseconds
因此,乘法似乎更快。
但是,溢出和下溢的问题很多,所以我建议使用Log方法,除非你能保证产品不会溢出并且产品不会太接近零。