针对区间范围设计一个高效hash函数,使得一个区间里的数对应一个hash值

小敏纸 2014-06-23 01:42:14
假如有一群范围对格式为:<范围表示,该范围对应的结果值>,设计一个最快查找算法,使得给定一个值,输出该值所对应的范围对的结果值。
注意:范围对之间不会存在交集,也不是严格相邻,即两个区间可能不是相邻的。

例如:
<<1, 2>, 65>
<<3, 37>, 75>
<<45, 157>, 12>
<<160, 200>, 23>
<<210, 255>, 121>
……
如果给定一个数78,则输出12,因为78属于范围对<45, 159>

要求:不要用数组存储下标求值,因为范围对可能非常大。

针对这个问题,我目前的解法是利用二分查找,针对范围对的高效查找算法设计(不准用数组)

但还是不够快,我现在有个新的思路,就是:
能不能设计利用这些范围对设计一个hash函数,使得一个区间对应一个hash值,举个例子:
上面的第一对范围<1, 2>里的所有数即1和2都对应hash值1,<3, 37>里的所有值3~37都对应hash值2,以此类推……

这样类似的hash函数能设计出来吗?
...全文
934 34 打赏 收藏 转发到动态 举报
写回复
用AI写文章
34 条回复
切换为时间正序
请发表友善的回复…
发表回复
小敏纸 2014-06-30
  • 打赏
  • 举报
回复
引用 33 楼 u013823973 的回复:
[quote=引用 32 楼 lanxuezaipiao 的回复:] [quote=引用 30 楼 u013823973 的回复:] 上面的内存没有释放,没来得及写了
感谢提供代码,这也是二分查找,我在想有没有更高效的方法,嘿嘿[/quote] 二分的时间复杂度已经是logN了,难道还有更少的啊[/quote] 我也想不出更好的了,以为有呢,所以过来问问,看来是真的没有啦
难题 2014-06-30
  • 打赏
  • 举报
回复
引用 32 楼 lanxuezaipiao 的回复:
[quote=引用 30 楼 u013823973 的回复:] 上面的内存没有释放,没来得及写了
感谢提供代码,这也是二分查找,我在想有没有更高效的方法,嘿嘿[/quote] 二分的时间复杂度已经是logN了,难道还有更少的啊
小敏纸 2014-06-27
  • 打赏
  • 举报
回复
引用 30 楼 u013823973 的回复:
上面的内存没有释放,没来得及写了
感谢提供代码,这也是二分查找,我在想有没有更高效的方法,嘿嘿
mujiok2003 2014-06-27
  • 打赏
  • 举报
回复
引用
<<1, 2>, 65> <<3, 37>, 75> <<45, 157>, 12> <<160, 200>, 23> <<210, 255>, 121>
两个vector, 第一个vector存区间的首位[3,37,45,157,160,200, 210, 255],另一个vecor保存值[65, 75, 12,23,121] 使用std::lower_bound(二分查找)找到所属区间, 进而读取对应的值。
难题 2014-06-27
  • 打赏
  • 举报
回复
上面的内存没有释放,没来得及写了
难题 2014-06-27
  • 打赏
  • 举报
回复
下面是我写的,但是有几个问题没来得及解决,要查找的数据不在这些集合里面没有判断
class Interval
{
public:
	int m_min;
	int m_max;
	int m_hashdata;

	Interval()
	{
		m_min = -1;
		m_max = -1;
		m_hashdata = -1;
	}
	Interval(int min1,int max1,int hashdata):m_min(min1),m_max(max1),m_hashdata(hashdata){}
};

void BinarySearch(int begin,int end,int data,map<int,Interval*>::iterator &it)
{
	int Half = (begin + end)/2;
	int i = 0;
	if (data == it->first || (data > it->first && data < it->second->m_max))
	{
		return;
	}else if(data > it->first)
	{
		for (i = 0;i < Half;i++)
		{
			it++;
		}
	}else if(data < it->first)
	{
		for (i = 0;i < Half;i++)
		{
			it--;
		}
	}
	

	if (data == it->first || (data > it->first && data < it->second->m_max))
	{
		return;
	}else if(data > it->first)
	{
		BinarySearch(Half,end,data,it);
	}else if(data < it->first)
	{
		BinarySearch(begin,Half,data,it);
	}
}



