当前位置: 首页 > 知识库问答 >
问题:

间隔和一组间隔之间的区别?

梁豪
2023-03-14

我有一组不重叠的,不相邻的区间,例如[{10,15},{30,35},{20,25}]。它们没有排序,但如果需要,我可以对它们进行排序。

现在,我得到了一些新的区间,例如{5,32},并希望生成一组新的区间来描述差异:这个新区间所覆盖的范围不在该集合中。在这个例子中,答案是:[{5,9},{16,19},{26,29}]。

计算这个的快速算法是什么?请注意,集合中通常有1个,有时有2个,很少有3个或更多项目,因此我想针对这种情况进行优化

对于上下文,下面是从输入的开始-结束数据流中最初创建集合的代码,我在其中进行合并:

type Interval struct {
    start int
    end   int
}

func (i *Interval) OverlapsOrAdjacent(j Interval) bool {
    return i.end+1 >= j.start && j.end+1 >= i.start
}

func (i *Interval) Merge(j Interval) bool {
    if !i.OverlapsOrAdjacent(j) {
        return false
    }
    if j.start < i.start {
        i.start = j.start
    }
    if j.end > i.end {
        i.end = j.end
    }
    return true
}

type Intervals []Interval

func (ivs Intervals) Len() int           { return len(ivs) }
func (ivs Intervals) Swap(i, j int)      { ivs[i], ivs[j] = ivs[j], ivs[i] }
func (ivs Intervals) Less(i, j int) bool { return ivs[i].start < ivs[j].start }

func (ivs Intervals) Merge(iv Interval) Intervals {
    ivs = append(ivs, iv)
    merged := make(Intervals, 0, len(ivs))
    for _, iv := range ivs {
        for i := 0; i < len(merged); {
            if iv.Merge(merged[i]) {
                merged = append(merged[:i], merged[i+1:]...)
            } else {
                i++
            }
        }
        merged = append(merged, iv)
    }
    return merged
}

func (ivs Intervals) MergeUsingSort(iv Interval) Intervals {
    ivs = append(ivs, iv)
    sort.Sort(ivs)
    merged := make(Intervals, 0, len(ivs))
    merged = append(merged, ivs[0])
    for i := 1; i < len(ivs); i++ {
        last := len(merged) - 1
        if !merged[last].Merge(ivs[i]) {
            merged = append(merged, ivs[i])
        }
    }
    return merged
}

func (ivs Intervals) Difference(iv Interval) Intervals {
    // ???
    return ivs
}

func main() {
    var ivs Intervals
    for _, input := range inputsFromSomewhere { // in reality, I don't have all these inputs at once, they come in one at a time
        iv := Interval{input.start, input.end}
        diffs := ivs.Difference(iv) // not yet implemented...
        // do something with diffs
        ivs = ivs.Merge(iv)
    }
}

我发现上述间隔。Merge()比MergeUsingSort()快2倍,所以我想知道是否还有一种简单的非排序方式来回答我的问题。

共有2个答案

朱阳曜
2023-03-14

为了回答我自己的问题,这里是我对Difference()的实现,它(在我的输入数据上)比JimB要求排序的建议更快。

func (i *Interval) Overlaps(j Interval) bool {
    return i.End >= j.Start && j.End >= i.Start
}

func (i *Interval) Difference(j Interval) (left *Interval, right *Interval, overlapped bool) {
    if !i.Overlaps(j) {
        return
    }

    overlapped = true
    if j.Start < i.Start {
        left = &Interval{j.Start, i.Start - 1}
    }
    if j.End > i.End {
        right = &Interval{i.End + 1, j.End}
    }
    return
}

func (ivs Intervals) Difference(iv Interval) (diffs Intervals) {
    diffs = append(diffs, iv)
    for _, prior := range ivs {
        for i := 0; i < len(diffs); {
            if left, right, overlapped := prior.Difference(diffs[i]); overlapped {
                if len(diffs) == 1 {
                    diffs = nil
                } else {
                    diffs = append(diffs[:i], diffs[i+1:]...)
                }

                if left != nil {
                    diffs = append(diffs, *left)
                }
                if right != nil {
                    diffs = append(diffs, *right)
                }
            } else {
                i++
            }
        }
        if len(diffs) == 0 {
            break
        }
    }
    return
}

它适用于我尝试过的数据,尽管我有点担心我可能错过了一个边缘案例,因为它得到了错误的答案。

后学
2023-03-14

问答代码不完整,无法编译。没有基准。快速浏览一下代码,它可能效率很低。

间隔的可用代码。进行间隔测试。go来自https://github.com/VertebrateResequencing/wr/tree/develop/minfys.

让我们首先为间隔差示例编写一个基准测试。

package minfys

