【问题标题】:Algorithm to count the time that occured on the same period计算同一时期发生的时间的算法
【发布时间】:2012-04-02 11:43:56
【问题描述】:

我已经为这个问题做了一个算法,但它仍然有问题。

假设我有时间表的回顾(包含在名为 GV 的 gridview 上):

TimeStart  TimeEnd   TotalOccuredOnThisTime
----------------------------------------------
08.00      08.50     1
08.00      09.40     43
08.00      10.50     2

请看下面的图片以获得更清晰的信息:

我想从算法中得到的是,计算同一时期发生的时间,例如08:00 发生了 46 个事件(见黄色行)。

这是我的算法:

Dim ColumnLength As Integer = GV.Rows.Count
Dim TimeStart(ColumnLength - 1) As Integer
Dim TimeEnd(ColumnLength - 1) As Integer
Dim Total(ColumnLength - 1) As Integer

For i = 0 to ColumnLength - 1
  TimeStart(i) = Convert.ToInt32(Regex.Replace(GV.Rows(i).Cells(0).Text, ".", ""))
  TimeEnd(i) = Convert.ToInt32(Regex.Replace(GV.Rows(i).Cells(1).Text, ".", ""))
  Total(i) = 0
Next 

For i = 0 To ColumnLength - 2
  For j = 0 To ColumnLength - 1        
    If TimeEnd(i) > TimeStart(j) And ((TimeStart(i) <= TimeEnd(j)) Or (TimeStart(i) >= TimeEnd(j))) 
      Total(j) = Total(j) - Total(i)
    End If
  Next
Next

但它导致了错误的值。

我想要的结果是这样的:

Time: 08.00 - 08.50   --> 46 event occured
      08.50 - 09.40   --> 46 event occured
      09.40 - 10.00   --> 45 event occured
      etc...

如何正确地做到这一点?真的需要你的帮助...

【问题讨论】:

  • ((TimeStart(i) &lt;= TimeEnd(j)) Or (TimeStart(i) &gt;= TimeEnd(j)) 总是正确的
  • 您应该向我们展示更多您的代码,ColumnLengthTimeStartTimeEndTotal 是什么?
  • @TimSchmelter:我已经编辑了这个问题。请看一下。谢谢 :)
  • @mrjimoy_05:这是 Winforms 还是 ASP.NET?为什么直接使用GridView而不是DataSource,一般情况下应该使用dbms来做这样的计算。但即使你想在客户端上做,最好还是使用底层的 DataSource(例如 DataTable)。

标签: vb.net algorithm


【解决方案1】:

您可以使用Dictionary 来跟踪时间范围,方法是将Key 的两个时间字符串连接在一起(无需转换为整数),然后对于该范围的每次出现,您可以递增ValueDictionary 的一部分。

此外,使用此方法无需使用嵌套循环,也无需使用暂存循环来填充开始和结束时间数组。以下算法应提供您正在寻找的结果:

Dim columnLength As Integer = GV.Rows.Count
Dim timeRangeCount As New Dictionary(Of String, Integer)

For i = 0 to columnLength - 1
  Dim timeStart As String = GV.Rows(i).Cells(0).Text
  Dim timeEnd As String = GV.Rows(i).Cells(1).Text
  Dim key As String = String.Format("{0}-{1}", timeStart, timeEnd)

  If timeRangeCount.ContainsKey(key)
      timeRangeCount(key) += 1
  Else
      timeRangeCount(key) = 1
  End If
Next

因此,您可以通过以下方式从字典中提取时间范围 09.40 - 10.00 的计数:

Dim totalOccuredOnThisTime As Integer = timeRangeCount("09.40-10.00")

我已经用类似的测试代码验证了这个算法:

Dim columnLength As Integer = 6
Dim timeStart() As String = {"08.00", "08.00", "10.00", "08.00", "08.00", "10.00"}
Dim timeEnd() As String = {"08.50", "08.50", "11.00", "09.00", "08.50", "11.00"}
Dim timeRangeCount As New Dictionary(Of String, Integer)

For i = 0 to columnLength - 1
  Dim key As String = String.Format("{0}-{1}", timeStart(i), timeEnd(i))

  If timeRangeCount.ContainsKey(key)
      timeRangeCount(key) += 1
  Else
      timeRangeCount(key) = 1
  End If
