web-dev-qa-db-ja.com

時間要素の配列の重複しないセグメント間のギャップを削除します

要素の配列があり、それぞれに開始時刻と終了時刻があります。

これらはタイムラインを形成し、一部は重複し、一部は重複しません(重複する要素のグループ間にギャップがあります)。

重複しない要素間のギャップ(下の画像のように赤い線)を削除するコードをいくつか記述しましたが、乱雑になり始め、Edgeのケースをキャッチできなくなりました。

red gaps

赤いギャップを取り除き、すべてをきれいにシフトするアルゴリズムはすでに存在しますか?

私のコードスタブは以下です:

// sort by time, lowest to highest
entries.Sort((a, b) => a.TimeOffset.CompareTo(b.TimeOffset));

var lookingForFirstGap = true;
var storedOffset = 0.0;
var indexToTrimFrom = 0;

for (var i = 0; i < entries.Count -1; i++ )
{
    // var previous = entries[i-1];
    var current = entries[i];
    var next = entries[i+1];

    // var distanceBack = previous.TimingsAbsolute.End - current.TimingsAbsolute.Start;
    var distanceForward = next.TimingsAbsolute.Start - current.TimingsAbsolute.End;

    Console.WriteLine("Distance {0} -> {1} is {2}", i, i+1, distanceForward);

    if (!(distanceForward > 0))
        continue;

    if (lookingForFirstGap)
    {
        indexToTrimFrom = i + 1;
        Console.WriteLine("To trim from " + indexToTrimFrom);
        storedOffset = distanceForward; // we have found a gap, store the amount
        lookingForFirstGap = false; // now start looking for element where there is a gap after it
    }
    else
    {
        var indexToTrimTo = i;

        for (var x = indexToTrimFrom; x <= indexToTrimTo; x++)
        {
            // trim all
            Console.WriteLine("Adjust [" + x + "] by " + storedOffset);
        }

        if (distanceForward > 0)
        {
            indexToTrimFrom = i + 1;
            Console.WriteLine("To trim from " + indexToTrimFrom);
            storedOffset = distanceForward; // we have found a gap, store the amount
        }
        else
            lookingForFirstGap = true; // start looking for gap again
    }
}
3
g18c

1つのループでやりすぎている。ギャップを特定することは難しいため、ギャップを差し引くことは困難です。間隔がオーバーラップするすべての奇妙な方法のため、ギャップを特定することは困難です。したがって、最初にオーバーラップする間隔の問題を削除します。

この質問 のアルゴリズムを使用して、1つのパスを作成して間隔を平坦化します。次に、1つのパスでギャップのリストを作成します。これは、フラット化されたリストを使用すると非常に簡単になるはずです。

これで間隔の元のリストをループする準備が整いました。各間隔について、その前に発生するギャップの数を確認し、それぞれの累積時間を差し引きます。

2
Karl Bielefeldt

これをコードとして投稿することで、@ Karlはこれを分割することで簡単になったことを実感できました。 SOLID原則により、コードがはるかに簡単になります。

List<Tuple<int, int>> FindGaps(List<LogEntry> entries)
{
    var gaps = new List<Tuple<int, int>>();

    for (var i = 0; i < entries.Count - 1; i++)
    {
        var current = entries[i];
        var next = entries[i + 1];

        var distanceForward = next.TimingsAbsolute.Start - current.TimingsAbsolute.End;

        if (!(distanceForward > 0)) // there is no gap to the next element, continue
            continue;

        gaps.Add(new Tuple<int, int>(i, i+1)); // otherwise, we have found a gap...
    }

    return gaps;
}

List<Tuple<int, int>> FindContiguous(List<Tuple<int, int>> gaps, int totalLength)
{
    var previous = 0;
    var lastIndex = totalLength - 1;

    var contiguous = new List<Tuple<int, int>>();

    foreach (var gap in gaps)
    {
        contiguous.Add(new Tuple<int, int>(previous,gap.Item1));

        previous = gap.Item2;
    }

    if (previous != lastIndex)
    {
        contiguous.Add(new Tuple<int, int>(previous,lastIndex));
    }

    return contiguous;
}


List<LogEntry> RemoveGaps(List<LogEntry> entries, List<Tuple<int, int>> contiguous)
{
    var newList = new List<LogEntry>();

    var totalShift = 0.0;
    LogEntry previousLast = null;

    foreach (var seq in contiguous)
    {
        var startIndex = seq.Item1;
        var endIndex = seq.Item2;

        if (previousLast != null)
        {
            totalShift += entries[startIndex].TimingsAbsolute.Start - previousLast.TimingsAbsolute.End;
        }

        previousLast = entries[endIndex];

        for (var i = startIndex; i <= endIndex; i++)
        {
            var entry = entries[i];

            newList.Add(
                new LogEntry(
                    entry.Url,
                    entry.Status,
                    entry.HeaderSize,
                    entry.PayloadSize,
                    entry.TimeOffset-totalShift,
                    entry.Timings.Blocked,
                    entry.Timings.Dns,
                    entry.Timings.Connect,
                    entry.Timings.Ssl,
                    entry.Timings.Send,
                    entry.Timings.Wait,
                    entry.Timings.Receive,
                    entry.ConnectionId));    
        }
    }

    return newList;
}

public void Generate(List<LogEntry> entries, Guid testId)
{
    // sort by time, lowest to highest
    entries.Sort((a, b) => a.TimeOffset.CompareTo(b.TimeOffset));

    // get a list of contigous elements
    var contiguous = FindContiguous(FindGaps(entries), entries.Count);

    // now remove the gaps between the contiguous sequences of elements 
    var list = RemoveGaps(entries, contiguous);
}
1
g18c