int main()
{
	map<int,Interval*> rang_map;
	rang_map.insert(pair<int,Interval*>(1,new Interval(1,2,20)));
	rang_map.insert(pair<int,Interval*>(3,new Interval(3,37,75)));
	rang_map.insert(pair<int,Interval*>(45,new Interval(45,157,12)));
	rang_map.insert(pair<int,Interval*>(160,new Interval(160,200,23)));
	rang_map.insert(pair<int,Interval*>(210,new Interval(210,255,121)));
	rang_map.insert(pair<int,Interval*>(10000,new Interval(10000,20000,1)));
	rang_map.insert(pair<int,Interval*>(30000,new Interval(30000,40000,2)));
	rang_map.insert(pair<int,Interval*>(50000,new Interval(50000,60000,3)));
	rang_map.insert(pair<int,Interval*>(70000,new Interval(70000,80000,4)));
	rang_map.insert(pair<int,Interval*>(90000,new Interval(90000,100000,5)));

	size_t start,end;
	start = GetTickCount();
	for (int i = 0;i < 100000;i++)
	{
		map<int,Interval*>::iterator it = rang_map.begin();
		BinarySearch(0,rang_map.size(),78,it);
	}
	end = GetTickCount();
	cout << "时间=" << end - start << endl;

	return 0;
}
赵4老师 2014-06-27
  • 打赏
  • 举报
回复
无profiler不要谈效率!!尤其在这个云计算、虚拟机、模拟器、CUDA、多核 、多级cache、指令流水线、多种存储介质、……满天飞的时代!
小敏纸 2014-06-27
  • 打赏
  • 举报
回复
引用 25 楼 daiweifeng 的回复:
[quote=引用 24 楼 lanxuezaipiao 的回复:] [quote=引用 23 楼 daiweifeng 的回复:] [quote=引用 22 楼 daiweifeng 的回复:] [quote=引用 20 楼 lanxuezaipiao 的回复:] [quote=引用 12 楼 daiweifeng 的回复:] [quote=引用 11 楼 daiweifeng 的回复:] 搞个随机数池,然后做一些简单的移位异或即可,这样最省事保险
无论多大操作都类似。[/quote] 这个这个,没看懂,具体怎么做?能否提示下哈,谢谢啦[/quote] 给定散列容器大小MAX,如果存在一个数组int a[MAX]是一个无重复的随机数整数数组,那么对所有的32bit数据x都可以如下处置: h = x ^ a[x%MAX] % MAX; // h表示散列值 此外还可以在x%MAX之前对x进行若干次移位+位异或运算,如y=x^(x<<3)^(x<<7)^(x<<17),然后h=y^a[y%MAX]%MAX 当MAX=2^n时,可以用掩码加速取模运算 [/quote] ------------------ 由于你需要的是一个有序对的散列,可以分别对两个数的散列值进行一次异或得到最终的散列值。 [/quote] 额,你这说的是对一堆整数进行hash,我这是范围对,我的目的不是对范围对进行hash就完了,我需要hash过后能一次查找得到结果,你说的方法不能查找范围吧[/quote] 对左右边界整数做一次右移位操作,然后按高低位拼起来。[/quote] 白搭,hash本来就不支持范围查找
小敏纸 2014-06-26
  • 打赏
  • 举报
回复
引用 25 楼 daiweifeng 的回复:
[quote=引用 24 楼 lanxuezaipiao 的回复:] [quote=引用 23 楼 daiweifeng 的回复:] [quote=引用 22 楼 daiweifeng 的回复:] [quote=引用 20 楼 lanxuezaipiao 的回复:] [quote=引用 12 楼 daiweifeng 的回复:] [quote=引用 11 楼 daiweifeng 的回复:] 搞个随机数池,然后做一些简单的移位异或即可,这样最省事保险
无论多大操作都类似。[/quote] 这个这个,没看懂,具体怎么做?能否提示下哈,谢谢啦[/quote] 给定散列容器大小MAX,如果存在一个数组int a[MAX]是一个无重复的随机数整数数组,那么对所有的32bit数据x都可以如下处置: h = x ^ a[x%MAX] % MAX; // h表示散列值 此外还可以在x%MAX之前对x进行若干次移位+位异或运算,如y=x^(x<<3)^(x<<7)^(x<<17),然后h=y^a[y%MAX]%MAX 当MAX=2^n时,可以用掩码加速取模运算 [/quote] ------------------ 由于你需要的是一个有序对的散列,可以分别对两个数的散列值进行一次异或得到最终的散列值。 [/quote] 额,你这说的是对一堆整数进行hash,我这是范围对,我的目的不是对范围对进行hash就完了,我需要hash过后能一次查找得到结果,你说的方法不能查找范围吧[/quote] 对左右边界整数做一次右移位操作,然后按高低位拼起来。[/quote] 谢谢哈,关键是给定一个数,按你说的我怎么查找呢?还是没能领悟你的意思,原谅我的理解能力
超级能量泡泡 2014-06-26
  • 打赏
  • 举报