Next

Console.WriteLine(timeRangeCount)

更新

在阅读了您在评论中的说明后,您仍然可以使用 Dictionary 以及几个辅助类(TimeRangeTimeRangeCounterTimeRangeEqualityComparer)来帮助跟踪这一点。不过,这确实使事情变得更复杂了。

第一个帮助类用于保存精确匹配和子范围匹配的计数:

Public Class TimeRangeCounter
    Property ExactRangeMatch as Integer
    Property SubRangeMatch as Integer
End Class

第二个帮助类用于帮助字典了解一个键(TimeRange 类型)与另一个键有何不同:

Public Class TimeRangeEqualityComparer 
    Implements IEqualityComparer(Of TimeRange)

    Public Overloads Function Equals(left As TimeRange, right As TimeRange) _
            As Boolean Implements IEqualityComparer(Of TimeRange).Equals           

        Return left.ToString = right.ToString   
    End Function

    Public Overloads Function GetHashCode(range As TimeRange) _
            As Integer Implements IEqualityComparer(Of TimeRange).GetHashCode

        return range.ToString().GetHashCode()
    End Function

End Class

第三个助手类存储范围的开始和结束时间:

Public Class TimeRange 
    Private readonly _start
    Private readonly _end

    Public Readonly Property Start 
        Get
           return _start
        End Get
    End Property

    Public Readonly Property [End] 
        Get
           return _end
        End Get
    End Property

    Public Sub New(start As String, [end] As string)
        Me._start = start
        Me._end = [end]
    End Sub

    Public Overrides Function ToString() as String
       Return String.Format("{0}-{1}", Start, [End])
    End Function

End Class

所以使用上面我们应该可以写出这个算法:

Dim columnLength As Integer = 5
Dim timeStart() As String = {"08.00", "08.00", "10.00", "08.00", "08.00"}
Dim timeEnd() As String = {"08.50", "11.50", "11.00", "09.00", "08.50"}
Dim comparer As New TimeRangeEqualityComparer()
Dim timeRangeCounts As New Dictionary(Of TimeRange, TimeRangeCounter)(comparer)

'Count exact range matches while building dictionary
For i = 0 to columnLength - 1
  Dim key As TimeRange = New TimeRange(timeStart(i), timeEnd(i))

  If timeRangeCounts.ContainsKey(key)
      timeRangeCounts(key).ExactRangeMatch += 1
  Else
      Dim counter =  New TimeRangeCounter()
      counter.ExactRangeMatch = 1
      timeRangeCounts(key) = counter
  End If        

Next           

'Count sub ranges          
For Each kvp in timeRangeCounts
    For Each key in timeRangeCounts.Keys
        If kvp.key.Start >= key.Start AndAlso _ 
           kvp.Key.End <= key.End AndAlso _
           kvp.key.ToString <> key.ToString then           

            kvp.Value.SubRangeMatch += 1
        End If
    Next
Next

Console.WriteLine(timeRangeCounts)

【讨论】:

  • 嗨,马特,这对我来说真的是一种新的方式。谢谢,太棒了! :) 但这并没有解决问题。是否有任何可能的方法来计算特定范围内发生的事件,即有一个键:08.00-08.50, 08.00-09.40。那么:(1) 我想统计08.00-08.50之间发生的事件,所以应该返回value:2 --> 因为08.00-08.5008.00-09.40在@987654343的范围内@。 (2) 08.50-09.40 之间应该返回 1 --> 因为 08.00-09.40 仍在范围内。有什么办法吗?
  • @mrjimoy_05:我已经更新了我的答案,以考虑完全匹配以及给定时间范围恰好是其他时间范围的子范围。
  • 感谢您的回复。我会尽快检查代码,并让您知道它是否有效...... :)
  • 嗨,马特,感谢您的帮助。它帮助我解决了这个问题。谢谢! :)
  • @pnuts,如果我没记错的话(这个答案是 6 岁),它只是从 LinqPad 内部运行时来自 Console.WriteLine(timeRangeCounts) 的输出图像。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-01-28
  • 2020-02-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-06-21
  • 1970-01-01
相关资源
最近更新 更多