【问题标题】:Randomising a list whilst ensuring duplicates are not sequential随机化列表,同时确保重复项不连续
【发布时间】:2015-06-30 02:03:21
【问题描述】:

我正在寻找一种随机化列表顺序的有效机制,但需要注意的是,如果列表的长度允许,重复项之间必须至少有 9 个项目,否则它们之间的距离必须与长度一样远的列表将允许。

首先,这仅适用于小型数据集,列表中的项目永远不会超过 150 个,可能只有 1 个。

所以基本前提是这样的:

  1. 列出可能包含一些重复的人名(通常任何给定名称的实例不会超过 2 个,但在特殊情况下可能有 3 个)
  2. 随机化列表(这部分已经在使用 Fisher-Yates(或 Knuth shuffle))
  3. 如果列表包含重复项,请确定它们之间的项目少于 9 项,并尽可能调整以使差距至少为 9 项。

第 3 部分是棘手的部分,我没有任何关于如何系统应确保间距正确的业务规则的指导,只是 应该在可能的情况下如此. 在最简单的级别上,检查违规然后适当移动列表元素的迭代循环似乎是可行的方法,但是我可以想象几种情况,其中对一对进行的调整然后导致另一对出现问题,依此类推。

我不是在找人为此编写代码,我只是在寻求一些明智的想法,以一种好的、有效的方式来解决这个问题。

源列表将是IList<string> 中的C# 以供参考。

【问题讨论】:

  • 该链接与我的问题不同,我正在寻找一种算法在列表被随机化后将元素隔开 - 显然,如果它可以作为随机化操作(伪随机)的一部分完成所有更好
  • 在您列表的典型实例中的 150 个名称中,有多少会与同一列表中的其他名称重复?
  • 所以随机化不重复的名字,在你想要的间隔插入重复的名字。您正在尝试平衡随机性和重复间隔的竞争目标,所以这可能已经足够了。
  • 所以随机化所有不重复名称的列表以及每个重复名称的一个实例。然后,在与现有实例的随机偏移处,插入每个副本。如果您遇到此算法失败的情况(考虑到您已经告诉我们的情况,这种情况很少发生),请重新开始。

标签: c# algorithm list sorting random


【解决方案1】:

试试下面的代码。我使用 Linq 将字符串组合在一起以获取所有重复项。然后我创建一个随机的唯一字符串列表。然后将剩余的重复项添加到列表中,使字符串均匀分布。结果是完全随机且均匀分布的。

注意:我发现了一个小错误。下面换行

from : firstItem += spacing;​
to : firstItem += spacing + 1;​

在调试代码时,我发现 firstItem 偶尔会变为负数,因此我添加了代码以确保 firstItem 始终为正数。然后我开始思考为什么我没有在 firstItem 大于数组大小的情况下出现任何溢出。那时我意识到我必须在间距上加 1。带有数组 A,B,C,D,E 的旧代码将给出 1,1,1,1,1,A,B,C,D,E。新代码将给出 1,A,1,B,1,C,1,D,1,E。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication20
{
    class Program
    {
        static void Main(string[] args)
        {
            Random rand = new Random();
            List<string> input = new List<string>();
            List<string> output = new List<string>();
            //create 150 random strings with duplicates
            for(int i = 0; i < 150; i++)
            {
                input.Add(rand.Next(0,25).ToString());
            }
            //create dictionary with two columns key, number of entries
            Dictionary<string, Value> dict = input.AsEnumerable()
                .GroupBy(x => x)
                .ToDictionary(x => x.Key, y => new Value { count = y.Count(), ranNumber = rand.Next() });

            dict = dict.OrderBy(x => x.Value.ranNumber).ToDictionary(x => x.Key, y => y.Value);
            //add 1 sorted numbers to output
            foreach(string key in dict.Keys)
            {
                output.Add(key);
            }
            //add rest of numbers
            foreach (string key in dict.Keys)
            {
                int numberOfItems = dict[key].count;
                if (dict[key].count > 1)
                {
                    int arraySize = output.Count;
                    int spacing = arraySize / numberOfItems;
                    int firstItem = 0;
                    //center around middle
                    if (numberOfItems % 2 == 0)
                    {
                        firstItem = (arraySize / 2) - (((numberOfItems / 2) * spacing) + (spacing / 2));
                    }
                    else
                    {
                        firstItem = (arraySize / 2) - (((numberOfItems - 1) / 2) * spacing);
                    }
                    if (firstItem < 0)
                    {
                        firstItem = 0;
                    }
                    //remove existing item
                    output.Remove(key);
                    //insert items
                    for (int i = 0; i < numberOfItems; i++)
                    {
                        output.Insert(firstItem,key);
                        firstItem += spacing;
                    }
                }
            }

            
        }
        public class Value
        {
            public int count { get; set; }
            public int ranNumber { get; set; }
        }

    }
}

【讨论】:

  • 我已经完成了你的示例代码,我必须说它看起来很棒,我实际上会用一些“真实”数据来尝试它,并会让你知道它的票价
  • 优秀的解决方案 - 谢谢,我已经实现了它,并且对真实数据进行了数百次测试,它可以按要求工作。
  • 您是否对 firstItem += 间距 + 1 进行了更改?
  • @jdweng 是的,我这样做是为了纠正那个错误 - 感谢更新
【解决方案2】:

我可能会这样做:

  1. 删除所有重复项,为每个项目保留一份副本,但请跟踪您删除的每个项目的数量。
  2. 随机化唯一条目列表
  3. 对于在步骤 1 中删除的每个重复项:
    1. 在列表中找到重复的项目。
    2. 在每个位置 ± 9 处将列表拆分为多个部分,忽略零或负长度分区。
    3. 确定不包含重复的分区最大。
    4. 在该分区的随机位置插入副本。

【讨论】:

  • 如果你有三倍(同名的三倍),这将变得很棘手
【解决方案3】:

试试这个:

通过 #0 随机播放所有名称

第 1 关:找出哪些名称存在多次。

第 2 步:为每个多重名称找到最接近的配对,将此名称与相距 9 个位置的名称交换 如果它不是称为多重名称的名称。在这种情况下与 10 个位置以外的名字交换(重复并增加距离)

当然,如果名称靠近列表的开头或结尾,您需要注意并妥善处理。

【讨论】:

  • @vib 我们从“随机列表”开始,然后应用约束。这使列表尽可能“随机”
  • 也许我不明白你的 pass 2 但我怀疑你会超重重复项目在 9 到 18 之间的距离。
  • 这种方法当然是一种选择,我最初考虑将条目“向上”或“向下”移动一个位置以创建所需的间距,您的方法将减少所需的操作数量。
  • 如果你这样做,你就会失去一致性。重复对象接近的实例将被超重,因为它们将作为您的第一个排列的输出(第 0 次)或作为输出或无效的第 0 次后跟纠正步骤出现。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-02-16
  • 1970-01-01
  • 2011-11-08
  • 2012-01-03
相关资源
最近更新 更多