C# 如何用Linq表示Hellinger距离

C# 如何用Linq表示Hellinger距离,c#,linq,C#,Linq,我想用Linq表示下面的公式 double result = (from i in Enumerable.Range(1, 100) let recStr = recording.FirstOrDefault(a => a.Strength == i) let readStr = reading.FirstOrDefault(a => a.Strength == i) let

我想用Linq表示下面的公式

double result = (from i in Enumerable.Range(1, 100) 
                 let recStr = recording.FirstOrDefault(a => a.Strength == i) 
                 let readStr = reading.FirstOrDefault(a => a.Strength == i) 
                 let recVal = recStr == null ? 0 : recStr.Probability 
                 let readVal = readStr == null ? 0 : readStr.Probability 
                 select Math.Pow(Math.Sqrt(recVal) - Math.Sqrt(readVal), 2)).Sum();


return Math.Sqrt(result / 2);

double result = (from i in Enumerable.Range(1, 100) 
                 let recStr = recording.FirstOrDefault(a => a.Strength == i) 
                 let readStr = reading.FirstOrDefault(a => a.Strength == i) 
                 let recVal = recStr == null ? 0 : recStr.Probability 
                 let readVal = readStr == null ? 0 : readStr.Probability 
                 select Math.Pow(Math.Sqrt(recVal) - Math.Sqrt(readVal), 2)).Sum();


return Math.Sqrt(result / 2);
我有以下功能

private double Calc(IEnumerable<Frequency> recording, IEnumerable<Frequency> reading)
{
}
double result = (from i in Enumerable.Range(1, 100) 
                 let recStr = recording.FirstOrDefault(a => a.Strength == i) 
                 let readStr = reading.FirstOrDefault(a => a.Strength == i) 
                 let recVal = recStr == null ? 0 : recStr.Probability 
                 let readVal = readStr == null ? 0 : readStr.Probability 
                 select Math.Pow(Math.Sqrt(recVal) - Math.Sqrt(readVal), 2)).Sum();


return Math.Sqrt(result / 2);
对该函数的调用示例如下

public void Caller(){
   IEnumerable<Frequency> recording = new List<Frequency>
                                            {
                                               new Frequency {Strength = 32, Probability = 0.2}, //p32 = 0.2
                                               new Frequency {Strength = 33, Probability = 0.2}, //p33 = 0.2
                                               new Frequency {Strength = 34, Probability = 0.2}, //p34 = 0.2
                                               new Frequency {Strength = 35, Probability = 0.2}, //...
                                               new Frequency {Strength = 41, Probability = 0.2} //...
                                            };

   IEnumerable<Frequency> reading = new List<Frequency>
                                            {
                                               new Frequency {Strength = 34, Probability = 0.2}, //q34 = 0.2
                                               new Frequency {Strength = 35, Probability = 0.2},  //q35 = 0.2
                                               new Frequency {Strength = 36, Probability = 0.2},
                                               new Frequency {Strength = 37, Probability = 0.2},
                                               new Frequency {Strength = 80, Probability = 0.2},
                                            };
   Calc(reading, recordig);
}
double result = (from i in Enumerable.Range(1, 100) 
                 let recStr = recording.FirstOrDefault(a => a.Strength == i) 
                 let readStr = reading.FirstOrDefault(a => a.Strength == i) 
                 let recVal = recStr == null ? 0 : recStr.Probability 
                 let readVal = readStr == null ? 0 : readStr.Probability 
                 select Math.Pow(Math.Sqrt(recVal) - Math.Sqrt(readVal), 2)).Sum();


return Math.Sqrt(result / 2);

既不高效也不优雅。我觉得解决方案可以改进,但我想不出更好的方法。

Resharper将您的功能转化为:

double result = (from i in Enumerable.Range(1, 100) 
                 let recStr = recording.FirstOrDefault(a => a.Strength == i) 
                 let readStr = reading.FirstOrDefault(a => a.Strength == i) 
                 let recVal = recStr == null ? 0 : recStr.Probability 
                 let readVal = readStr == null ? 0 : readStr.Probability 
                 select Math.Pow(Math.Sqrt(recVal) - Math.Sqrt(readVal), 2)).Sum();


return Math.Sqrt(result / 2);
正如Patashu所说,您可以使用
字典
来获得O(1)查找时间:

double result = (from i in Enumerable.Range(1, 100) 
                 let recStr = recording.FirstOrDefault(a => a.Strength == i) 
                 let readStr = reading.FirstOrDefault(a => a.Strength == i) 
                 let recVal = recStr == null ? 0 : recStr.Probability 
                 let readVal = readStr == null ? 0 : readStr.Probability 
                 select Math.Pow(Math.Sqrt(recVal) - Math.Sqrt(readVal), 2)).Sum();


