【问题标题】:Shuffle element such that, no element should come at its original index随机播放元素,使得任何元素都不应出现在其原始索引处
【发布时间】:2015-02-26 09:27:26
【问题描述】:

我有一个对象元素列表

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 中的元素,这样,任何元素都不应出现在其原始索引处(在 SourceList 中)在结果列表中。

例如,在 SourceList 中,C 位于索引 2 处,因此它不能位于 ResultList 中的索引 2 处强>

到目前为止,我已经研究了Dearrangement Wiki,但算法给了我可能的安排,我只需要一个。

【问题讨论】:

  • 我假设您排除了必须对仅包含一个元素的列表进行操作。
  • 您可以通过简单地旋转数组来实现这一点(将每个元素的索引移动 1 并将最后一个元素放在前面)。您需要如何“洗牌”结果数组?
  • 另请注意,通过统一随机洗牌(如fisher yates),您有1/e 的概率得到重新排列。这意味着你可以重复这个过程直到你得到一个,这将花费预期的 e~= 2.7 排列数,所以平均情况下需要O(n) 才能得到一个去排列,并且保证没有偏见,所有去排列都是可以达到的。

标签: c# algorithm random permutation shuffle


【解决方案1】:

您可以将fisher-yates shuffle 用作黑匣子,并反复打乱您的数组,直到您的结果是重新排列。

伪代码:

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 shuffle,这意味着该算法以 O(n) 预期时间复杂度运行

(1) The number of dearrangementsint(n!/e + 1/2),这意味着对于较大的 n 值,数组成为去排列的概率是 (n!/e + 1/2)/n! ~= 1/e

【讨论】:

    【解决方案2】:

    这对我有用:

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

    .OrderBy(x =&gt; rnd.Next()) 的使用产生了统一的排序(无偏见)——我过去已经证实了这一点。

    【讨论】:

    • 在算法标签问题中(实际上,在任何问题中,但特别是算法)“这对我有用”+ 代码是一个糟糕的答案。请描述逻辑。社区期望高代表用户提供更高质量的答案。
    • 请在解释中详细说明:(1)解释(2)它为什么起作用(3)为什么它是公正的(4)为什么每个dearrangement都是可以实现的。
    • @amit - 添加了解释。
    • 解释不尽如人意。它解释了为什么你得到一个随机排列,而不是一个随机的去排列(你得到一个吗?)。此外,“我过去已经证实了这一点”也很难解释。此外,它在时间复杂度方面效率低下。 (找一个置换cab一次性完成,无需排序)。
    • @amit - 请重新阅读“//如果索引有任何匹配的位置,则丢弃它们”。此外,这几乎不会有可怕的时间复杂度。在我的测试中,我最多重试了 12 次,其中 1 次是中位数。
    【解决方案3】:

    进行洗牌,让所有索引不变的元素,然后旋转所有这些错误元素的位置。

    【讨论】:

    • 这个解决方案似乎对我有偏见。
    • 这很简单。我猜每个元素的旋转(在随机播放后具有相同的索引)不会是一个问题。对
    • 它可以,因为我怀疑你得到某些排列的可能性比其他排列高。
    • 例如,您有两种(至少)可能制作[2,3,4,5,6,1],这个以及其他排列您只有一种可能(我假设 [3,2,4,5 ,6,1] 是其中之一,但不确定)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-01-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-23
    • 2021-12-30
    相关资源
    最近更新 更多