有没有一种简单的方法可以随机化VB.NET中的列表?

时间:2009-02-16 21:15:13

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

我有一个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

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

谢谢大家,我很感激:D

8 个答案:

答案 0 :(得分:14)

在这里查看Fisher-Yates shuffle算法:http://en.wikipedia.org/wiki/Knuth_shuffle

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

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

答案 1 :(得分:5)

我使用以下List函数扩展了Randomize()类,以使用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

答案 2 :(得分:2)

建立一个比较者:

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)())

你也可以使用一个闭包来使随机值'sticky',然后只使用linq的.OrderBy()(这次是C#,因为VB lambda语法很难看):

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

这里解释一下,为什么它甚至可能不如真正的洗牌一样快:
http://www.codinghorror.com/blog/archives/001008.html?r=31644

答案 3 :(得分:2)

有几种合理的改组方法。

已经提到了一个。 (The 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

答案 4 :(得分:1)

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

答案 5 :(得分:0)

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

答案 6 :(得分:0)

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

答案 7 :(得分:-2)

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