Go 比较两片

Go 比较两片,go,comparison,slice,Go,Comparison,Slice,在Go中是否有一种方法可以比较两个切片并获得切片X中不在切片Y中的元素,反之亦然 X := []int{10, 12, 12, 12, 13} Y := []int{12, 14, 15} func compare(X, Y []int) calling compare(X, Y) result1 := []int{10, 12, 12, 13} // if you're looking for elements in slice X that are no

在Go中是否有一种方法可以比较两个切片并获得切片X中不在切片Y中的元素,反之亦然

    X := []int{10, 12, 12, 12, 13}
    Y := []int{12, 14, 15}

func compare(X, Y []int)  

calling compare(X, Y)   
    result1 := []int{10, 12, 12, 13} // if you're looking for elements in slice X that are not in slice Y

calling compare(Y, X)
    result2 := []int{14, 15} // if you're looking for elements in slice Y that are not in slice X

如果顺序不重要,而且集合很大,则应该使用集合实现,并使用其diff函数来比较它们

集合不是标准库的一部分,但您可以使用此库。例如,您可以使用它从切片自动初始化集合

大概是这样的:

import (
    set "github.com/deckarep/golang-set"
    "fmt"
    )

func main() {
    //note that the set accepts []interface{}
    X := []interface{}{10, 12, 12, 12, 13}
    Y := []interface{}{12, 14, 15}

    Sx := set.NewSetFromSlice(X)
    Sy := set.NewSetFromSlice(Y)
    result1 := Sx.Difference(Sy)
    result2 := Sy.Difference(Sx)

    fmt.Println(result1)
    fmt.Println(result2)
}

在集合实现中拖动可能有点过头了:


如果切片变大,可以添加bloom过滤器。

提供的所有解决方案都无法准确回答问题。解决方案提供了片中元素集的差异,而不是片中的差异

具体而言,代替以下预期示例:

    X := []int{10, 12, 12, 12, 13}
    Y := []int{12, 14, 15}

func compare(X, Y []int)  

calling compare(X, Y)   
    result1 := []int{10, 12, 12, 13} // if you're looking for elements in slice X that are not in slice Y

calling compare(Y, X)
    result2 := []int{14, 15}
提供的解决方案将导致:

result1 := []int{10,13}
result2 := []int{14,15}
为了严格得出示例结果,需要使用不同的方法。以下是两种解决方案:

如果切片已排序:

如果您对切片进行排序,然后调用compare,此解决方案可能会更快。如果你的切片已经分类,它肯定会更快

func compare(X, Y []int) []int {
    difference := make([]int, 0)
    var i, j int
    for i < len(X) && j < len(Y) {
        if X[i] < Y[j] {
            difference = append(difference, X[i])
            i++
        } else if X[i] > Y[j] {
            j++
        } else { //X[i] == Y[j]
            j++
            i++
        }
    }
    if i < len(X) { //All remaining in X are greater than Y, just copy over
        finalLength := len(X) - i + len(difference)
        if finalLength > cap(difference) {
            newDifference := make([]int, finalLength)
            copy(newDifference, difference)
            copy(newDifference[len(difference):], X[i:])
            difference = newDifference
        } else {
            differenceLen := len(difference)
            difference = difference[:finalLength]
            copy(difference[differenceLen:], X[i:])
        }
    }
    return difference
}

如您所见,如果不使用映射对数组进行排序,则速度要快得多,但使用映射比先对数组进行排序,然后使用迭代方法要快得多。对于小案例,排序可能足够快,因此应该使用它,但基准测试将很快完成,以便进行计时。

类似的操作应该可以:

package main

import "fmt"

func main() {
    X := []int{10, 12, 12, 12, 13}
    Y := []int{12, 14, 15}

    fmt.Println(compare(X, Y))
    fmt.Println(compare(Y, X))
}

func compare(X, Y []int) []int {
    m := make(map[int]int)

    for _, y := range Y {
        m[y]++
    }

    var ret []int
    for _, x := range X {
        if m[x] > 0 {
            m[x]--
            continue
        }
        ret = append(ret, x)
    }

    return ret
}
还有一个软件包():

