我有一个{0,1,2,3}
的数组,想要改组它。 This工作得非常好
Public Function ShuffleArray(ByVal items() As Integer) As Integer()
Dim ptr As Integer
Dim alt As Integer
Dim tmp As Integer
Dim rnd As New Random()
ptr = items.Length
Do While ptr > 1
ptr -= 1
alt = rnd.Next(ptr - 1)
tmp = items(alt)
items(alt) = items(ptr)
items(ptr) = tmp
Loop
Return items
End Function
有时候。但是,我发现它经常产生一堆{1,2,3,0}
,其中0
只是放在堆栈的背面。实际上,通常情况下这根本不是随机的。不需要“新随机阵列中3的原始序列”。
无论如何要改进这一点,以便:
阵列中可能有6个项目或10个项目,但我目前正在使用的只有4个项目。 C#或VB.net都没问题。
答案 0 :(得分:2)
看起来你正在尝试Fisher-Yates随机播放,但对Next
的调用不正确。拨打Next
的电话应为rnd.Next(ptr + 1)
。当您使用ptr - 1
调用它时,您只为4个项目的序列生成两个排列。起始序列为[0 1 2 3],其中一个序列为[1 2 3 0]。请参阅下表以获取解释。
ptr ptr - 1 alt Permutations Remarks
--- ------- --- ------------ -------
4 [0 1 2 3] Starting condition
3 2 1 or 0 [0 3 2 1] or [3 1 2 0] First pass
2 1 0 [2 3 0 1] or [2 1 3 0] Second pass
1 0 0 [3 2 0 1] or [1 2 3 0] Final pass
alt
两次为0的原因是Random.Next(0)
返回0
。
编辑:使用rnd.Next(ptr)
,如CoderDennis所述,而不是rnd.Next(ptr + 1)
可能更接近您的要求,因为它可以更好地将数字移动到新位置。当您使用rnd.Next(ptr + 1)
时,您会获得更多排列,但是对于每个可能的循环,您可能无法执行任何可能在其原始位置留下数字的交换(取决于序列中的位置和其他交换)。
答案 1 :(得分:2)
我相信以下内容符合要求。我将@ CoderDennis的修正包含在初始随机值中,并传入随机数。我的VB技能在C#和JavaScript中已经被多年玷污了,所以对任何明显的语法错误表示道歉。
它只过滤掉三个连续项目的序列,而不是“(或任意数量的连续原始项目)”。
Public Function ShuffleArray(ByVal items() As Integer, ByVal rnd As Random) As Integer()
Dim original as Integer() = items.ToArray()
Dim ptr As Integer
Dim alt As Integer
Dim tmp As Integer
Dim stacksOfThree = new List(Of Integer())
Dim isGood As Boolean = True
ptr = items.Length
Do While ptr > 2
ptr -= 1
stacksOfThree.Add(new Integer() { items(ptr - 2), items(ptr - 1), items(ptr) })
Loop
ptr = items.Length
Do While ptr > 1
ptr -= 1
alt = rnd.Next(ptr)
tmp = items(alt)
While items(alt).Equals(items(ptr)) Or items(ptr).Equals(tmp)
alt = rnd.Next(ptr)
tmp = items(alt)
End While
items(alt) = items(ptr)
items(ptr) = tmp
Loop
ptr = items.Length
Do While ptr > 1
ptr -= 1
If items(ptr).Equals(original(ptr)) Then
isGood = False
Exit Do
End If
Loop
If isGood Then
ptr = items.Length
Do While ptr > 2
ptr -= 1
For Each stack In stacksOfThree
If stack(2).Equals(items(ptr)) And stack(1).Equals(items(ptr - 1)) And stack(0).Equals(items(ptr - 2)) Then
isGood = False
Exit For
End If
Next
If Not isGood Then
Exit Do
End If
Loop
End If
If isGood Then
Return items
Else
Return ShuffleArray(original, new Random())
End If
End Function
答案 2 :(得分:2)
每个人都在解决你的问题并错过了实际问题。
通过这样的约束,我会简单地进行洗牌,然后测试结果是否符合标准,如果没有,则再次进行洗牌。遗憾的是,这有一个不确定的运行时间,但只要约束不太可能拒绝它,真实世界的性能通常是可以接受的。
然而,在这种特殊情况下,我会采取完全不同的方法。列表中有4个项目,只有24种可能的排列,其中4种肯定无效。 (我不确定你是否想要[0,1,3,2]之类的东西。)因此,我将存储列表的所有有效排列,对列表进行排序,从预先计算的列表中选择随机排列并相应地“洗牌”。
答案 3 :(得分:2)
包含3个连续项目的堆栈(绝不允许原始序列)
我假设shuffle(n)的结果是用作shuffle(n + 1)的起始序列。这非常简单,因为使用相同的开始系列只会导致{0, 1, 2, 3}
的7个有效组合。应用程序启动时使用固定的启动顺序意味着第一个shuffle只能是其中一个(可能足够多)。
Scrambler类:
Public Class Scrambler
Private rand As Random
Public Sub New()
rand = New Random
End Sub
' FY In-Place integer array shuffle
Public Sub Shuffle(items() As Integer)
Dim tmp As Integer
Dim j As Integer
' hi to low, so the rand result is meaningful
For i As Integer = items.Length - 1 To 0 Step -1
j = rand.Next(0, i + 1) ' NB max param is EXCLUSIVE
tmp = items(j)
' swap j and i
items(j) = items(i)
items(i) = tmp
Next
End Sub
' build a list of bad sequences
' fullfils the "stack of 3 sequential items (from the original sequence..." requirement
' nsize - allows for the "(or any number ..." portion though scanning for
' a series-of-5 may be fruitless
Public Function GetBadList(source As Integer(),
nSize As Integer) As List(Of String)
Dim BList As New List(Of String)
Dim badNums(nSize - 1) As Integer
For n As Integer = 0 To source.Length - nSize
Array.Copy(source, n, badNums, 0, badNums.Length)
BList.Add(String.Join(",", badNums))
Array.Clear(badNums, 0, badNums.Length)
Next
Return BList
End Function
Public Function ScrambleArray(items() As Integer, badSize As Integer) As Integer()
' FY is an inplace shuffler, make a copy
Dim newItems(items.Length - 1) As Integer
Array.Copy(items, newItems, items.Length)
' flags
Dim OrderOk As Boolean = True
Dim AllDiffPositions As Boolean = True
Dim BadList As List(Of String) = GetBadList(items, badSize)
' build the bad list
Do
Shuffle(newItems)
' check if they all moved
AllDiffPositions = True
For n As Integer = 0 To items.Length - 1
If newItems(n) = items(n) Then
AllDiffPositions = False
Exit For
End If
Next
' check for forbidden sequences
If AllDiffPositions Then
Dim thisVersion As String = String.Join(",", newItems)
OrderOk = True
For Each s As String In BadList
If thisVersion.Contains(s) Then
OrderOk = False
Exit For
End If
Next
End If
Loop Until (OrderOk) And (AllDiffPositions)
Return newItems
End Function
End Class
测试代码/如何使用它:
' this series is only used once in the test loop
Dim theseItems() As Integer = {0, 1, 2, 3}
Dim SeqMaker As New Scrambler ' allows one RNG used
Dim newItems() As Integer
' reporting
Dim rpt As String = "{0} Before: {1} After: {2} time:{3}"
ListBox1.Items.Clear()
For n As Integer = 0 To 1000
sw.Restart()
newItems = SeqMaker.ScrambleArray(theseItems, 3) ' bad series size==3
sw.Stop()
ListBox1.Items.Add(String.Format(rpt, n.ToString("0000"), String.Join(",", theseItems),
String.Join(",", newItems), sw.ElapsedTicks.ToString))
Console.WriteLine(rpt, n.ToString("0000"), String.Join(",", theseItems),
String.Join(",", newItems), sw.ElapsedTicks.ToString)
' rollover to use this result as next start
Array.Copy(newItems, theseItems, newItems.Length)
Next
项目永远不会处于原始位置这对小集合有意义。但对于较大的集合,它排除了大量合法的混洗(> 60%);在某些情况下,仅仅因为1个项目在同一个位置。
Start: {1,2,8,4,5,7,6,3,9,0}
Result: {4,8,2,0,7,1,6,9,5,3}
由于'6'而失败,但它真的是无效的随机播放?三系列规则很少出现在较大的集合中(<1%),这可能是浪费时间。
如果没有列表框和控制台报告(以及某些分发收集未显示),则速度非常快。
Std Shuffle, 10k iterations, 10 elements: 12ms (baseline)
Modified, 10k iterations, 10 elements: 91ms
Modified, 10k iterations, 04 elements: 48ms
修改过的shuffle依赖于重新洗牌,我知道这不会耗费时间。因此,当Rule1 OrElse Rule2失败时,它只会重新洗牌。 10元素shuffle实际上必须执行28k shuffle才能获得10,000个“好”的shuffle。 4元素shuffle实际上具有更高的拒绝率,因为规则更容易被这么少的项目(34,000个拒绝)打破。
这对我来说几乎和洗牌分配一样不感兴趣,因为如果这些“改进”引入偏见,那就不好了。 10k 4元素分布:
seq: 3,2,1,0 count: 425
seq: 1,0,2,3 count: 406
seq: 3,2,0,1 count: 449
seq: 2,3,1,0 count: 424
seq: 0,1,3,2 count: 394
seq: 3,0,2,1 count: 371
seq: 1,2,3,0 count: 411
seq: 0,3,1,2 count: 405
seq: 2,1,3,0 count: 388
seq: 0,3,2,1 count: 375
seq: 2,0,1,3 count: 420
seq: 2,1,0,3 count: 362
seq: 3,0,1,2 count: 396
seq: 1,2,0,3 count: 379
seq: 0,1,2,3 count: 463
seq: 1,3,0,2 count: 398
seq: 2,3,0,1 count: 443
seq: 1,0,3,2 count: 451
seq: 3,1,2,0 count: 421
seq: 2,0,3,1 count: 487
seq: 0,2,3,1 count: 394
seq: 3,1,0,2 count: 480
seq: 0,2,1,3 count: 444
seq: 1,3,2,0 count: 414
使用较小的迭代次数(1K),您可以看到与修改后的表单相比更均匀的分布。但如果你拒绝某些合法的洗牌,那就是预料之中。
十大元素分布尚无定论,因为有太多可能性(360万次洗牌)。也就是说,通过10k次迭代,往往会有大约9980个系列,12-18个计数为2.
答案 4 :(得分:1)
我还没有实施关于结果永远不会处于原始位置或结果序列的规则。无论如何,真正随机的序列将不符合这些规则。但是,我确实发现了一些与您的实现有关的问题。我在测试中在循环中运行了ShuffleArray
100次。下面的代码似乎在产生随机结果方面要好得多。确保在循环调用Random
之前创建ShuffleArray
实例一次消除了种子问题。
此外,您拨打Next
的电话正在通过ptr - 1
,我认为这是不正确的。只考虑循环的第一次迭代,这是您的原始代码正在做的事情:
ptr = items.Length
将ptr
设置为4。ptr -= 1
将ptr
设置为3。rnd.Next(ptr - 1)
将选择0到1之间的整数。这是更新后的代码:
Public Function ShuffleArray(ByVal items() As Integer, ByVal rnd As Random) As Integer()
Dim ptr As Integer
Dim alt As Integer
Dim tmp As Integer
ptr = items.Length
Do While ptr > 1
ptr -= 1
alt = rnd.Next(ptr)
tmp = items(alt)
items(alt) = items(ptr)
items(ptr) = tmp
Loop
Return items
End Function
这是使用For
代替While
的更简单的版本:
Public Function ShuffleArray(ByVal items() As Integer, ByVal rnd As Random) As Integer()
Dim alt As Integer
Dim tmp As Integer
For ptr = items.Length - 1 To 0 Step -1
alt = rnd.Next(ptr)
tmp = items(alt)
items(alt) = items(ptr)
items(ptr) = tmp
Next
Return items
End Function
答案 5 :(得分:1)
小集合的简单解决方案:
public static List<int> Shuffle(List<int> ints)
{
var random = new Random();
var result = ints.ToList();
var hs = new HashSet<int>(ints);
for (int i = 0; i < ints.Count; i++)
{
result[i] = ints.Where((x, j) => j != i).Intersect(hs).OrderBy(x => random.Next()).First();
hs.Remove(result[i]);
}
return result;
}
答案 6 :(得分:1)
在这里,我建议一种方法,通过它可以实现你的洗牌目标,将一个数字作为随机数并移动其他数字来填充数组。请考虑以下代码:
Public Function ShuffleArray(ByVal a() As Integer) As Integer()
Dim ptr As Integer
Dim alt As Integer
Dim items(3) As Integer
Dim rnd As New Random()
ptr = a.Length
alt = rnd.Next(1, 4) '<---- change here it now generate a random number between 1 and 3
Do While ptr <> 0
ptr -= 1
items(ptr) = a(alt)
Select Case alt
Case 0 To 2
alt += 1
Case Else
alt = 0
End Select
Loop
Return items
End Function
这些项目将包含混洗数组, 例如:
1 2 3 0
2 3 0 1
3 0 1 2 etc
<强>更新强>
我的答案不会产生输出0,1,2,3
,因为alt = rnd.Next(1, 4)
将产生1(lowerLimit)和4(upperLimit)之间的数字。 the Link表示rnd.Next(lowerLimit,upperLimit)
会产生一个随机数,包括lowerLimit
和排除upperLimit
。
根据生成的随机数生成即将生成的序列,因为它不会产生0
,不会生成序列0,1,2,3
。
希望答案足够清楚
答案 7 :(得分:1)
如果你减少问题来改变索引数组,然后使用这个数组来排序一个项目数组,这会容易得多。我创建了一个GuardedShuffle
类,演示了如何做到这一点。它目前将一个顺序元素定义为一个元素,在原始序列中提到了附近的值,升序或降序。
代码是人类对你的改组规则的解释,即我用VB.NET编写的手工解决方法。我没有尝试优化它,但它应该足够快,适合合理大小的集合。
如果您对代码有任何疑问 - 请在评论中告诉我,我会尽力解释 - 虽然我试图保持清洁,但不会因为重构和编码标准而过于疯狂。
在将其作为实际应用程序的一部分包含之前,您可能需要添加一些错误检查。
Module Module1
Sub Main()
Dim items() As Integer = {0, 11, 22, 33, 44, 55, 66, 77, 88, 99}
Dim gs As New GuardedShuffle(maxSequentialItems:=1)
Dim shuffledItems() As Integer = gs.ShuffleArray(items)
End Sub
Public Class GuardedShuffle
Private _maxSequentialItems As Integer
Public Sub New(maxSequentialItems As Integer)
_maxSequentialItems = maxSequentialItems
End Sub
Public Function ShuffleArray(items() As Integer) As Integer()
Dim indicesSequential As New List(Of Integer)
For i = 0 To items.Count - 1
indicesSequential.Add(i)
Next
Dim indicesShuffled() As Integer = ShuffleIndices(indicesSequential.ToArray)
Dim retValue As New List(Of Integer)
For i = 0 To items.Count - 1
retValue.Add(items(indicesShuffled(i)))
Next
Return retValue.ToArray
End Function
Private Function ShuffleIndices(indices() As Integer) As Integer()
Dim inputList As New List(Of Integer)(indices)
Dim outputList As New List(Of Integer)
Dim r As New Random
While inputList.Count > 0
Dim seq As New List(Of Integer)
If _maxSequentialItems = 1 AndAlso outputList.Count > 0 Then
seq.Add(outputList.Last)
Else
For k As Integer = outputList.Count - _maxSequentialItems + 1 To outputList.Count - 1
If k >= 0 Then
seq.Add(outputList(k))
End If
Next
End If
Dim allowedList As New List(Of Integer)
For Each el In inputList
If IsAllowed(seq, el, _maxSequentialItems) Then
allowedList.Add(el)
End If
Next
allowedList.Remove(outputList.Count) 'An item is never in its original position
Dim randomIndex As Integer = Math.Floor(r.Next(allowedList.Count))
Dim i As Integer = allowedList.Item(randomIndex)
inputList.Remove(i)
outputList.Add(i)
End While
Return outputList.ToArray
End Function
Private Shared Function IsAllowed(curSeq As List(Of Integer), newValue As Integer, maxSequential As Integer) As Boolean
Dim seq As New List(Of Integer)(curSeq)
seq.Add(newValue)
Return IsAllowed(seq, maxSequential)
End Function
Private Shared Function IsAllowed(seq As List(Of Integer), maxSequential As Integer) As Boolean
Dim curSequential As Integer = 0
For i = 1 To seq.Count - 1
If Math.Abs(seq(i) - seq(i - 1)) = 1 Then
curSequential += 1
End If
Next
Return curSequential < maxSequential
End Function
End Class
End Module
性能/可扩展性测试(在不到100毫秒内洗牌1000件):
Sub Main()
Dim items() As Integer = Enumerable.Range(0, 1000).ToArray
Dim gs As New GuardedShuffle(maxSequentialItems:=1)
Dim t As New Stopwatch
t.Start()
Dim shuffledItems() As Integer = gs.ShuffleArray(items)
t.Stop()
Console.WriteLine("Elapsed (ms): " & t.ElapsedMilliseconds.ToString("N2"))
Console.ReadLine()
End Sub
使用相同的代码,在7-8秒内对10000个项目进行排序。
答案 8 :(得分:1)
注意**如果你制作相同的元素(例如在阵列中的2个不同位置出现9),那么就可以将9a拖入9b的插槽中,同样,因为shuffle功能不考虑什么是洗牌的价值,它只是由数组索引进行洗牌。
Option Strict On
Option Explicit On
Option Infer Off
Public Class Form1
Dim rnd As New Random(Today.Millisecond)
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim originalArray As Integer() = {0, 1, 2, 3, 4, 5, 6, 7, 9}
Dim sb As New System.Text.StringBuilder
Dim final As New System.Text.StringBuilder
Dim msg As String = "Original: {0} Shuffled: {1}"
Dim msg2 As String = "Original: {0} Shuffled: {1} ◄- Same"
For i2 As Integer = 1 To 3
Dim shuffledArray As Integer() = ShuffleArray(originalArray)
For i As Integer = 0 To shuffledArray.Count - 1
If originalArray(i) = shuffledArray(i) Then
sb.AppendLine(String.Format(msg2, originalArray(i), shuffledArray(i)))
Else
sb.AppendLine(String.Format(msg, originalArray(i), shuffledArray(i)))
End If
Next
Dim result As String = sb.ToString.Substring(0, sb.ToString.Length - 1) & vbCrLf & vbCrLf
final.AppendLine(result)
sb.Clear()
Next
RichTextBox1.Text = final.ToString
End Sub
Public Function ShuffleArray(Of t)(ByVal items() As t) As t()
Dim results As New List(Of t)
Dim usedIndexes As New List(Of Integer)
Do
Dim nextIndex As Integer = rnd.Next(0, items.Count)
If usedIndexes.IndexOf(nextIndex) = -1 Then
If usedIndexes.Count = nextIndex Then
If usedIndexes.Count = items.Count - 1 Then
usedIndexes.Clear()
results.Clear()
End If
Continue Do
End If
If Not last3sequential(usedIndexes, nextIndex) Then
usedIndexes.Add(nextIndex)
results.Add(items(nextIndex))
Else
If usedIndexes.Count > items.Count - 3 Then
usedIndexes.Clear()
results.Clear()
End If
End If
End If
Loop Until results.Count = items.Count
Return results.ToArray
End Function
Function last3sequential(usedIndexes As List(Of Integer), nextIndex As Integer) As Boolean
If usedIndexes.Count < 2 Then Return False
Dim last As Integer = nextIndex
Dim secondToLast As Integer = usedIndexes(usedIndexes.Count - 1)
Dim thirdToLast As Integer = usedIndexes(usedIndexes.Count - 2)
If last - secondToLast = 1 AndAlso secondToLast - thirdToLast = 1 Then
Return True
End If
Return False
End Function
End Class
5 test cases:
Original: 0 Shuffled: 7
Original: 1 Shuffled: 8
Original: 2 Shuffled: 5
Original: 3 Shuffled: 2
Original: 4 Shuffled: 9
Original: 5 Shuffled: 4
Original: 6 Shuffled: 0
Original: 7 Shuffled: 6
Original: 8 Shuffled: 3
Original: 9 Shuffled: 1
Original: 0 Shuffled: 4
Original: 1 Shuffled: 2
Original: 2 Shuffled: 9
Original: 3 Shuffled: 6
Original: 4 Shuffled: 7
Original: 5 Shuffled: 0
Original: 6 Shuffled: 3
Original: 7 Shuffled: 5
Original: 8 Shuffled: 1
Original: 9 Shuffled: 8
Original: 0 Shuffled: 8
Original: 1 Shuffled: 7
Original: 2 Shuffled: 6
Original: 3 Shuffled: 2
Original: 4 Shuffled: 0
Original: 5 Shuffled: 1
Original: 6 Shuffled: 9
Original: 7 Shuffled: 4
Original: 8 Shuffled: 5
Original: 9 Shuffled: 3
Original: 0 Shuffled: 6
Original: 1 Shuffled: 4
Original: 2 Shuffled: 8
Original: 3 Shuffled: 7
Original: 4 Shuffled: 9
Original: 5 Shuffled: 2
Original: 6 Shuffled: 5
Original: 7 Shuffled: 3
Original: 8 Shuffled: 1
Original: 9 Shuffled: 0
Original: 0 Shuffled: 6
Original: 1 Shuffled: 9
Original: 2 Shuffled: 0
Original: 3 Shuffled: 1
Original: 4 Shuffled: 5
Original: 5 Shuffled: 2
Original: 6 Shuffled: 3
Original: 7 Shuffled: 8
Original: 8 Shuffled: 7
Original: 9 Shuffled: 4
答案 9 :(得分:1)
如果你提出像你这样的标准,那么随机播放会失去其随机特征。这里是一些带有测试程序的Knuth shuffle算法的实现。我要做的两个主要想法是确保Random是一个全局变量并从i和N中选择一个元素,知道我是循环的索引,N是数组的大小。
using System;
using System.Collections.Generic;
using System.Linq;
public class Solution
{
private static void Main(String[] args)
{
var array = new int[] { 1, 2, 3, 4 };
Dictionary<string, int> results = new Dictionary<string, int>();
for (int i = 0; i < 500000; i++)
{
var a = array.ToArray();
Shuffller.Shuffle(a);
var data = string.Join(" ", a);
if (results.ContainsKey(data))
{
results[data]++;
}
else
{
results.Add(data, 1);
}
}
foreach (var item in results.OrderBy(e => e.Key))
{
Console.WriteLine("{0} => {1}", item.Key, item.Value);
}
Console.ReadKey();
}
public class Shuffller
{
private static Random random = new Random();
/// <summary>
/// * Rearranges an array of objects in uniformly random order
/// (under the assumption that Random generates independent
/// and uniformly distributed numbers).
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="a">the array to be shuffled </param>
public static void Shuffle<T>(T[] a)
{
int N = a.Length;
for (int i = 0; i < N; i++)
{
// choose index uniformly in [i, N-1]
int r = i + random.Next(0, N - i);
T swap = a[r];
a[r] = a[i];
a[i] = swap;
}
}
}
}
如果你想要元素一些结果,为什么不在两个数组之间实现相似性算法然后定义一个阈值,如果混洗数组和原始数据之间的相似性高于阈值然后重新洗牌,虽然我不建议触摸结果,特别是如果你需要随机改组,如果你的算法基于随机改组,你将有很多缺陷。
答案 10 :(得分:-1)
我相信你所看到的是因为你没有用一个值为随机类播种。我建议尝试接受int作为种子值的构造函数。一个简单的种子值就是DateTime.Now.Ticks
有关Random类的更多信息,请参阅以下链接: http://msdn.microsoft.com/en-us/library/system.random.aspx