【问题标题】:Speeding up a search for best binary matching number加快搜索最佳二进制匹配数
【发布时间】:2014-02-15 21:47:56
【问题描述】:

我有一个 256 位值的数组。该数组很大(数百万条记录),但很少修改它并且适合内存。对于给定的 256 位数字,我想查找是否存在至少 N 位相等的记录。例如,10000 和 01111 有 0 位相等,1000 和 1001 有 3 位相等。总是 N > 128,或者更确切地说是 N > 140。我不需要找到特定的数字,我只需要查找列表中是否存在这样的数字。

是否有某种数据结构或某种索引可以以某种方式加快搜索速度?

【问题讨论】:

  • 有趣的问题。 nearest neighbor search 算法可以做到这一点,尽管这会做比必要的工作更多的工作。如果这些位接近随机,答案几乎总是“是”。我觉得应该有一种方法可以非常非常快地做到这一点。
  • 我也有同样的感觉 :-(
  • 这个问题大约是 32 位,但答案可能至少部分仍然适用:stackoverflow.com/q/6389841/555045
  • 数据会是什么样子?随机案例很简单,但您不太可能拥有随机数据。我们应该期待很多“是”的答案还是“不是”的答案?我们是否应该在边界上期待很多查询?
  • ...好吧,我认为如果我揭示我的问题背后的问题是公平的。我正在处理我发现的 NILSIMCA 哈希算法的垃圾邮件过滤。 en.wikipedia.org/wiki/Nilsimsa_Hash 我想我会有很多 NILSIMCA 哈希值,它们将是垃圾邮件样本。当收到一封电子邮件时,我需要将所有邮件都穿墙,搜索该电子邮件是否为垃圾邮件。

标签: algorithm search data-structures


【解决方案1】:

我可能错了,但看起来您可以将数据按位索引trie,在您的情况下,这与二叉树的结构相同,但解释不同。这是插图(source):

为简单起见,让我们将 256 位数字视为具有 256 个数字(当然也是二进制)的向量。有了像上面这样的树,您只需沿着树走下去并测试是否存在后续分支(“0”或“1”),就可以在 256 步或更少的时间内找到特定向量是否存在于数据集中。像这样的东西(代码很原始,但你应该明白):

def exists(node, v, i):
    """Checks if subvector of v starting from index i exists in 
       sub-tree defined by node
    """
    if i == len(v): 
        return True
    elif v[i] == 0 and node.left: 
        return exists(node.left, v, i + 1)
    elif v[i] == 1 and node.right: 
        return exists(node.right, v, i + 1)
    else: 
        return False

在每个步骤中,我们根据v 的当前值元素决定去哪里向左或向右分支。但是您还需要处理多达 K 个不同的元素。好的,那么为什么不使用错误计数并处理两个分支呢?

def exists(node, v, i, err_left=K):
    """Checks if subvector of v starting from index i exists in 
       sub-tree defined by node with up to err_left errors.
    """
    if i == len(v): 
        return True
    elif v[i] == 0: 
        if node.left:
            return exists(node.left, v, i + 1, err_count)
        elif node.right: 
            return exists(node.left, v, i + 1, err_left - 1)  # proceed, but decrease 
                                                              #   errors left
        else: 
            return False            
    elif v[i] == 1: 
        if node.right:
            return exists(node.right, v, i + 1, err_count)
        elif node.left: 
            return exists(node.right, v, i + 1, err_left - 1)  # proceed, but decrease 
                                                               #   errors left
        else: 
            return False 
    else: 
        return False

此算法的运行时间很大程度上取决于您的设置。在最坏的情况下(K = 256),它仍然是 O(n)(你需要检查每个分支),但是随着 K 的减小,这个时间会迅速下降(对于小的 K,它几乎是 O(log n))。 K ~ 128 它在中间的某个地方。

您可能还想重写函数,以便它首先检查美好的一天(无错误)场景(例如,如果您预计平均错误数量很少)。

在某种意义上尝试压缩数据,但如果您遇到内存问题,请尝试使用表表示而不是对象和指针。

最后,您可以将它与bloom filters 结合使用,以首先过滤绝对不在集合中的数字,然后使用上面的树检查其余的数字。

【讨论】:

  • 有趣的想法,谢谢!
【解决方案2】:

解决这个问题的算法是 O(n)。您唯一的选择是遍历数组,直到找到与您的目标匹配的数字。

现在,我们如何找到两个数字是否匹配?最有效的方法是使用按位运算。例如,我编写了一个适用于 64 位长数字的代码:

int compare(long l1, long l2)
{
    //Use xor to get what bits are the same
    long xor = ~ (l1 ^ l2);

    //count the number of bits with value = 1
    int count = 0;
    while(xor != 0)
    {
        //check if the right bit is 1, using & operator beetween our number and 0000001
        if(xor & 1 > 0) count++;

        //shift right the bits
        xor = xor >> 1
    }

    return count;
}

可以根据您的 256 位数字的实现方式来调整此比较。 当您到达count >= N 时,可能会中断while 循环。

您可以检查this question 以寻找更有效的方法来计算值为1 的位。

希望对你有帮助!

【讨论】:

  • "您唯一的选择是遍历数组,直到找到与您的目标匹配的数字。"你能证明吗?为什么没有更好的方法?
  • 如果数组未排序并且您没有关于项目的任何其他信息,则必须循环遍历数组直到找到匹配项。这与查找数组的最大整数相同,在最坏的情况下,您需要 n 移动以找到最大项,其中 n 是数组的长度。
  • 但是您可以对数组进行排序并加快搜索速度,并且您可以对其进行预处理以存储其他信息。 OP 正在寻找一种方法来加快这一进程。
  • 你是对的!如果您要执行超过 1 个查询,您可以进行一些预处理,这将带您至少 O(n),但会降低每个查询的复杂性。这个问题我再看看,很有意思,谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-05-18
  • 2017-08-07
  • 1970-01-01
相关资源
最近更新 更多