import (
    "fmt"
    "testing"
)

// Example
var (
    xA = Intervals{{10, 15}, {30, 35}, {20, 25}}
    xB = Interval{5, 32}
    xD = Intervals{{5, 9}, {16, 19}, {26, 29}}
    xR = Intervals{}
)

func BenchmarkExample(b *testing.B) {
    b.ReportAllocs()
    a := make(Intervals, len(xA))
    b.ResetTimer()
    for i := 0; i < b.N; i++ {
        copy(a, xA)
        xR = a.Difference(xB)
    }
    b.StopTimer()
    if fmt.Sprint(xD) != fmt.Sprint(xR) {
        b.Fatal(xD, xR)
    }
}

接下来,编写一个差分方法。

package minfys

func (a Intervals) Difference(b Interval) Intervals {
    // If A and B are sets, then the relative complement of A in B
    // is the set of elements in B but not in A.
    // The relative complement of A in B is denoted B ∖  A:
    //     B \ A = {x ∈ A | x ∉ B}
    //     B \ A = B ∩ A'
    //
    // For example. d = a\b,
    //     a: [{10, 15}, {30, 35}, {20, 25}]
    //     b: {5,32}
    //     d: [{5,9}, {16,19}, {26,29}]
    // The elements of set a are non-overlapping, non-adjacent,
    // and unsorted intervals.

    if len(a) <= 0 {
        return Intervals{b}
    }

    d := make(Intervals, 0, 3)
    for ; len(a) > 0; a = a[1:] {
        for i := 1; i < len(a); i++ {
            if a[i].Start < a[0].Start {
                a[i], a[0] = a[0], a[i]
            }
        }

        if b.Start < a[0].Start {
            if b.End < a[0].Start {
                d = append(d, b)
                break
            }
            d = append(d, Interval{b.Start, a[0].Start - 1})
            b.Start = a[0].Start
        }
        if b.End <= a[0].End {
            break
        }
        if b.Start <= a[0].End {
            b.Start = a[0].End + 1
        }
        if len(a) == 1 {
            if b.Start <= a[0].End {
                b.Start = a[0].End + 1
            }
            d = append(d, b)
            break
        }
    }
    return d
}

现在,对差分法进行基准测试。

BenchmarkExample-4     20000000     62.4 ns/op    48 B/op      1 allocs/op

sbs写了一个差异方法。

// Interval struct is used to describe something with a start and end. End must
// be greater than start.
type Interval struct {
    Start int64
    End   int64
}

// Overlaps returns true if this interval overlaps with the supplied one.
func (i *Interval) Overlaps(j Interval) bool {
    // https://nedbatchelder.com/blog/201310/range_overlap_in_two_compares.html
    return i.End >= j.Start && j.End >= i.Start
}

// Intervals type is a slice of Interval.
type Intervals []Interval

// Difference returns any portions of iv that do not overlap with any of our
// intervals. Assumes that all of our intervals have been Merge()d in.
func (ivs Intervals) Difference(iv Interval) (diffs Intervals) {
    diffs = append(diffs, iv)
    for _, prior := range ivs {
        for i := 0; i < len(diffs); {
            if left, right, overlapped := prior.Difference(diffs[i]); overlapped {
                if len(diffs) == 1 {
                    diffs = nil
                } else {
                    diffs = append(diffs[:i], diffs[i+1:]...)
                }

                if left != nil {
                    diffs = append(diffs, *left)
                }
                if right != nil {
                    diffs = append(diffs, *right)
                }
            } else {
                i++
            }
        }
        if len(diffs) == 0 {
            break
        }
    }

    return
}

基准sbs的差分法。

BenchmarkExample-4      5000000    365 ns/op     128 B/op      4 allocs/op

彼得索的差分法要快得多。

old.txt (sbs) versus new.txt (peterSO):

benchmark              old ns/op     new ns/op     delta
BenchmarkExample-4     365           62.4          -82.90%

benchmark              old allocs     new allocs   delta
BenchmarkExample-4     4              1            -75.00%

benchmark              old bytes     new bytes     delta
BenchmarkExample-4     128           48            -62.50%

这只是一个开始。可能还有其他改进。

interval_test.go中有一些错误。ShouldBeNil代表指针;ShouldBeAir ty代表集合。ShouldResemble不处理集合相等(包含相同元素的两个集合是相同的集合)。更改ShouldResemble顺序以匹配实现相关顺序。

