C# 对元素进行洗牌,使任何元素都不应位于其原始索引处
我有一个对象元素列表C# 对元素进行洗牌,使任何元素都不应位于其原始索引处,c#,algorithm,random,permutation,shuffle,C#,Algorithm,Random,Permutation,Shuffle,我有一个对象元素列表 SourceList ResultList (Expected) Obj_A Obj_F Obj_B Obj_C Obj_C Obj_G Obj_D Obj_B Obj_E Obj_A Obj_F Obj_B Obj_G Obj_E
SourceList ResultList (Expected)
Obj_A Obj_F
Obj_B Obj_C
Obj_C Obj_G
Obj_D Obj_B
Obj_E Obj_A
Obj_F Obj_B
Obj_G Obj_E
将源列表中的元素洗牌,这样,在结果列表中,任何元素都不应位于其原始索引(在源列表中)
例如,在源列表中,C位于索引2处,因此它不能位于结果列表中的索引2处
到目前为止,我已经研究过了,但algo给了我可能的安排,我只需要一个。进行洗牌,让所有索引未更改的元素,然后旋转所有这些错误元素的位置。这对我来说很有效:
var SourceList = new List<string>()
{
"Obj_A", "Obj_B", "Obj_C", "Obj_D", "Obj_E", "Obj_F", "Obj_G",
};
var rnd = new Random();
var ResultList =
Enumerable
// create an exceedingly long sequence for retries
.Repeat(0, int.MaxValue)
// select and randomly order all of the indices of `SourceList`
.Select(_ => Enumerable.Range(0, SourceList.Count)
.OrderBy(x => rnd.Next())
.ToArray())
// Discard the indices if they have any matching positions
.Where(x => !x.Where((y, i) => y == i).Any())
// only take one successful set of indices
.Take(1)
// flatten the list of lists to a list
.SelectMany(x => x)
// select the elements from `SourceList` from their index positions
.Select(x => SourceList[x])
// convert to list
.ToList();
var SourceList=newlist()
{
“对象A”、“对象B”、“对象C”、“对象D”、“对象E”、“对象F”、“对象G”,
};
var rnd=新随机数();
var结果列表=
可枚举
//创建一个非常长的重试序列
.重复(0,int.MaxValue)
//选择SourceList的所有索引并随机排序`
.Select(=>Enumerable.Range(0,SourceList.Count)
.OrderBy(x=>rnd.Next())
.ToArray())
//如果索引有任何匹配位置,则丢弃索引
.Where(x=>!x.Where((y,i)=>y==i).Any())
//只取一组成功的索引
.采取(1)
//将列表列表展平为一个列表
.SelectMany(x=>x)
//从“SourceList”的索引位置选择元素
.选择(x=>SourceList[x])
//转换为列表
.ToList();
使用.OrderBy(x=>rnd.Next())
会产生一个统一的顺序(无偏差)-我在过去已经证实了这一点。您可以将其用作一个黑盒,并重复地洗牌数组,直到结果是一个去范围
伪代码:
while true:
arr = [1,2,...,n]
shuffle(arr)
flag = true
for i from 0 to n:
if arr[i] == i: //not a dearrangement
flag = false
if flag == true: //it's a dearrangement
break
shuffle(arr): //fisher yates
for i from 0 to n:
j = rand(i,n)
swap(arr,i,j)
此方法的属性:
- 这保证了的一致性和普遍性,因为每个有效的重新排列 在每次迭代中获取完全相同的几率
- 因为Fisher-Yates生成所有排列,我们只使去排列无效-每个去排列都是可以实现的
- 去排列的概率为1/e1,这意味着平均情况下需要(1-1/e)^-1~=1.56次洗牌,这意味着该算法在
预期时间复杂度下运行O(n)
(1) 是
int(n!/e+1/2)
,这意味着数组的去范围概率是(n!/e+1/2)/n!~=1/e
,对于较大的n
值,我假设您不必对只有一个元素的列表进行操作。如果您只需旋转数组(将每个元素的索引移动1,并将最后一个元素移到前面),就可以实现这一点。您需要如何“洗牌”生成的数组?还要注意,通过均匀随机洗牌(如fisher-yates),您有概率1/e
获得重新排列。这意味着你可以重复这个过程,直到你得到一个,这将花费预期的e~=2.7个排列,因此需要O(n)
平均情况才能得到一个去排列,并且保证是无偏的,所有的去排列都是可以实现的。这个解决方案对我来说似乎有偏见。这将是一个简单的方法。我猜每个元素的旋转(洗牌后索引相同)不会成为问题。是的,它可能会,因为我怀疑你得到一些排列的概率比其他的高。例如,你有两种(至少)可能性使[2,3,4,5,6,1]
,这和其他排列只有一种可能性(我假设[3,2,4,5,6,1]是其中之一,尽管不确定)在一个算法标记问题中(实际上,在任何问题中,但特别是在算法中)“这对我有效”+代码是一个糟糕的答案。请描述一下逻辑。社区期望高代表性用户提供更高质量的答案。请在解释中详细说明:(1)解释(2)为什么它有效(3)为什么它是公正的(4)为什么每一次取消限制都是可以实现的。@amit-解释补充。解释不令人满意。它解释了为什么你会得到一个随机排列,而不是一个随机去排列(你得到了吗?)。而且,“我过去已经证实了这一点”也很难解释。此外,它在时间复杂度方面效率低下。(查找置换cab可以在一次过程中完成,无需排序)。@amit-如果索引有任何匹配位置,请重新读取“//丢弃索引”。而且,这几乎不会有可怕的时间复杂性。在我的测试中,我最多重试12次,其中1次为中位数。