哈希值为三倍

时间:2016-12-29 13:17:28

标签: swift hash set double

对象包含三个双精度值。如果所有三个值在任何可能的组合中相等,则两个对象相等。需要一个函数来确定数组中“唯一”对象的数量。 我想从数组生成Set并返回计数,但是符合Hashable协议需要hashValue函数... 在Swift上编码但在任何语言(外星人除外)上的算法将不胜感激:)

所以我需要三个双精度的hashValue(值的顺序无关紧要)或确定数组中唯一对象的任何其他解决方案

更新:“唯一”表示不相等。如上所述,两个具有双重值的对象(例如a,b,c)等于任何可能组合中的所有三个值。例如:

obj1 = (a: 1, b: 2, c: 3)
obj2 = (a: 3, b: 2, c: 1)
// obj1 is equal obj2

1 个答案:

答案 0 :(得分:4)

这是一个让一个类保持3个双打Hashable的示例,以便您可以使用Set来确定数组中保存的唯一数量:

class Triple: Hashable {
    var a: Double
    var b: Double
    var c: Double

    // hashValue need only be the same for "equal" instances of the class,
    // so the hashValue of the smallest property will suffice        
    var hashValue: Int {
        return min(a, b, c).hashValue
    }

    init(a: Double, b: Double, c: Double) {
        self.a = a
        self.b = b
        self.c = c
    }
}

// Protocol Hashable includes Equatable, so implement == for type Triple:
// Compare sorted values to determine equality    
func ==(lhs: Triple, rhs: Triple) -> Bool {
    return [lhs.a, lhs.b, lhs.c].sorted() == [rhs.a, rhs.b, rhs.c].sorted()
}

let t1 = Triple(a: 3, b: 2, c: 1)
let t2 = Triple(a: 1, b: 2, c: 3)
let t3 = Triple(a: 1, b: 3, c: 2)
let t4 = Triple(a: 3, b: 3, c: 2)

let arr = [t1, t2, t3, t4]

// Find out how many unique Triples are in the array:    
let set = Set(arr)
print(set.count)  // 2

讨论:更好的散列函数

正如@PatriciaShanahan在评论中指出的那样,在min的计算中使用hashValue有一些缺点:

  1. min因为涉及比较而价格昂贵。
  2. 在计算hashValue的{​​{1}}时,仅考虑最小项目将导致许多具有相同Triple的“常见”Triple。例如,值hashValue且两个正值的任何Triple都具有相同的0
  3. 我之所以选择hashValue,是因为我觉得很容易理解,min会为min(a, b, c)a和{{的所有排序带来相同的价值1}}这意味着我们会为所有排序获得相同的b。这很重要,因为我们认为c s与3个值的排序无关,hashValue对于值的任何排序必须相同,因为Triple暗示{ {1}}。

    我考虑过其他散列函数,例如:

    • hashValue
    • a == b

    但这些都是有缺陷的。从数学上讲,加法和乘法是可交换的,因此从理论上讲,无论a.hashValue == b.hashValue(a + b + c).hashValue(a * b * c).hashValue的顺序如何,这些都会产生相同的hashValue。但是,实际上,更改操作顺序可能会导致溢出下溢

    考虑以下示例:

    a

    b的理想散列函数将:

    1. clet a = Int.max let b = Int.min let c = 5 let t1 = (a + b) + c // 4 let t2 = (a + c) + b // Overflow! Triple的所有排序保证相同hashValue
    2. 快速计算。
    3. 如果abc中的任何一项发生变化,则会更改结果。
    4. 无法溢出下溢
    5. 组合数字的一个好的数学运算是按位OR 函数a。它通过比较两个值按位并将结果位设置为b(如果两个位相同)和c(如果位不同)组合两个值。

      ^

      将其扩展为3个值:

      0

      如上表所示,所有订单均为1,所有订单均为 a b result --- --- ------ 0 0 0 0 1 1 1 0 1 1 1 0 ,所有订单均为 a b c result --- --- --- ------ 0 0 0 0 0 0 1 1 0 1 0 1 0 1 1 0 1 0 0 1 1 0 1 0 1 1 0 0 1 1 1 1 ,所有订单均为XOR(0, 0, 0) = 0。因此,使用异或来组合这些值符合为所有排序提供相同结果的第一个标准。

      异或是一种快速操作。它由单个汇编指令实现。所以它符合良好散列函数的第二个标准。

      如果XOR(1, 0, 0) = 1XOR(1, 1, 0) = 0XOR(1, 1, 1) = 1中的任何一项发生变化,则a的结果会发生变化。所以异或符合良好散列函数的第三个标准。

      异或不能上溢或下溢,因为它只是设置位。因此它符合良好散列函数的第四个标准。

      因此,更好的b函数将是:

      c