$ go test
..........................................................................................................................x......................................................x................x
Failures:

  * interval_test.go 
  Line 247:
  Expected: nil
  Actual:   '[]'

  * interval_test.go 
  Line 375:
  Expected: 'minfys.Intervals{minfys.Interval{Start:5, End:6}, minfys.Interval{Start:31, End:32}, minfys.Interval{Start:11, End:14}, minfys.Interval{Start:19, End:19}}'
  Actual:   'minfys.Intervals{minfys.Interval{Start:5, End:6}, minfys.Interval{Start:11, End:14}, minfys.Interval{Start:19, End:19}, minfys.Interval{Start:31, End:32}}'
  (Should resemble)!

  * interval_test.go 
  Line 413:
  Expected: 'minfys.Intervals{minfys.Interval{Start:7, End:10}, minfys.Interval{Start:1, End:3}, minfys.Interval{Start:15, End:17}}'
  Actual:   'minfys.Intervals{minfys.Interval{Start:1, End:3}, minfys.Interval{Start:7, End:10}, minfys.Interval{Start:15, End:17}}'
  (Should resemble)!


195 total assertions

...
198 total assertions

--- FAIL: TestIntervals (0.04s)
FAIL

.

$ diff -a -u ../interval_test.go interval_test.go
--- ../interval_test.go 2017-04-29 20:23:29.365344008 -0400
+++ interval_test.go    2017-04-29 20:54:14.349344903 -0400
@@ -244,19 +244,19 @@
            So(len(ivs), ShouldEqual, 1)

            newIvs = ivs.Difference(twoSix)
-           So(newIvs, ShouldBeNil)
+           So(newIvs, ShouldBeEmpty)
            ivs = ivs.Merge(twoSix)
            So(len(ivs), ShouldEqual, 1)

            newIvs = ivs.Difference(oneThree)
-           So(newIvs, ShouldBeNil)
+           So(newIvs, ShouldBeEmpty)
            ivs = ivs.Merge(oneThree)
            So(len(ivs), ShouldEqual, 1)

            oneSeven := Interval{1, 7}

            newIvs = ivs.Difference(oneSix)
-           So(newIvs, ShouldBeNil)
+           So(newIvs, ShouldBeEmpty)
            ivs = ivs.Merge(oneSix)
            So(len(ivs), ShouldEqual, 1)

@@ -372,7 +372,7 @@

            fiveThirtyTwo := Interval{5, 32}
            newIvs = ivs.Difference(fiveThirtyTwo)
-           So(newIvs, ShouldResemble, Intervals{Interval{5, 6}, Interval{31, 32}, Interval{11, 14}, Interval{19, 19}})
+           So(newIvs, ShouldResemble, Intervals{Interval{5, 6}, Interval{11, 14}, Interval{19, 19}, Interval{31, 32}})
            ivs = ivs.Merge(fiveThirtyTwo)
            So(len(ivs), ShouldEqual, 3)

@@ -409,7 +409,7 @@

            ivs = ivs.Truncate(17)

-           expected := Intervals{sevenTen, oneThree, Interval{15, 17}}
+           expected := Intervals{oneThree, sevenTen, Interval{15, 17}}
            So(ivs, ShouldResemble, expected)
        })
    })

.

$ go test
.............................................................................................................................................................................................................
205 total assertions

...
208 total assertions

PASS
$ 

我[@sbs]确认它比我的解决方案快。尽管如果你只是测量使用差异()所花费的墙时间(在interval_test.go中最后一个差异()调用之前加上一个:=时间。Now()在它之后,并在循环中求和这些持续时间),它似乎没有什么惊人的差异(在我的机器上,我的解决方案需要大约31毫秒,你的解决方案需要大约29毫秒)。

根据要求,进行间隔测试。go修改为测量墙壁时间:

$ diff -a -u ../interval_test.go walltime_test.go
--- ../interval_test.go 2017-04-29 20:23:29.365344008 -0400
+++ walltime_test.go    2017-04-30 13:39:29.000000000 -0400
@@ -24,6 +24,7 @@
    "math/rand"
    "testing"
    "time"
