我想预先计算一些一元函数f
的值数组。
我知道我只需要f(x)
的值,其中x
的格式为a*b
,其中a
和b
都是范围0..N
中的整数。
显而易见的时间优化选择只是制作一个大小为N*N
的数组,并且只是预先计算我稍后要阅读的元素。对于f(a*b)
,我只需检查并设置tab[a*b]
。这是最快的方法 - 但是,由于此数组中有许多索引(以N+1
开头),这将占用大量空间,而这些索引永远不会被触及。
另一种解决方案是制作一个简单的树形图...但是这会通过引入大量分支来大大减慢查找本身非常。否。
我想知道 - 是否有任何解决方案可以使这样的数组稀疏且更小,但在查找中仍然可以快速无分支O(1)?
修改
我可以听到很多关于哈希映射的评论......我将继续对基准行为进行基准测试(我希望由于分支而导致正常查找的性能显着下降;比树中的要少,但仍然如此。 ..让我们看看我是对的!)。
我想强调一下:我非常欣赏一个分析解决方案,该解决方案将使用一些聪明的方式(?)来利用只有“产品类似”指数的事实。我觉得这个事实可能被利用来获得一个更好的结果,一般的哈希映射函数,但我自己也没有想法。
修改
根据您的建议,我从gcc 4.5尝试了std::unordered_map
。这比简单的数组查找慢一点,但确实比基于树的std::map
快得多 - 最终我对这个解决方案很满意。我现在明白为什么不可能做我原本打算做的事情;谢谢你的解释!
我只是不确定哈希映射是否实际上保存了任何内存! :)正如@Keith Randall所描述的那样,我的内存占用率不能低于N*N/4
,而且@Sjoerd描述的三角矩阵方法给了我N*N/2
。我认为如果元素大小很小(取决于容器开销),哈希映射完全有可能使用超过N*N/2
空间 - 这将使最快的方法也是最有效的内存!我会试着检查一下。
我希望我能接受2个答案......
答案 0 :(得分:5)
首先将其视为二维数组:tab[a][b]
。这仍然需要N * N尺寸。
将使用每个条目,但会有重复:f(a,b) = f(b,a)
。因此,仅需要三角形矩阵(以> b与a
if (a < b) return tab[b*(b+1) + a]; // assuming 0 <= a < b < N
else return tab[a*(a+1) + b]; // assuming 0 <= b <= a < N
或者
if (a < b) return tab[b*(b-1) + a]; // assuming 1 <= a < b <= N
else return tab[a*(a-1) + b]; // assuming 1 <= b <= a <= N
编辑:三角矩阵使用的存储器是(N + 1)* N / 2,大约是方阵的一半大小。但仍然是二次方:(
EDIT2:请注意,er仍然在矩阵中重复:例如f(3, 2) = f(6, 1)
。我不认为如果不引入大量的分支和循环就可以消除这种情况,但这只是一种直觉。
答案 1 :(得分:2)
这里似乎没有很多结构可以利用。如果你问是否有办法安排排列表,这样你可以避免存储不能发生的条目(因为它们的素数大于N),你就无法节省太多。存在theory of smooth numbers,其表明N ^ 2附近的N平滑数的密度为~2 ^ -2。因此,绝对最好的情况是,您可以将(最大)存储要求降低至多4倍。
我认为你最好利用对称性,然后使用哈希表,如果你期望大多数参数永远不会发生。
答案 2 :(得分:0)
为什么不简单地散列A和B组合并将结果放在地图中?懒得去做,这样你才能得到你想要的那些?
public Result f(Type1 a, Type2 b) {
TypePair key = new TypePair(a, b);
Result res = map.get(key);
if (res == null) {
res = reallyCalculate(a, b);
map.put(key, res);
}
return res;
}
基本记忆。
答案 3 :(得分:0)
Hash tables在查找速度和内存开销之间提供了良好的平衡。 C ++标准库不提供哈希表,尽管它有时可用作非标准扩展。例如,请参阅SGI hash_map。
Poco C ++库还有一个HashTable和HashMap类,请参阅documentation。