packagediff实现了一个差分算法。Eugene Myers,Algorithmica第1卷第2期,1986年,第251-266页中描述了该算法



你的实现不会产生任何结果。如果写得好且不臃肿,那么拖动复制粘贴的半个集合实现比导入一个同样的集合库更糟糕。@not_a_Golfer,我认为有一些用例,通过使用小型自定义函数而不是外部库,您的情况不会变得更糟。此外,即使你大部分时间都在使用这个库,你也可能想知道一个简单的版本。@iliachly Go以一种如此美妙的方式促进了代码重用,在这种情况下重新发明轮子似乎是多余的。但这是有争议的。依赖关系确实有其隐藏的成本。但话说回来,为什么要重新发明轮子?“Go以如此美丽的方式促进代码重用”,因为它缺乏依赖性版本控制?还有,现在是17点。。。不完全是重新发明轮子。我正在寻找一个独立于任何外部库的实现,订购并不重要。@jwesonga你可以把这个库放到你的源代码树中,为什么还要实现另一个集?正如miku指出的,我正在寻找一个更简单的实现,习俗function@jwesonga在这种情况下,你不应该操之过急——使用全套实现不会降低效率,而且你可以把实现它的时间花在更重要的东西上——比如其他功能。或者在上面辩论;)@jwesonga,有人将您的问题发布到,似乎也有一些代码…为什么
result1
包含
12
?大概是因为
X
包含3个不同的
12
项,
Y
包含1个。
result1
切片包含两个
12
s(或者,
3-1
)。@AndrewM在这种情况下,应该删除
set
set difference
标记。如果可以重构相同的代码,使其以O(n)而不是O(n^2)运行,这段代码的复杂性实际上是O(n*log(n))(如果映射复杂性是O(log(n)),我认为它实际上更低,因为它是作为(多级)散列映射实现的)。我不确定如果切片没有排序,性能是否可以提高。如果它们类似于Laremere的方法,则可以使用。请注意,在排序版本中,
//X中所有剩余的
if
块都大于Y,只需复制
即可显著简化为使用子切片
剩余:=X[I:]
差异=追加(差异,剩余…
和Go将自动处理容量检查和复制。
func compareMapAlternate(X, Y []int) []int {
    counts := make(map[int]int)
    var total int
    for _, val := range X {
        counts[val] += 1
        total += 1
    }
    for _, val := range Y {
        if count := counts[val]; count > 0 {
            counts[val] -= 1
            total -= 1
        }
    }
    difference := make([]int, total)
    i := 0
    for val, count := range counts {
        for j := 0; j < count; j++ {
            difference[i] = val
            i++
        }
    }
    return difference
}
Case: len(X)== 10000 && len(Y)== 10000
--compareMap time: 4.0024ms
--compareMapAlternate time: 3.0225ms
--compareSort time: 4.9846ms
--compareSorted time: 1ms
--Result length == 6754 6754 6754 6754
Case: len(X)== 1000000 && len(Y)== 1000000
--compareMap time: 378.2492ms
--compareMapAlternate time: 387.2955ms
--compareSort time: 816.5619ms
--compareSorted time: 28.0432ms
--Result length == 673505 673505 673505 673505
Case: len(X)== 10000 && len(Y)== 1000000
--compareMap time: 35.0269ms
--compareMapAlternate time: 43.0492ms
--compareSort time: 385.2629ms
--compareSorted time: 3.0242ms
--Result length == 3747 3747 3747 3747
Case: len(X)== 1000000 && len(Y)== 10000
--compareMap time: 247.1561ms
--compareMapAlternate time: 240.1727ms
--compareSort time: 400.2875ms
--compareSorted time: 17.0311ms
--Result length == 993778 993778 993778 993778
package main

import "fmt"

func main() {
    X := []int{10, 12, 12, 12, 13}
    Y := []int{12, 14, 15}

    fmt.Println(compare(X, Y))
    fmt.Println(compare(Y, X))
}

func compare(X, Y []int) []int {
    m := make(map[int]int)

    for _, y := range Y {
        m[y]++
    }

    var ret []int
    for _, x := range X {
        if m[x] > 0 {
            m[x]--
            continue
        }
        ret = append(ret, x)
    }

    return ret
}