+   "fmt"
 )

 func TestIntervals(t *testing.T) {
@@ -459,16 +460,20 @@

        var ivs Intervals
        errors := 0
+       var diffTime time.Duration
        t := time.Now()
        for i, input := range inputs {
            iv := NewInterval(int64(input), int64(readSize))
+           before := time.Now()
            newIvs := ivs.Difference(iv)
+           diffTime += time.Since(before)
            if (len(newIvs) == 1) != exepectedNew[i] {
                errors++
            }
            ivs = ivs.Merge(iv)
        }
-       // fmt.Printf("\ntook %s\n", time.Since(t))
+       fmt.Printf("took %s\n", time.Since(t))
+       fmt.Printf("\n  Difference took %s\n", diffTime)
        So(errors, ShouldEqual, 0)
        So(len(ivs), ShouldEqual, 1)
        So(time.Since(t).Seconds(), ShouldBeLessThan, 1) // 42ms on my machine
$ 

间隔测试。go基准输入大小和频率

size    frequency
0       1
1       94929
2       50072
3       4998

输出大小和频率为

size    frequency
0       50000
1       100000

调整peterSo对此分布的差分方法

package minfys

func (a Intervals) Difference(b Interval) Intervals {
    // If A and B are sets, then the relative complement of A in B
    // is the set of elements in B but not in A.
    // The relative complement of A in B is denoted B ∖  A:
    //     B \ A = {x ∈ A | x ∉ B}
    //     B \ A = B ∩ A'
    //
    // For example. d = a\b,
    //     a: [{10, 15}, {30, 35}, {20, 25}]
    //     b: {5,32}
    //     d: [{5,9}, {16,19}, {26,29}]
    // The elements of set a are non-overlapping, non-adjacent,
    // and unsorted intervals.

    if len(a) <= 0 {
        return Intervals{b}
    }

    var d Intervals
    for ; len(a) > 0; a = a[1:] {
        for i := 1; i < len(a); i++ {
            if a[i].Start < a[0].Start {
                a[i], a[0] = a[0], a[i]
            }
        }

        if b.Start < a[0].Start {
            if b.End < a[0].Start {
                d = append(d, b)
                break
            }
            d = append(d, Interval{b.Start, a[0].Start - 1})
            b.Start = a[0].Start
        }
        if b.End <= a[0].End {
            break
        }
        if b.Start <= a[0].End {
            b.Start = a[0].End + 1
        }
        if len(a) == 1 {
            if b.Start <= a[0].End {
                b.Start = a[0].End + 1
            }
            d = append(d, b)
            break
        }
    }
    return d
}

运行间隔测试。peterSO和sbs的差分方法的基准测试

$ go test -v

  Merging many intervals is fast took 26.208614ms

  Difference took 10.706858ms

$ go test -v

  Merging many intervals is fast took 30.799216ms

  Difference took 14.414488ms

PeterSO的差异方法明显快于sbs的:10.706858ms对14.414488ms或负25.7%。

更新peterSO修正差分法的早期示例基准结果

old.txt (sbs) versus new.txt (peterSO):

benchmark              old ns/op     new ns/op     delta
BenchmarkExample-4     365           221           -39.45%

benchmark              old allocs     new allocs   delta
BenchmarkExample-4     4              3            -25.00%

benchmark              old bytes     new bytes     delta
BenchmarkExample-4     128           112           -12.50%
 类似资料:
  • 时间间隔:一条链上相邻区块的时间差。时间间隔越小,出块速度越快,TPS就越高。 本系统中,时间最小单位为1毫秒。 第一条链的时间间隔为1分钟,新链的时间间隔降为其父链的15/16,所以第二条链的时间间隔为56250毫秒。 新链有更小的时间间隔,出块速度更快。 区块的时间间隔可以根据需要调整,最大为1分钟。

  • 间隔有开始和结束时间。间隔可能重叠。可能有几个包含时间t的间隔。只返回其中一个就可以了。 这是一个面试问题。我能够解决这个问题,方法是根据结束对间隔进行排序,根据开始对另一个时间进行排序,并取具有匹配开始和结束的间隔的交点。显然有更多优化的解决方案。 这里有一个例子:[1,5][2,10][3,6][2,9],目标是8。在这种情况下,[2,10]和[2,9]中的任何一个都是正确答案。 我想问题的关

  • 问题内容: 我需要将表分组为15分钟间隔。我可以这样做: 但是要在图表中显示返回的数据,我还需要插入没有任何数据且当前未出现在我的select语句中的间隔。我该如何插入这些? 问题答案: 用15分钟的增量创建一个带有所有可能时间戳的表,然后从该表向上面的查询进行LEFT JOIN。 如果您知道图表始终涵盖24小时,则只需创建一个数字为0-95的表格,然后为每个条目将其添加到图表的开始时间。

  • 让事件反复发生 用法 Your browser does not support the video tag. 案例:坏掉的小台灯 功能:不停闪烁 工作原理 在配置项中可以选择每隔多少秒反转一次 例:制作一个闪烁的灯 例:温度过高时会发出“哔哔”声报警

  • # interval(date) Alias for interval.floor(date). For example, d3.time.day(new Date()) returns midnight (12:00 AM) on the current day, in local time. # interval.floor(date) Rounds down the specified da

  • 假设我有一个这样的范围列表 现在我想找到一个范围,比如。我的算法应该给我这个范围的所有范围。例如,这个的输出应该是 <代码>输出-[1,3]、[2,5]、[4,6]、[8,10] 我该如何着手解决这个问题? PS:我知道段树可能会有所帮助。我可以在其中构建树来存储间隔并查询位于间隔内的Point,但如何在给定间隔的情况下获取所有间隔。