我需要一种专门的字典。我的用例是这样的:用户想要指定值的范围(范围也可以是单个点)并为特定范围分配值。然后,我们想要使用单个值作为键来执行查找。如果此单个值出现在其中一个范围内,那么我们将返回与该范围相关联的值。
例如:
// represents the keyed value
struct Interval
{
public int Min;
public int Max;
}
// some code elsewhere in the program
var dictionary = new Dictionary<Interval, double>();
dictionary.Add(new Interval { Min = 0, Max = 10 }, 9.0);
var result = dictionary[1];
if (result == 9.0) JumpForJoy();
这显然只是一些代码来说明我正在寻找的东西。有谁知道实现这样的事情的算法?如果是这样,他们会指出我吗?
我已经尝试在Interval上实现自定义IEqualityComparer对象并重载Equals()和GetHashCode()但到目前为止无济于事。可能是我做错了。
答案 0 :(得分:26)
字典不是您所描述的操作的适当数据结构。
如果要求间隔永远不会重叠,那么您只需构建一个有序间隔列表并对其进行二分搜索。
如果间隔可能重叠,那么您需要解决更难的问题。要有效地解决这个问题,你需要构建一个区间树:
http://en.wikipedia.org/wiki/Interval_tree
这是一个众所周知的数据结构。请参阅“算法简介”或任何其他关于数据结构的体面本科文本。
答案 1 :(得分:6)
仅当间隔不重叠时才会起作用。而你的主要问题似乎是从单个(键)值转换为间隔。
我会在SortedList周围写一个包装器。 SortedList.Keys.IndexOf()会找到一个索引,可用于验证间隔是否有效,然后使用它。
答案 2 :(得分:3)
这不是你想要的,但我认为它可能是你最接近的。
你当然可以比这更好(我早点喝酒了吗?)。但你必须承认它很简单。
var map = new Dictionary<Func<double, bool>, double>()
{
{ d => d >= 0.0 && d <= 10.0, 9.0 }
};
var key = map.Keys.Single(test => test(1.0))
var value = map[key];
答案 3 :(得分:1)
我已经通过确保集合是连续的来解决了类似的问题,其中间隔从不重叠并且它们之间从不存在间隙。每个间隔被定义为下边界,并且如果它等于或大于该边界并且小于下一个间隔的下边界,则任何值都在该间隔中。低于最低边界的任何东西都是特殊情况箱。
这有点简化了问题。然后,我们还通过实现二进制斩波来优化密钥搜索。不幸的是,我不能分享这些代码。
答案 4 :(得分:0)
我会做一个小的Interval类,就像那样:
public class Interval
{
public int Start {get; set;}
public int End {get; set;}
public int Step {get; set;}
public double Value {get; set;}
public WriteToDictionary(Dictionary<int, double> dict)
{
for(int i = Start; i < End; i += Step)
{
dict.Add(i, Value);
}
}
}
所以你仍然可以在字典中进行正常查找。也许您还应该在调用Add()
之前执行一些检查,或者如果字典中已有任何值,则执行某种回滚。
答案 5 :(得分:0)
您可以在Java flavored中找到区间树的Open Geospatial Library C#实现。它需要一些小的调整来解决你的问题,它也可以真正使用一些C#-ification。
它是开源的,但我不知道在什么许可下。
答案 6 :(得分:0)
我为字典和 func 修改了一些构想,例如“ ChaosPandion”在上面的早些时候的帖子中给了我构想。
我仍然解决了编码问题,但是如果我尝试重构
我有一个令人惊讶的问题/错误/缺乏理解:
Dictionary<Func<string, double, bool>, double> map = new Dictionary<Func<string, double, bool>, double>()
{
{ (a, b) => a == "2018" && b == 4, 815.72},
{ (a, b) => a == "2018" && b == 6, 715.72}
};
这是什么,我用“ 2018”(年)和4(月)之类的搜索来调用地图,结果是双倍值815,72。 当我检查唯一的地图条目时,它们看起来像这样:
所以这就是原始的行为,到目前为止还不错。 然后我尝试将其重构为:
Dictionary<Func<string, double, bool>, double> map =
new Dictionary<Func<string, double, bool>, double>();
WS22(map, values2018, "2018");
private void WS22(Dictionary<Func<string, double, bool>, double> map, double[] valuesByYear, string strYear)
{
int iMonth = 1;
// step by step this works:
map.Add((a, b) => (a == strYear) && (b == 1), dValue);
map.Add((a, b) => (a == strYear) && (b == 2), dValue);
// do it more elegant...
foreach (double dValue in valuesByYear)
{
//this does not work: exception after second iteration of foreach run
map.Add((a, b) => (a == strYear) && (b == iMonth), dValue );
iMonth+=1;
}
}
this works: (i use b==1 and b==2)
this does not work (map not working exception on add item on second iteration)
所以我认为问题是,地图添加到地图字典时没有唯一键。问题是,我没有看到我的错误,为什么b == 1可以正常工作,而b == iMonth却不能正常工作。
谢谢你的帮助,打开我的眼睛:)
答案 7 :(得分:-2)
您可以查看codeplex上找到的有关可以执行所需内容的集合的powercollections。
希望这有帮助, 最好的祝福, 汤姆。