回复
引用 24 楼 lanxuezaipiao 的回复:
[quote=引用 23 楼 daiweifeng 的回复:] [quote=引用 22 楼 daiweifeng 的回复:] [quote=引用 20 楼 lanxuezaipiao 的回复:] [quote=引用 12 楼 daiweifeng 的回复:] [quote=引用 11 楼 daiweifeng 的回复:] 搞个随机数池,然后做一些简单的移位异或即可,这样最省事保险
无论多大操作都类似。[/quote] 这个这个,没看懂,具体怎么做?能否提示下哈,谢谢啦[/quote] 给定散列容器大小MAX,如果存在一个数组int a[MAX]是一个无重复的随机数整数数组,那么对所有的32bit数据x都可以如下处置: h = x ^ a[x%MAX] % MAX; // h表示散列值 此外还可以在x%MAX之前对x进行若干次移位+位异或运算,如y=x^(x<<3)^(x<<7)^(x<<17),然后h=y^a[y%MAX]%MAX 当MAX=2^n时,可以用掩码加速取模运算 [/quote] ------------------ 由于你需要的是一个有序对的散列,可以分别对两个数的散列值进行一次异或得到最终的散列值。 [/quote] 额,你这说的是对一堆整数进行hash,我这是范围对,我的目的不是对范围对进行hash就完了,我需要hash过后能一次查找得到结果,你说的方法不能查找范围吧[/quote] 对左右边界整数做一次右移位操作,然后按高低位拼起来。
小敏纸 2014-06-26
  • 打赏
  • 举报
回复
引用 23 楼 daiweifeng 的回复:
[quote=引用 22 楼 daiweifeng 的回复:] [quote=引用 20 楼 lanxuezaipiao 的回复:] [quote=引用 12 楼 daiweifeng 的回复:] [quote=引用 11 楼 daiweifeng 的回复:] 搞个随机数池,然后做一些简单的移位异或即可,这样最省事保险
无论多大操作都类似。[/quote] 这个这个,没看懂,具体怎么做?能否提示下哈,谢谢啦[/quote] 给定散列容器大小MAX,如果存在一个数组int a[MAX]是一个无重复的随机数整数数组,那么对所有的32bit数据x都可以如下处置: h = x ^ a[x%MAX] % MAX; // h表示散列值 此外还可以在x%MAX之前对x进行若干次移位+位异或运算,如y=x^(x<<3)^(x<<7)^(x<<17),然后h=y^a[y%MAX]%MAX 当MAX=2^n时,可以用掩码加速取模运算 [/quote] ------------------ 由于你需要的是一个有序对的散列,可以分别对两个数的散列值进行一次异或得到最终的散列值。 [/quote] 额,你这说的是对一堆整数进行hash,我这是范围对,我的目的不是对范围对进行hash就完了,我需要hash过后能一次查找得到结果,你说的方法不能查找范围吧
超级能量泡泡 2014-06-26
  • 打赏
  • 举报
回复
引用 22 楼 daiweifeng 的回复:
[quote=引用 20 楼 lanxuezaipiao 的回复:] [quote=引用 12 楼 daiweifeng 的回复:] [quote=引用 11 楼 daiweifeng 的回复:] 搞个随机数池,然后做一些简单的移位异或即可,这样最省事保险
无论多大操作都类似。[/quote] 这个这个,没看懂,具体怎么做?能否提示下哈,谢谢啦[/quote] 给定散列容器大小MAX,如果存在一个数组int a[MAX]是一个无重复的随机数整数数组,那么对所有的32bit数据x都可以如下处置: h = x ^ a[x%MAX] % MAX; // h表示散列值 此外还可以在x%MAX之前对x进行若干次移位+位异或运算,如y=x^(x<<3)^(x<<7)^(x<<17),然后h=y^a[y%MAX]%MAX 当MAX=2^n时,可以用掩码加速取模运算 [/quote] ------------------ 由于你需要的是一个有序对的散列,可以分别对两个数的散列值进行一次异或得到最终的散列值。
超级能量泡泡 2014-06-26
  • 打赏
  • 举报