return Math.Sqrt(result / 2);
private double Calc(Dictionary<int, Frequency> recording, Dictionary<int, Frequency> reading)
{
    double result = (from i in Enumerable.Range(1, 100) 
                     let recVal = recording.ContainsKey(i) ? 0 : recording[i].Probability 
                     let readVal = reading.ContainsKey(i) ? 0 : reading[i].Probability 
                     select Math.Pow(Math.Sqrt(recVal) - Math.Sqrt(readVal), 2)).Sum();

    return Math.Sqrt(result / 2);
}
private double Calc(字典记录、字典阅读)
{
double result=(从可枚举范围(1100)中的i开始)
让recVal=recording.ContainsKey(i)?0:recording[i]。概率
设readVal=reading.ContainsKey(i)?0:reading[i]。概率
选择Math.Pow(Math.Sqrt(recVal)-Math.Sqrt(readVal),2)).Sum();
返回数学Sqrt(结果/2);
}

由于列表稀疏(我们没有所有读数的概率),这个问题变得复杂。因此,首先我们要解决这个问题:

double result = (from i in Enumerable.Range(1, 100) 
                 let recStr = recording.FirstOrDefault(a => a.Strength == i) 
                 let readStr = reading.FirstOrDefault(a => a.Strength == i) 
                 let recVal = recStr == null ? 0 : recStr.Probability 
                 let readVal = readStr == null ? 0 : readStr.Probability 
                 select Math.Pow(Math.Sqrt(recVal) - Math.Sqrt(readVal), 2)).Sum();


return Math.Sqrt(result / 2);
public static IEnumerable<Frequency> FillHoles(this IEnumerable<Frequency> src, int start, int end) {
    IEnumerable<int> range = Enumerable.Range(start, end-start+1);
    var result = from num in range
                 join _freq in src on num equals _freq.Strength into g
                 from freq in g.DefaultIfEmpty(new Frequency { Strength = num, Probability = 0 })
                 select freq;
    return result;
}
公共静态IEnumerable填充孔(此IEnumerable src,int start,int end){
IEnumerable range=可枚举范围(开始、结束开始+1);
var结果=范围内的从数值
将_freqin src on num equals _freq.Strength连接到g中
从g.DefaultIfEmpty中的freq(新频率{Strength=num,Probability=0})
选择频率;
返回结果;
}
这给我们留下了密集的频率读数阵列。现在我们只需要应用以下公式:

double result = (from i in Enumerable.Range(1, 100) 
                 let recStr = recording.FirstOrDefault(a => a.Strength == i) 
                 let readStr = reading.FirstOrDefault(a => a.Strength == i) 
                 let recVal = recStr == null ? 0 : recStr.Probability 
                 let readVal = readStr == null ? 0 : readStr.Probability 
                 select Math.Pow(Math.Sqrt(recVal) - Math.Sqrt(readVal), 2)).Sum();


return Math.Sqrt(result / 2);
// Make the arrays dense
recording = recording.FillHoles(1, 100);
reading = reading.FillHoles(1, 100);
// This is the thing we will be summing
IEnumerable<double> series = from rec in recording
                            join read in reading on rec.Strength equals read.Strength
                            select Math.Pow(Math.Sqrt(rec.Probability)-Math.Sqrt(read.Probability), 2);

double result = 1 / Math.Sqrt(2) * Math.Sqrt(series.Sum());
result.Dump();
//使数组密集
记录=记录。填充孔(1100);
读数=读数。填充孔(1100);
//这就是我们要总结的东西
IEnumerable series=从记录中的记录
加入阅读练习,阅读强度等于阅读强度
选择Math.Pow(Math.Sqrt(rec.Probability)-Math.Sqrt(read.Probability),2);
double result=1/Math.Sqrt(2)*Math.Sqrt(series.Sum());
result.Dump();

不过,我不确定这是否比您现有的性能更好。

我仍在学习LINQ表达式,一个很好的方法是使用Resharper VS扩展(您可以进行30天的试用)。这个工具提供了更简洁的语句替代方案,我可以从使用LINQ表达式中受益,也可以已经受益,但不要尽可能优雅地这样做。这只是一个想法和未来的努力!也许做一个从强度到频率的散列映射,然后你有O(1)个键查找,而不是O(n)个列表扫描来查找强度。优雅在旁观者的眼里,但你当前的代码看起来是可维护的,我认为这更重要。LINQ很棒,但在代码可读性和可维护性方面可能会被误用。使用循环没有什么错!
double result = (from i in Enumerable.Range(1, 100) 
                 let recStr = recording.FirstOrDefault(a => a.Strength == i) 
                 let readStr = reading.FirstOrDefault(a => a.Strength == i) 
                 let recVal = recStr == null ? 0 : recStr.Probability 
                 let readVal = readStr == null ? 0 : readStr.Probability 
                 select Math.Pow(Math.Sqrt(recVal) - Math.Sqrt(readVal), 2)).Sum();


return Math.Sqrt(result / 2);