【问题标题】:Is there an easy way to randomize a list in VB.NET?有没有一种简单的方法可以在 VB.NET 中随机化一个列表?
【发布时间】:2010-10-07 23:06:42
【问题描述】:

我有一个System.IO.FileInfo 类型的列表,我想随机化该列表。我想我记得不久前看到过类似list.randomize() 的东西,但我找不到我可能在哪里看到过。

我的第一次尝试让我有了这个功能:

Private Shared Sub GetRandom(ByVal oMax As Integer, ByRef currentVals As List(Of Integer))
    Dim oRand As New Random(Now.Millisecond)
    Dim oTemp As Integer = -1
    Do Until currentVals.Count = IMG_COUNT
        oTemp = oRand.Next(1, oMax)
        If Not currentVals.Contains(oTemp) Then currentVals.Add(oTemp)
    Loop
End Sub

我向它发送我希望它迭代的最大 val,以及对我想要随机化内容的列表的引用。变量 IMG_COUNT 在脚本中设置得更远,指定我想要多少随机图像显示出来。

谢谢大家,我很感激:D

【问题讨论】:

    标签: vb.net visual-studio-2008 list random


    【解决方案1】:

    在此处查看 Fisher-Yates 洗牌算法:http://en.wikipedia.org/wiki/Knuth_shuffle

    这个网站的主要霸主在这里进行了更简洁的讨论: http://www.codinghorror.com/blog/archives/001015.html

    博客条目中有一个简单的 C# 实现,应该很容易更改为 VB.NET

    【讨论】:

    • 很棒的链接,我一定会去看看!
    【解决方案2】:

    我使用以下 Randomize() 函数扩展了 List 类以使用 Fisher-Yates shuffle 算法:

    ''' <summary>
    ''' Randomizes the contents of the list using Fisher–Yates shuffle (a.k.a. Knuth shuffle).
    ''' </summary>
    ''' <typeparam name="T"></typeparam>
    ''' <param name="list"></param>
    ''' <returns>Randomized result</returns>
    ''' <remarks></remarks>
    <Extension()>
    Function Randomize(Of T)(ByVal list As List(Of T)) As List(Of T)
        Dim rand As New Random()
        Dim temp As T
        Dim indexRand As Integer
        Dim indexLast As Integer = list.Count - 1
        For index As Integer = 0 To indexLast
            indexRand = rand.Next(index, indexLast)
            temp = list(indexRand)
            list(indexRand) = list(index)
            list(index) = temp
        Next index
        Return list
    End Function
    

    【讨论】:

    • 我知道是 9 年后,但对于未来的观众来说,上面的代码有一个错误:rand.Next(index, indexLast) 应该是 rand.Next(0, indexLast)跨度>
    【解决方案3】:

    构建一个比较器:

    Public Class Randomizer(Of T)
        Implements IComparer(Of T)
    
        ''// Ensures different instances are sorted in different orders
        Private Shared Salter As New Random() ''// only as random as your seed
        Private Salt As Integer
        Public Sub New()
            Salt = Salter.Next(Integer.MinValue, Integer.MaxValue)
        End Sub
    
        Private Shared sha As New SHA1CryptoServiceProvider()
        Private Function HashNSalt(ByVal x As Integer) As Integer
          Dim b() As Byte = sha.ComputeHash(BitConverter.GetBytes(x))
          Dim r As Integer = 0
          For i As Integer = 0 To b.Length - 1 Step 4
              r = r Xor BitConverter.ToInt32(b, i)
          Next
    
          Return r Xor Salt
        End Function
    
        Public Function Compare(x As T, y As T) As Integer _
            Implements IComparer(Of T).Compare
    
            Return HashNSalt(x.GetHashCode()).CompareTo(HashNSalt(y.GetHashCode()))
        End Function
    End Class
    

    像这样使用它,假设你的意思是一个通用的List(Of FileInfo)

    list.Sort(New Randomizer(Of IO.FileInfo)())
    

    您也可以使用闭包使随机值“粘性”,然后在其上使用 linq 的 .OrderBy()(这次是 C#,因为 VB lambda 语法很难看):

    list = list.OrderBy(a => Guid.NewGuid()).ToList();
    

    在此解释,以及为什么它甚至可能不如真正的随机播放快:
    http://www.codinghorror.com/blog/archives/001008.html?r=31644

    【讨论】:

    • 我不断收到错误消息:“类 'Randomizer' 必须为接口 'System.Collections.Generic.IComparer(of T) 实现 'Function Compare(x as T,y as T) As Integer' ’。”仅使用您的第二个代码块就会出现此错误。
    • 请注意,使用第二个选项,该方法不需要存在于单独的类中,您可以通过 AddressOf 运算符使用它,如图所示,而不是通过创建类实例。
    • -1:只是一个糟糕的实现。该函数实际上并没有随机化任何东西,因为包含相同项目的两个列表将以相同的顺序“随机化”。此外,没有什么可以阻止顺序项具有顺序哈希码。编写这个函数有很多更好的方法。
    • 但是,我仍然相信这可以作为基本实现工作:您只需向 Randomizer 类添加一个额外的(私有)成员,该成员可以包含在计算结果中,以便不同的实例类将产生不同的结果。
    • 尽管Fisher-Yates在所有意义上都更好,但我不得不多玩一些,因为我可以看到希望能够交换多种不同排序之一的用处选项,包括随机,基于用户在运行时的选择。
    【解决方案4】:

    有几种合理的洗牌方法。

    已经提到过一个。 (Knuth Shuffle。)

    另一种方法是为每个元素分配一个“权重”并根据该“权重”对列表进行排序。这种方法是可行的,但会很笨拙,因为您不能从 FileInfo 继承。

    最后一种方法是在原始列表中随机选择一个元素并将其添加到新列表中。当然,也就是说,如果您不介意创建一个新列表。 (尚未测试此代码...)

    
            Dim rnd As New Random
            Dim lstOriginal As New List(Of FileInfo)
            Dim lstNew As New List(Of FileInfo)
    
            While lstOriginal.Count > 0
                Dim idx As Integer = rnd.Next(0, lstOriginal.Count - 1)
                lstNew.Add(lstOriginal(idx))
                lstOriginal.RemoveAt(idx)
            End While
    

    【讨论】:

      【解决方案5】:

      您也可以实现随机播放,有很多方法可以做到这一点,最简单的方法是随机选择一个项目并将其多次插入新位置。

      【讨论】:

        【解决方案6】:

        如果您有元素数量,则可以使用伪随机方法,您可以随机选择第一个元素(例如使用内置随机数函数),然后添加一个素数并在除以数后取余数价值观。例如对于 10 个列表,您可以执行 i = (i + prime) % 10 从某个起始值生成索引 i 。只要素数大于列表中值的数量,那么您就可以创建一个序列,该序列贯穿所有数字 0...n,其中 n 是值的数量 - 1,但以伪随机顺序排列。

        【讨论】:

          【解决方案7】:
          Dim oRand As New Random() 'do not seed!!!!
          Private Sub GetRandom(ByRef currentVals As List(Of Integer))
              Dim i As New List(Of Integer), j As Integer
              For x As Integer = 0 To currentVals.Count - 1
                  j = oRand.Next(0, currentVals.Count)
                  i.Add(currentVals(j))
                  currentVals.RemoveAt(j)
              Next
              currentVals = i
          End Sub
          

          【讨论】:

          • 如果您简单地将oRand.Next(0, currentVals.Count) 替换为oRand.Next(x, currentVals.Count),您将获得更好的随机分布。见Jeff Atwood's insightful blog post
          【解决方案8】:

          您可以创建只返回随机数的自定义比较器,然后使用此比较器对列表进行排序。它可能效率极低并导致几乎无限循环,但可能值得一试。

          【讨论】:

          • 不,您肯定想将 array.Sort() 与一些自定义比较器一起使用。只是如何实现比较器的问题-> 可能基于每个对象的 GetHashCode() 值。
          • 您知道自定义比较器有什么好的资源吗?我最近没怎么看那些鸭子
          • 实际上,这是一个错字:我的意思是比较器。示例即将发布。
          • -1:这是另一个糟糕的实现。如果两个元素返回不一致的排序,.NET 将引发“Array.Sort 无法与值进行比较”异常。例如,如果 A > B,并且 B > C,但是 A 和 C 一起比较表明 C > A,那么我们就有问题了。
          猜你喜欢
          • 2023-04-06
          • 1970-01-01
          • 1970-01-01
          • 2019-06-15
          • 1970-01-01
          • 2010-09-24
          • 2012-02-25
          • 2011-02-17
          相关资源
          最近更新 更多