回复
引用 20 楼 lanxuezaipiao 的回复:
[quote=引用 12 楼 daiweifeng 的回复:] [quote=引用 11 楼 daiweifeng 的回复:] 搞个随机数池,然后做一些简单的移位异或即可,这样最省事保险
无论多大操作都类似。[/quote] 这个这个,没看懂,具体怎么做?能否提示下哈,谢谢啦[/quote] 给定散列容器大小MAX,如果存在一个数组int a[MAX]是一个无重复的随机数整数数组,那么对所有的32bit数据x都可以如下处置: h = x ^ a[x%MAX] % MAX; // h表示散列值 此外还可以在x%MAX之前对x进行若干次移位+位异或运算,如y=x^(x<<3)^(x<<7)^(x<<17),然后h=y^a[y%MAX]%MAX 当MAX=2^n时,可以用掩码加速取模运算
灌水号 2014-06-24
  • 打赏
  • 举报
回复 1
我觉得不可能。 一个区间可以表示多个点。但是现在你想拿一个点来映射一个没有规律的区间。
zilaishuichina 2014-06-24
  • 打赏
  • 举报
回复
引用 7 楼 lanxuezaipiao 的回复:
我的第一个方法就是这样,速度差的要死,具体看我给出的链接


lz不一定非的信赖stl。

这是我以前自己实现的一个map


测试10个区间,Release,优化全开MaxSpeed,10W次查找,耗时2.7秒

测试代码

class Interval
{
public:
void Init(int min, int max, int value)
{
m_min = min;
m_max = max;
m_value = value;
}

bool operator==(const int &k)
{
if (m_min <= k && k <= m_max )
{
return true;
}
else
{
return false;
}
}

bool operator>(const int &k)
{
if (m_min > k)
{
return true;
}
else
{
return false;
}
}
private:
int m_min;
int m_max;
int m_value;
};

int _tmain(int argc, _TCHAR* argv[])
{
TTree<int, Interval> interval_map;
TLeaf<int, Interval> interval[10];
interval[0].m_Key = 0;
interval[0].m_Value.Init(0, 20000, 1);
interval[1].m_Key = 30000;
interval[1].m_Value.Init(30000, 35000, 2);
interval[2].m_Key = 40000;
interval[2].m_Value.Init(40000, 41000, 3);
interval[3].m_Key = 42000;
interval[3].m_Value.Init(42000, 43000, 4);
interval[4].m_Key = 44000;
interval[4].m_Value.Init(44000, 45000, 5);
interval[5].m_Key = 46000;
interval[5].m_Value.Init(46000, 48000, 6);
interval[6].m_Key = 50000;
interval[6].m_Value.Init(50000, 60000, 7);
interval[7].m_Key = 70000;
interval[7].m_Value.Init(70000, 75000, 8);
interval[8].m_Key = 80000;
interval[8].m_Value.Init(80000, 90000, 9);
interval[9].m_Key = 95000;
interval[9].m_Value.Init(95000, 100000, 10);

for (int i = 0; i < 10; i++)
{
interval_map.Insert(interval + i);
}

int num = 0;
float times = 0.0f;

CPerformance perf;
LARGE_INTEGER dwQPart;

perf.Start(dwQPart);
for (int i = 0; i < 100000; i++)
{
interval_map.Find(i);
}
times = perf.End(dwQPart);

printf("%f\n", times);

getchar();

return 0;
}


zilaishuichina 2014-06-24
  • 打赏
  • 举报
回复
引用 7 楼 lanxuezaipiao 的回复:
[quote=引用 1 楼 zilaishuichina 的回复:] 这不就是一个map吗 struct Interval { int min; int max; } map<Interval, int> table; 要查找78,等价于搜索区间 [78,78]
我的第一个方法就是这样,速度差的要死,具体看我给出的链接[/quote] 那么楼主你希望达到的速度是什么样子的呢, 总共有多少个区间?10W次查找你希望耗时在多少秒以内?
躺着睡的蜗牛 2014-06-24
  • 打赏
  • 举报
回复
引用 13 楼 diplopod 的回复:
能不能把范围分成N段, 让值除以N得到某段, 然后再二分法或者直接比较找范围。 对2^n的除法可以用与实现
说错了, [对2^n的除法可以用与实现]请无视
躺着睡的蜗牛 2014-06-24
  • 打赏
  • 举报
回复
能不能把范围分成N段, 让值除以N得到某段, 然后再二分法或者直接比较找范围。 对2^n的除法可以用与实现
超级能量泡泡 2014-06-24
  • 打赏
  • 举报
回复
引用 11 楼 daiweifeng 的回复:
搞个随机数池,然后做一些简单的移位异或即可,这样最省事保险
无论多大操作都类似。
超级能量泡泡 2014-06-24
  • 打赏
  • 举报
回复
搞个随机数池,然后做一些简单的移位异或即可,这样最省事保险
加载更多回复(14)

64,654

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