【问题标题】:Hashing the arrangement of an array散列数组的排列
【发布时间】:2021-01-26 04:07:15
【问题描述】:

我正在尝试编写一个函数,它接受一个整数数组,并输出一个哈希值来表示它们的排列顺序。哈希键应该尽可能小(我们正在处理嵌入式和空间/执行优化是至关重要的)

template<size_t N>
int CalculateHashOfSortOrder(int* itemsBegin)
{
    // return ???
}

换句话说,数组[ 4, 2, 1, 3 ] 应该产生一个反映它们的顺序的ID,并匹配以下任何数组:

[ 4, 2, 1, 3 ]
[ 4, 2, 0, 3 ]
[ 400, 200, 100, 300]
et al

我不知道如何以最有效的方式做到这一点。解决这个问题的算法是什么?我想出的方法似乎非常缓慢。

例如,给定上面的例子,有 24 种可能的排列 (4 * 3 * 2 * 1),它们适合 5 位。这些位可以这样计算:

  • 第一个值 (1)。可能有 4 个位置,因此需要 2 位来描述其位置。该值位于位置 2(从 0 开始)。所以推送二进制10
  • 第二个值 (2)。可能有 3 个位置,因此需要 2 个位。它在位置 1,所以推送二进制 01。哈希键现在是 1001b
  • 第三个值 (3)。可能有 2 个位置,因此需要 1 个位。它位于剩余值的第 1 位,因此请推送1

生成的密钥是10011b,但我看不出有什么明确的方法可以做到这一点,但效率极低。我认为还有另一种方法可以解决我从未想过的问题。


编辑:我想到了几个想法:

  1. 计算数组中每一项的排名,并对排名数组进行哈希处理。那么通过什么方法可以将该排名数组打包成理论上最小的 ID?这似乎是我的问题中最令人头疼的部分。
  2. 找到一种在插入项目时保存项目排名的方法,优化 #1
  3. 对于足够少的 # 项 (if() 语句树。需要查看 exe 大小是否有问题。

【问题讨论】:

  • “令人讨厌的低效”——在太空中?还是计算的笨拙? 5 位 几乎 是理论上的最小值。对于 4 个项目,它是最小值(5 位);对于 5 个项目,你会得到 8 位,但理论上的最小值是 7。
  • @RickJames 主要是我想不出一个直接的实现。我的人性化描述似乎并不能非常有效地转化为代码。
  • 你能容忍多大的碰撞概率?
  • 也许“哈希”是错误的术语; @Dave我不能容忍任何碰撞。每个排列都应该产生一个唯一的空间优化 ID。
  • 我还发现了一个相关问题,这些问题被称为Lehmer Code

标签: c++ algorithm optimization


【解决方案1】:

[ 40, 20, 10, 30 ] 的等效哈希是这样的

  1. 40 大于后续值中的 3 个
  2. 20 大于后续值中的 1 个
  3. 10大于0的后续值
  4. 30 后面什么都没有,所以忽略它。

这是一个时间Order(N^2)的一对嵌套循环。 (其实大概是4*4/2,这里有4项。)几行代码就行了。

包装 3,1,0,无论是按照您的方式还是使用 anatolyg 稍微紧一点:

3 * 3! +
1 * 2! +
0 * 1!

等于20。需要存储4!所需的位数,即5位。

我很确定这是空间的最佳选择。除了 O(N^2) 之外,我还没有想到更快的计算方法。

你的 N 有多大?对于 N=100,您需要大约 520 位和 5K 操作。 5K 操作可能需要几微秒(对于 C++),并且可能不到一毫秒(即使对于解释型语言)。

【讨论】:

  • 由于 OP 正在考虑 #3 中的硬编码解决方案,一种可能加快速度以创建硬编码 2D 查找表(似乎比一堆 if-else 逻辑好得多),对于a * b!,其中0 &lt;= a,b &lt;= X 对于一些X。这允许您只做类似table[3][3] + table[1][2] + table[0,1] 的事情,保存阶乘/乘法。
  • 我认为这是正确的想法。我的N 很小:
  • 不确定它是否比 O(N^2) 解决方案更快(如果 N 只有 10,则该查找表可能不会更快),但您也可以创建一个查找表来确定b 在我上面的评论中。 1) 注意列表中的最后一个数字。 2)对数字进行排序(如果可能,就地排序)。 3) 循环遍历表,a 是数字,b 是它在计算table[a][b] 的排序数组中的索引。跳过未排序的最后一个 a 的值。
【解决方案2】:

使用factorial number system 之类的内容。如果您连接位,就好像您将第一个数字乘以 2 的某个幂,第二个数字乘以另一个 2 的幂,等等。在阶乘数字系统中,您乘以一个更“公平”的因子,这不是幂2 个。

为了使结果变小,以一些小的素数为模计算它,例如127. 或一个中等大的素数。 “mod”实际上不一定是素数,它只是不应该有任何小于 N(列表的长度)的素数。

【讨论】:

  • Re modulo:除了破坏信息并将不同的排序折叠在一起之外,我看不出这会做什么。您能说明它如何应用于 OP 的示例吗?
  • 通常,当你做散列时,你想破坏信息,以便输出具有固定大小,即使输入大小可能是无限的。我现在看到 OP 想要防止冲突,所以“mod”部分是不必要的。
【解决方案3】:

计算数组中每一项的排名

  • 并行排序两个数组(或一个 2 元组数组,如果您的语言使这变得容易的话)。数组中的第一个元素是值,第二个是初始索引。请记住,如果这些确实是整数,您可以使用基数排序。
  • 最后,删除已排序的值,只查看索引。这是秩数组(实际上,它是秩数组的逆排列,但既然你在散列,我假设你不在乎)。

然后,将秩数组转换为位。如果您真的想将其打包,您可以查看如何存储“排列”或使用阶乘。或者,您可以只使用每个数字的 (log n) 位,并将它们打包在一起——只要您不这样做不需要扭转整个过程,这就是我的建议。无论您如何执行此操作,您都需要大约 N log(N) 位,但如果您需要,您可以在实践中将该乘数降至最低。

您描述的元编程技术称为“排序网络”,当您在递归排序结束时找到最小的组时,它被用来使排序更快。

你能多谈谈你的实际问题吗?这听起来很可能是XY problem

【讨论】:

  • 我知道这听起来像是一个 XY 问题(不过,几乎不是所有问题?)。很难解释为什么需要它。它是更大算法的一部分,该算法使用基于项目“重要性”(等级)的巨大 LUT。我要问的功能是生成 LUT 键。
  • 是的,这些经常出现在 stackoverflow 上,是为了让问题变得简单到可以理解(对你自己或其他人来说)。这不是一件坏事,但我认为值得在这里标记。例如,您需要所有项目的重要性吗?也许你可以扔掉 90% 的钥匙。我建议详细说明。
【解决方案4】:

Finding n-th permutation without computing others

这是一种解决方案,可让您在 O(n) 中以一致但非字典顺序的方式获得 n 个元素的第 k 个排列。

【讨论】:

    猜你喜欢
    • 2014-05-19
    • 2014-01-08
    • 2012-06-12
    • 2010-11-02
    • 1970-01-01
    • 1970-01-01
    • 2012-08-03
    • 2010-12-04
    • 2015-04-21
    相关资源
    最近更新 更多