2013-05-06 7 views
1

과 Hellinger의 거리를 표현하는 방법은 Linq에 다음과 같은 공식을 표현하려는는 Linq에

Hellinger distance formula 나는 Frequency 다음과 같은 기능

private double Calc(IEnumerable<Frequency> recording, IEnumerable<Frequency> reading) 
{ 
} 

입니다 어디로 :

public class Frequency 
{ 
    public double Probability { get; set; } //which are p's and q's in the formula 
    public int Strength { get; set; } //the i's i the formula 
} 

함수 호출의 예는입니다. 예

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); 
} 

new Frequency {Strength = 32, Probability = 0.2},는 Hellinger 수식이 p32 = 0.2을 의미한다.

k은 수식에서 100이됩니다. 요소가 컬렉션에 없으면 값은 0입니다. 예를 들어 i = 32,33,34,35,41에 대한 값만 가지므로 다른 값 1 ~ 100 파이에서 0이됩니다.

내 첫 번째 구현은 효율적도 우아하지도

private double Calc(IEnumerable<Frequency> recording, IEnumerable<Frequency> reading) 
    { 
    double result = 0; 

    foreach (var i in Enumerable.Range(1,100)) 
    { 
     var recStr = recording.FirstOrDefault(a => a.Strength == i); 
     var readStr = reading.FirstOrDefault(a => a.Strength == i); 
     var recVal = recStr == null ? 0 : recStr.Probability; 
     var readVal = readStr == null ? 0 : readStr.Probability; 

     result += Math.Pow(Math.Sqrt(recVal) - Math.Sqrt(readVal), 2); 
    } 

    result = Math.Sqrt(result/2); 
    return result; 
    } 

입니다. 나는 해결책이 향상 될 수 있다고 느낀다. 그러나 나는 더 좋은 길을 생각할 수 없었다.

+0

아직 LINQ 표현식을 배우고 있으며, 즉석에서 통찰력을 제공하는 좋은 방법은 Resharper VS 확장 (30 일 평가판을 사용할 수 있음)을 사용하는 것입니다. 이 도구는 LINQ 표현식을 사용하여 이익을 얻을 수 있거나 이미 수행 한 진술에 대해보다 간결한 대안을 제공하지만 가능한 한 우아하게 그렇게하지는 않습니다. 이 미래에 대한 생각뿐입니다! –

+4

강도에서 빈도로 해시 맵을 만든 다음 O (n) 목록 대신 O (1) 키 조회를 사용하여 강도 검사를 수행 할 수 있습니다. – Patashu

+0

우아함은 보는 사람의 눈에는 있지만 현재 코드는 유지 보수가 가능해 보입니다. LINQ는 훌륭하지만 코드 가독성 및 유지 관리 측면에서 오용 될 수 있습니다. 루프를 사용할 때 아무 문제가 없습니다! – joshuahealy

답변

1

이 질문은 사실에 의해 복잡 리스트가 희박하다는 것 (우리는 모든 수치에 대해 확률이 없음). 주파수 측정의 조밀 한 배열을 우리에게 잎

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; 
} 

: 그래서, 우리는 먼저 그 문제를 해결한다. 이제 우리는 공식을 적용해야합니다

// 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(); 

이 있지만, 당신이 가진 것보다 더 성능이 좋은 것입니다 확실하지.

1

ReSharper에서이로 기능을 전환 : Patashu 말했듯이

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); 

, 당신은 (1) 조회 O를 얻을 수있는 Dictionary<int, Frequency>를 사용할 수있는 시간 :

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); 
}