我想要一个混乱的合并列表,它将保持列表的内部顺序。
例如:
列表A:11 22 33
列表B:6 7 8
有效结果: 11 22 6 33 7 8
无效结果:22 11 7 6 33 8
答案 0 :(得分:2)
只需随机选择一个列表(例如,生成0到1之间的随机数,如果< 0.5列表A,否则列出B),然后从该列表中取出该元素并将其添加到新列表中。重复,直到每个列表中没有剩余元素。
答案 1 :(得分:2)
原始答案:
static IEnumerable<T> MergeShuffle<T>(IEnumerable<T> lista, IEnumerable<T> listb)
{
var first = lista.GetEnumerator();
var second = listb.GetEnumerator();
var rand = new Random();
bool exhaustedA = false;
bool exhaustedB = false;
while (!(exhaustedA && exhaustedB))
{
bool found = false;
if (!exhaustedB && (exhaustedA || rand.Next(0, 2) == 0))
{
exhaustedB = !(found = second.MoveNext());
if (found)
yield return second.Current;
}
if (!found && !exhaustedA)
{
exhaustedA = !(found = first.MoveNext());
if (found)
yield return first.Current;
}
}
}
基于marcog答案的第二个答案
static IEnumerable<T> MergeShuffle<T>(IEnumerable<T> lista, IEnumerable<T> listb)
{
int total = lista.Count() + listb.Count();
var random = new Random();
var indexes = Enumerable.Range(0, total-1)
.OrderBy(_=>random.NextDouble())
.Take(lista.Count())
.OrderBy(x=>x)
.ToList();
var first = lista.GetEnumerator();
var second = listb.GetEnumerator();
for (int i = 0; i < total; i++)
if (indexes.Contains(i))
{
first.MoveNext();
yield return first.Current;
}
else
{
second.MoveNext();
yield return second.Current;
}
}
答案 2 :(得分:1)
在区间[0,A.Length
)中生成B.Length
个随机整数。对随机数进行排序,然后从i
迭代0..A.Length
,将A[i]
添加到r[i]+i
中的B
位置。 +i
是因为当您从B
插入值时,您正在将A
中的原始值向右移动。
这与您的RNG一样随机。
答案 3 :(得分:1)
如果您需要将输出均匀分布,则此页面中提供的答案均无效。
为说明我的示例,假设我们要合并两个列表A=[1,2,3]
,B=[a,b,c]
在大多数答案中提到的方法中(即,将两个列表合并为la mergesort,但每次都随机选择一个列表头),输出[1 a 2 b 3 c]
的可能性远小于[1 2 3 a b c]
。直观上,发生这种情况是因为当列表中的元素用完时,另一个列表中的元素会附加在末尾。因此,第一种情况的概率为0.5*0.5*0.5 = 0.5^3 = 0.125
,但是在第二种情况下,存在更多的随机随机事件,因为随机头必须被选择5次而不是3次,这给了我们概率0.5^5 = 0.03125
中的。经验评估也可以轻松验证这些结果。
@marcog建议的答案几乎是正确的。但是,存在r
的排序后分布不均匀的问题。发生这种情况是因为原始列表[0,1,2]
,[2,1,0]
,[2,1,0]
都被排序为[0,1,2],这使得排序后的r
比{ {1}}仅有一种可能性。
有一种巧妙的方式来生成列表[0,0,0]
,使其均匀分布,如下面的数学StackExchange问题:https://math.stackexchange.com/questions/3218854/randomly-generate-a-sorted-set-with-uniform-distribution
要总结该问题的答案,必须采样| B |。集合r
中的元素(均匀随机且无重复),对结果进行排序,然后将其索引减去此新列表中的每个元素。结果是列表{0,1,..|A|+|B|-1}
,可用于替换@marcog的答案。
答案 4 :(得分:0)
除了生成索引列表之外,还可以根据每个列表中剩余元素的数量来调整概率,从而完成此操作。在每次迭代中,A将剩余A_size个元素,B将剩余B_size个元素。从1 ..(A_size + B_size)中选择一个随机数R。如果R <= A_size,则使用A中的元素作为输出中的下一个元素。否则,请使用B中的元素。
int A[] = {11, 22, 33}, A_pos = 0, A_remaining = 3;
int B[] = {6, 7, 8}, B_pos = 0, B_remaining = 3;
while (A_remaining || B_remaining) {
int r = rand() % (A_remaining + B_remaining);
if (r < A_remaining) {
printf("%d ", A[A_pos++]);
A_remaining--;
} else {
printf("%d ", B[B_pos++]);
B_remaining--;
}
}
printf("\n");
随着列表变小,从中选择元素的可能性将降低。
这可以缩放到多个列表。例如,给定列表A,B和C的大小分别为A_size,B_size和C_size,请在1 ..(A_size + B_size + C_size)中选择R。如果R <= A_size,则使用A中的元素。否则,如果R <= A_size + B_size,则使用B中的元素。否则C。
答案 5 :(得分:0)
这是一个确保输出均匀分布的解决方案,并且很容易得出原因。首先想到的是生成令牌列表,其中每个令牌代表特定列表的一个元素,而不是特定元素。例如,对于两个具有3个元素的列表,我们生成以下令牌列表:0、0、0、1、1、1。然后,对令牌进行洗牌。最后,我们为每个令牌生成一个元素,然后从相应的原始列表中选择下一个元素。
public static IEnumerable<T> MergeShufflePreservingOrder<T>(
params IEnumerable<T>[] sources)
{
var random = new Random();
var queues = sources
.Select(source => new Queue<T>(source))
.ToArray();
var tokens = queues
.SelectMany((queue, i) => Enumerable.Repeat(i, queue.Count))
.ToArray();
Shuffle(tokens);
return tokens.Select(token => queues[token].Dequeue());
void Shuffle(int[] array)
{
for (int i = 0; i < array.Length; i++)
{
int j = random.Next(i, array.Length);
if (i == j) continue;
if (array[i] == array[j]) continue;
var temp = array[i];
array[i] = array[j];
array[j] = temp;
}
}
}
用法示例:
var list1 = "ABCDEFGHIJKL".ToCharArray();
var list2 = "abcd".ToCharArray();
var list3 = "@".ToCharArray();
var merged = MergeShufflePreservingOrder(list1, list2, list3);
Console.WriteLine(String.Join("", merged));
输出:
ABCDaEFGHIb @ cJKLd
答案 6 :(得分:-1)
只需遍历两个列表,同时随机选择要从中挑选项目的列表。然后将它们粘贴到另一个列表中这是一种方法:
static IEnumerable<T> Shuffle<T>(IEnumerable<T> listB, IEnumerable<T> listA)
{
var rng = new Random();
var lists = new[] { new Queue<T>(listA), new Queue<T>(listB) };
while (lists.Any(l => l.Any()))
{
int i = rng.Next(2);
var selected = lists[i];
if (!lists[i].Any())
selected = lists[1-i];
yield return selected.Dequeue();
}
}