对列表进行随机化同时确保重复列表不是连续的

时间:2015-04-22 12:09:34

标签: c# algorithm list sorting random

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

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

所以基本前提是:

  1. 列出可能包含一些重复项的人名(通常不会有超过2个任何给定名称的实例,但在特殊情况下可能有3个)
  2. 随机化列表(我已经使用Fisher-Yates(或Knuth shuffle)使用此部分)
  3. 如果列表包含重复项,请标识它们之间少于9项的项目,并尽可能调整以使差距至少为9项。
  4. 第3部分是棘手的一点,我没有关于如何系统应该确保间距正确的任何业务规则的准则只是它应该在可能的情况下。 在最简单的层面上,一个迭代循环检查违规,然后根据需要移动列表元素似乎是要走的路,但是我可以想象一些情况,其中一个配对的调整然后导致另一个的问题等等。

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

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

2 个答案:

答案 0 :(得分:1)

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

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

&#13;
&#13;
from : firstItem += spacing;​
to : firstItem += spacing + 1;​
&#13;
&#13;
&#13;

在调试代码时,我发现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。

&#13;
&#13;
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; }
        }

    }
}
&#13;
&#13;
&#13;

答案 1 :(得分:0)

试试这个:

传递#0随机播放所有名称

传递#1:找出多于一次存在的名称。

传递#2:对于每个多名称查找其最接近的配对,将此名称与9个位置的名称交换如果它不是称为多个名称的名称。在这种情况下交换名称10个位置(重复和增加距离)

当然,如果名称接近列表的开头或结尾,则需要注意并充分处理。