【问题标题】:Generating ids for a set of integers为一组整数生成 id
【发布时间】:2010-11-24 02:55:24
【问题描述】:

背景:

我正在处理整数序列 {0, 1, 2 ... , n} 的排列。 我有一个本地搜索算法,它以某种系统的方式将一个排列转换为另一个排列。该算法的重点是产生一个最小化成本函数的排列。我想处理从 n=5 到 n=400 的各种问题。

问题:

为了减少搜索工作,我需要能够检查我之前是否处理过特定的整数排列。我为此使用了一个哈希表,我需要能够为每个排列生成一个 id,我可以将其用作表中的键。但是,我想不出有什么好的散列函数可以将一组整数映射到一个键中,这样冲突就不会太频繁地发生。

我尝试过的东西:

我首先生成一个由 n 个素数组成的序列,然后将排列中的第 i 个数与第 i 个素数相乘,然后对结果求和。然而,即使 n=5,生成的密钥也会产生冲突。

我还考虑将所有数字的值连接在一起,并将结果字符串的整数值作为键,但即使对于较小的 n 值,id 也会很快变得太大。理想情况下,我希望能够将每个键存储为整数。

stackoverflow 对我有什么建议吗?

【问题讨论】:

    标签: algorithm math hash


    【解决方案1】:

    Zobrist hashing 可能对你有用。您需要创建一个由随机整数组成的 NxN 矩阵,表示该元素 i 的每个单元格位于当前排列的第 j 个位置。 对于给定的排列,您选择 N 个单元格值,然后将它们逐个异或以获得排列的键(请注意,键的唯一性不能保证)。

    该算法的要点是,如果您交换到排列中的元素,您可以通过简单地异或新位置中的旧和异或来轻松地从当前排列中生成新键。

    【讨论】:

    【解决方案2】:

    从你的问题和你留下的cmets来看,我会说你的问题是不可能解决的。

    让我解释一下。

    您说您需要组合中的唯一哈希,所以让我们制定规则 #1:

    • 1:需要一个唯一的数字来表示任意数量的数字/数字的组合

    好的,那么您在评论中说过,由于您使用了很多数字,因此由于内存限制,将它们存储为字符串或诸如此类的东西作为哈希表的键是不可行的。所以让我们把它改写成另一个规则:

    • 2:无法使用用于生成哈希的实际数据,因为它们不再在内存中

    基本上,您正在尝试获取一个大数字,并将其存储到一个小得多的数字范围内,并且仍然具有唯一性。

    抱歉,您不能这样做。

    典型的散列算法会产生相对唯一的散列值,因此除非您愿意接受冲突,否则即使新组合没有被标记为“已经看到”,那么您就已经出局了运气。

    如果您要尝试一个位域,其中每个组合都有一个位,如果没有看到则为 0,您仍然需要大量内存。

    对于您在评论中留下的 n=20 的排列,您有 20 个! (2,432,902,008,176,640,000) 个组合,如果您尝试将每个组合简单地存储为位字段中的 1 位,则需要 276,589TB 的存储空间。

    你将不得不限制你想要做的事情的范围。

    【讨论】:

    • 嗨,拉瑟。感谢您的评论。我认为我的帖子应该更清楚;我整天都在处理小实例(n = 5),在这些情况下,我想要唯一性。对于较大的实例,最小化冲突就足够了。我并不想详尽地列举所有排列,而是进行定向本地搜索。
    【解决方案3】:

    正如其他人所建议的那样,您可以使用散列来生成一个很有可能唯一的整数。但是,如果您需要整数始终是唯一的,您应该 rank 排列,即为它们分配顺序。例如,集合 {1,2,3} 的常见排列顺序是字典顺序:

    1. 1,2,3
    2. 1,3,2
    3. 2,1,3
    4. 2,3,1
    5. 3,1,2
    6. 3,2,1

    在这种情况下,排列的 id 是它在字典顺序中的索引。当然,还有其他排列排列的方法。

    使 ids 成为一系列连续整数,可以将处理后的排列存储为位字段或布尔数组。

    【讨论】:

    • 这种方法将为您提供保证唯一性的尽可能小的密钥大小。谷歌“排名”“排列”以找到一些对任意排列进行排名的有效方法。 (嗯,比枚举每个较低的排列更有效...... :))
    • 我喜欢这个,但它确实需要比简单的散列方法保存更多的数据。
    • 如果 n 大约是 6 或 7,这将是一个很好的解决方案。当您将 20 的排列映射到前 20 时!整数,它变得非常愚蠢。 Daniel 说的是 n=400。唯一性是不现实的。
    • 当您使用散列时,有两种可能性: 1. 散列是唯一的 - 在这种情况下,您再次映射到 n!整数,以及 2. 散列不是唯一的,在这种情况下,您还需要存储每个已处理的排列,以确保您处理了它。但是,对于较大的 n,没有什么可以提高内存效率,因此肯定不得不求助于使用外部存储。
    • @Daniel:您能否更新您的问题以提及您的 n=400 左右? ozan 正确地说这种方法不适合 n>10 左右。
    【解决方案4】:

    需要多快?

    您总是可以将整数作为字符串收集,然后对其进行哈希处理,然后只获取前 4 个字节。

    对于哈希,您可以使用任何函数,例如 MD5 或 SHA-256。

    【讨论】:

    • 你好丝滑。感谢您发布。哈希需要相当快,因为​​要考虑许多排列。我不熟悉 MD5 或 SHA-256 的时间复杂度,但我确实喜欢获取结果的前 4 个字节的建议。
    • 我会同时尝试,然后比较时间。
    【解决方案5】:

    您可以对包含您的整数的逗号分隔字符串进行 MD5 哈希处理。

    在 C# 中它看起来像这样(免责声明:我今天使用的机器上没有编译器):

    using System;
    using System.Security.Cryptography;
    using System.Text;
    
    public class SomeClass {
        static Guid GetHash(int[] numbers) {
            string csv = string.Join(',', numbers);
            return new Guid(new MD5CryptoServiceProvider().ComputeHash(Encoding.ASCII.GetBytes(csv.Trim())));
        }
    }
    

    编辑:我在想什么?正如其他人所说,您不需要哈希。 CSV 应该足以作为字符串 ID(除非您的 numbers 数组很大)。

    【讨论】:

    • 散列是个好主意,因为他想要一个整数,而不仅仅是一个字符串,来表示唯一性。但是,正如我在回复中所说,您可以只取任何散列的前 4 个字节来得到这个:)
    【解决方案6】:

    将每个数字转换为字符串,连接字符串(通过 StringBuffer)并将 StringBuffer 的内容作为键。

    【讨论】:

    • 你当然需要一个分隔符,否则 12,3 将与 1,23 相同
    • 嗨,维克多,这个建议在我原来的帖子中提到过。它会起作用,但问题是密钥很快就会变得非常大。考虑 n = 20 的情况。如果我的排列是 {1, 2, 3 ... 18, 19, 20},则相应的 id 是:01234567891011121314151617181920 我希望处理的一些更大的问题有 n = 400。理想情况下,我更喜欢内存效率更高的解决方案。
    • n=400?您想将排列唯一映射到整数吗? 400!是一个非常大的数字……大约 870 个十进制数字。您可能不得不忘记唯一性并采用上述建议的基于哈希的解决方案。
    【解决方案7】:

    与问题没有直接关系,但作为替代解决方案,您可以使用 Trie tree 作为查找结构。 Trie 树非常适合字符串操作,它的实现相对容易,并且对于大量长字符串,它应该比哈希集更快(n(k) 的最大值,其中 k 是键的长度)。而且您不受密钥大小的限制(例如在必须 int 中的常规哈希集中,而不是更大)。在你的情况下,关键将是一个由一些字符分隔的所有数字的字符串。

    【讨论】:

      【解决方案8】:

      素数可以起作用:如果 p_i 是第 ith 素数并且 a_i 是你的元组的第 ith 元素,那么

      p_0**a_0 * p_1**a_1 * ... * p_n**a_n
      

      Fundamental Theorem of Arithmetic 应该是唯一的。不过,这些数字会变得非常大:-)

      (例如,对于 n=5,(1,2,3,4,5) 将映射到已经超过 32 位的 870,037,764,750)

      【讨论】:

        【解决方案9】:

        类似于Bojan's post,似乎最好的方法是对排列有一个确定的顺序。如果您按该顺序处理它们,则无需进行查找以查看您是否已经完成了任何特定的排列。

        【讨论】:

          【解决方案10】:

          得到同一系列数{1,..,n}的两个排列,构造一个映射元组,(id, permutation1[id], permutation2[id]), or (id, f1(id), f2( ID));您将通过 {f3(id)| 获得一张独特的地图对于元组 (id, f1(id), f2(id)) ,我们从 id 得到一个 f2(id),并从元组 (id',f1(id'),f2(id')) 中找到一个 id'其中 f1(id') == f2(id)}

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2014-09-04
            • 1970-01-01
            • 1970-01-01
            • 2011-01-11
            • 1970-01-01
            • 2015-05-11
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多