【问题标题】:Sorted Dictionary customize key look-up for efficiency排序字典自定义键查找以提高效率
【发布时间】:2020-12-06 07:24:01
【问题描述】:

最近,我了解到Sorted Dictionary 类实现了对键的二进制搜索,我想利用它来发挥我的优势。我正在制作一个piecewise 线性函数类,它表示一系列间隔上的线条集合。

我这样定义了一个Interval 类:

public class Interval : ICloneable, IComparable, IComparable<Interval>
{
    public Interval()
    {

    }
    public Interval(double start, double end)
    {
        Start = start;
        End = end;
    }

    // Properties
    public double Start { get; set; } = double.NaN;
    public double End { get; set; } = double.NaN;
    public double Span => End - Start;

    // Methods
    public object Clone() => MemberwiseClone();

    public int CompareTo(object obj)
    {
        return Start.CompareTo(obj);
    }

    public int CompareTo([AllowNull] Interval other)
    {
        if (Start < other.Start)
        {
            return -1;
        }
        else if (Start > other.Start)
        {
            return 1;
        }
        else
        {
            return 0;
        }
    }

    public bool Contains(double x) => Start <= x && x <= End;
    public override string ToString() => $"[{Start}, {End}]";
}

而有问题的SortedDictionary 在分段函数类中的工作方式如下:

public class PiecewiseLinearFunction : ICloneable
{
    ...
    // The dictionary
    public SortedDictionary<Interval, Line2D> Functions { get; set; } = new SortedDictionary<Interval, Line2D>(); // Where Line2D is just a class that contains a function definition for a line

    // Methods
    public Interval FindInterval(double x)
        => Functions.Keys.Where(interval => interval.Contains(x)).FirstOrDefault();
    public double Solve(double x)
    {
        var interval = FindInterval(x);
        if (interval != null && Functions.ContainsKey(interval))
        {
            return Functions[interval].Solve(x);
        }
        else
        {
            return double.NaN;
        }
    }
}

如您所见,PiecewiseLinearFunction.FindInterval(double x) 方法线性搜索字典的键,以便找到包含(或不包含)x 的区间,可用于二进制查找,但这显然完全违背了进行二进制查找的目的。

我想知道是否可以以某种方式让字典查找 double x 值,并在检查 Interval.Contains(double x)truefalse 的同时对区间进行二进制搜索以确定是否存在有效区间(key)和对应的行,可用于获取x处的函数值。

换句话说,有没有办法使用谓词进行搜索,例如FindInterval(double x) =&gt; Functions.Keys.FindBinary(i =&gt; i.Contains(x))

【问题讨论】:

  • 你会面临双精度值的损失,应该使用某种公差
  • 我同意,在完整的代码中还有很多改进之处,但我现在主要关心的是查找。
  • 我不认为SortedDictionary 在内部使用二叉树这一事实对您有帮助。该类的所有使用者都知道字典键是排序的。您想对数据执行二进制搜索,但SortedDictionary 不提供对数据的索引访问,因此不适合二进制搜索。
  • 所以你的意思是我最好编写一个本身包含间隔和行的类,然后为该类的集合编写自定义二进制搜索?
  • 如果间隔可以重叠,我需要一个封装的数据结构来跟踪额外信息以支持二进制搜索,仅自定义间隔类和二进制搜索是不够的。

标签: c# dictionary binary-search-tree


【解决方案1】:

因此,如果间隔不重叠,则实际上不需要自定义二进制搜索。

首先,如果我们将Intervaldouble 相媲美会有所帮助:

public class Interval: IComparable<double>
{
    public double Start { get; set; }
    public double End { get; set; }

    public bool Contains(double x) => x >= Start && x <= End;
    public int CompareTo(double other)
    {
        if (Contains(other))
            return 0;
        return other < Start ? 1 : -1;
    }
}

现在我们创建了一个List 扩展,这样我们就可以对其他类型进行二分搜索,而不仅仅是列表的类型:

public static class ListExtensions
{
    public static int BinarySearch<T, TU>(this List<T> sortedSource, TU val) where T : IComparable<TU>
    {
        return sortedSource.BinarySearch(default(T), Comparer<T>.Create((item, _) => item.CompareTo(val)));
    }
}

现在我们可以像这样测试它:

var list = new List<Interval>
{
    new Interval
    {
        Start = 0,
        End = 4
    },
    new Interval
    {
        Start = 6,
        End = 7
    },
    new Interval
    {
        Start = 10,
        End = 12
    }
};

var index = list.BinarySearch(6.0d); //index = 1
var interval = index < 0 ? null : list[index]; //interval = [6, 7]

index = list.BinarySearch(9.0d); //index = -3
interval = index < 0 ? null : list[index]; //interval = null

【讨论】:

  • 哦,我的错,我刚刚注意到你发布了一个答案,让我快点试一试!
  • 所以如果我使用这个实现,我还不如使用常规的Dictionary&lt;Interval, Line2D&gt;,因为这样查找速度要快得多,对吧?我目前这样做的方式将具有O(n log n) 的复杂性,而将二进制搜索与常规字典结合起来将具有接近O(log n) 的复杂性?
  • @JansthcirlU 这是正确的,在列表中进行二进制搜索,然后在字典中进行查找组合将是 O(log n)。也许将整个事情包装在一个自定义数据结构中,该数据结构同时包含已排序的 List&lt;Interval&gt;Dictionary&lt;Interval, Line2D&gt; 以集中添加、删除等。然后您还可以使用 BinarySearch 在已排序的 List&lt;Interval&gt; 中优化插入。
猜你喜欢
  • 2020-04-21
  • 1970-01-01
  • 1970-01-01
  • 2021-12-11
  • 2012-08-15
  • 2010-10-26
  • 1970-01-01
  • 2017-06-08
  • 2016-02-29
相关资源
最近更新 更多