C++ 在排序静态数组中搜索的最快方法

C++ 在排序静态数组中搜索的最快方法,c++,algorithm,sorting,optimization,C++,Algorithm,Sorting,Optimization,我正在寻找最快的方法来搜索排序,固定数组的32位键。数组大小和数据是静态的,永远不会更改。此阵列的大小约为1000-10000个唯一元素。搜索范围要大得多(~100000),因此无法找到大量搜索值。我只对精确匹配感兴趣 以下是搜索过程: 生成约100个密钥。这些键是按相关性排序的,因此不能简单地对它们进行排序 在一组静态数组中搜索大约100个键(通常在50到300个之间) 当我们找到足够的匹配结果时停止搜索(因此不排序键以获得最相关的结果很重要) 键的一个潜在有趣的特性是,即使它们在整数值方面不

我正在寻找最快的方法来搜索排序,固定数组的32位键。数组大小和数据是静态的,永远不会更改。此阵列的大小约为1000-10000个唯一元素。搜索范围要大得多(~100000),因此无法找到大量搜索值。我只对精确匹配感兴趣

以下是搜索过程:

  • 生成约100个密钥。这些键是按相关性排序的,因此不能简单地对它们进行排序
  • 在一组静态数组中搜索大约100个键(通常在50到300个之间)
  • 当我们找到足够的匹配结果时停止搜索(因此不排序键以获得最相关的结果很重要)
  • 键的一个潜在有趣的特性是,即使它们在整数值方面不接近,它们中的大多数也只与最近的邻居有几个不同的位(~1-4)

    我找到的大多数答案都指向二进制搜索,但没有一个涉及静态数组的情况,这可能会带来一些优化的可能性

    我完全可以控制数据结构,现在它是一个固定的、排序的数组,但如果不是最优的,我可以改变它。我还可以添加预计算的信息,因为如果不占用不合理的内存量,数据不会改变

    我们的目标是提高CPU和内存的效率,尽管CPU是这里的首要任务


    使用C++,虽然这可能不会影响答案。

    考虑到静态数组从不改变,并且你有无限的预处理能力,我认为最好的方法是为每个数组创建一个特定的哈希函数。p> 我的方法-定义参数化哈希函数(java代码):

    私有静态函数createHashFunction(int sz){
    int mvLeft=ThreadLocalRandom.current().nextInt(30);
    int mvRight=ThreadLocalRandom.current().nextInt(16);
    int mvLeft2=ThreadLocalRandom.current().nextInt(10);
    int mvRight2=ThreadLocalRandom.current().nextInt(16);
    int mvLeft3=ThreadLocalRandom.current().nextInt(16);
    int mvRight3=ThreadLocalRandom.current().nextInt(20);
    返回(键)->{
    //这些操作完全是随机的,并且没有数学背景!
    key=~key+(key>>mvRight);
    键=键+(键>>mvRight2);
    键=键+(键>>mvRight3);
    return(int)(Math.abs(key)%sz);//sz是目标数组的大小
    };
    }
    
    对于每个测试阵列,找到这样的参数组合,即最大桶大小最小

    一些测试(输入数组大小为10k,填充随机元素):

    • 散列映射到[0..262k]会导致一个包含2个项目的存储桶,最多测试5k个随机数组,单线程版本以每秒约100个数组的速度查找散列函数
    考虑到最大bucket size为2时,可以将两个值映射为一个64位整数,这种方法只会导致一个内存跳转,CPU哈希的最简单操作是通过xor、plus和shifts进行的,这应该是非常快的,也应该是位比较


    但是,您的数据可能不太好,可能需要桶大小为3,这会破坏桶项目的
    long
    使用可能性。在这种情况下,您可以尝试找到一些合适的散列函数,而不是我编写的随机乱码。

    如果您可以控制数据结构,为什么搜索空间被划分为“50-300”个不同的数组,而不是在单个排序数组中?二进制搜索可能仍然最快,尤其是在静态数据已经排序的情况下(并且不需要对数据进行任何操作)。除非您想将数据加载到一堆散列容器中(如果您只寻找精确匹配)如果你有正确的哈希算法,它可能会表现得更好。@Tylerduden有两个原因。1.我们不在每个数组中搜索,大约有2000个,搜索的一个取决于当前的请求。2.每个数组是绑定到特定数据段的一组键,不同的集合有具有相同数值但不相同的键同样的意思。要搜索的静态数组通常是(完美的)散列是个好主意。足够的匹配结果是什么意思?@JonChesterfield在给定的搜索中,如果我们找到了预定数量的匹配项,我们将停止搜索并处理这些结果。我将研究完美散列这类似于@JonChesterfield建议的完美散列,这是迄今为止最好的建议。@user2413068,我的主要Idea的目的是表明您可以消除潜在的缓慢操作,这在使用常规方法时是不可避免的,例如Trie或通用哈希映射(对存储桶大小没有限制)。此外,我现在对大小为2-3的桶的想法似乎不对-无论如何,您都必须指定桶的大小。事实上,您需要做的就是找到最大桶大小
    L
    ,并以下一种方式放置每个桶的数据:
    [L,val1,val2,…,valL]
    ,这将导致
    ((L+1)*4)*sz
    每个数组的内存字节数。@user2413068,只是想强调一下-在设计这样的系统时,主要的问题是人们倾向于以渐进的复杂性来衡量性能,而主要考虑的是CPU周期和堆内存跳跃的数量。我遇到过
    O(n*lg^2(n))
    优于
    O的情况(n)<代码> N~1KK算法,因为第二个操作无效。当然,这是有意义的,当然,它的分析将决定最佳的解决方案而不是复杂性。谢谢你的洞察力。你也可以考虑哪一个软件包是用来寻找完美散列的。哈希函数提出了与上面不同的形式,通常涉及在小数组中查找以构建哈希函数
    
    private static Function<Long, Integer> createHashFunction(int sz) {
        int mvLeft = ThreadLocalRandom.current().nextInt(30);
        int mvRight = ThreadLocalRandom.current().nextInt(16);
        int mvLeft2 = ThreadLocalRandom.current().nextInt(10);
        int mvRight2 = ThreadLocalRandom.current().nextInt(16);
        int mvLeft3 = ThreadLocalRandom.current().nextInt(16);
        int mvRight3 = ThreadLocalRandom.current().nextInt(20);
        return (key) -> {
            // These operations are totally random, and has no mathematical background beneath them!
            key = ~key + (key << mvLeft);
            key = key ^ (key >>> mvRight);
            key = key + (key << mvLeft2);
            key = key ^ (key >>> mvRight2);
            key = key + (key << mvLeft3);
            key = key ^ (key >>> mvRight3);
            return (int) (Math.abs(key) % sz); // sz is the size of target array
        };
    }