稀疏的O(1)数组,索引是连续的产品

时间:2011-01-15 01:39:03

标签: c++ algorithm sparse-array

我想预先计算一些一元函数f的值数组。

我知道我只需要f(x)的值,其中x的格式为a*b,其中ab都是范围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个答案......

4 个答案:

答案 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