在 C# 中均匀分布的 Int 数组重新排序?

Int Array Reorder with Even Distribution in C#?

12,13,14,15,16,19,19,19,19 

12,19,13,19,14,19,15,19,16

大家好。谁能指出 clues/samples 如何将第一个 Int32 值数组(其中附加了一堆 19 个值)分配到第二个数组(其中 19 个值相当均匀地散布在数组中)?

我不是在寻找随机洗牌,因为在此示例中,如果有随机化,#19 仍可能连续出现。我想确保 #19 以可预测的模式置于其他数字之间。

这个用例类似于团队轮流展示一个主题:12-16 队各展示一次,然后 19 号队出现但不应该连续四次展示他们的主题,他们应该展示他们的话题在其他团队之间。

稍后,如果将12个7的值添加到数组中,那么它们也必须均匀分布到序列中,数组将是21个元素,但同样的规则是#19和#7都不是应该有连续放映。

我认为 Math.NET 库中可能有一些东西可以做到这一点,但我没有找到任何东西。在 .NET Framework 4.7 上使用 C#。

谢谢。

如果随机分布就足够了,下面的代码就足够了:

    static void MixArray<T>(T[] array)
    {
        Random random = new Random();

        int n = array.Length;
        while (n > 1)
        {
            n--;
            int k = random.Next(n + 1);
            T value = array[k];
            array[k] = array[n];
            array[n] = value;
        }
    }

例如:

    int[] input = new int[]{12,13,14,15,16,19,19,19,19};
    MixArray<int>(input);

如果您需要在保持元素顺序的同时精确均匀分布,则使用以下代码即可:

    public static T[] EvenlyDistribute<T>(T[] existing, T[] additional)
    {
        if (additional.Length == 0)
            return existing;

        if (additional.Length > existing.Length) 
        {
            //switch arrays
            T[] temp = additional;
            additional = existing;
            existing = temp;
        }

        T[] result = new T[existing.Length + additional.Length];
        List<int> distribution = new List<int>(additional.Length);
        double ratio = (double)(result.Length-1) / (additional.Length);
        double correction = -1;

        if (additional.Length == 1)
        {
            ratio = (double)result.Length / 2;
            correction = 0;
        }

        double sum = 0;
        for (int i = 0; i < additional.Length; i++)
        {
            sum += ratio;

            distribution.Add(Math.Max(0, (int)(sum+correction)));
        }

        int existing_added = 0;
        int additional_added = 0;
        for (int i = 0; i < result.Length; i++)
        {
            if (additional_added == additional.Length)
                result[i] = existing[existing_added++];
            else
            if (existing_added == existing.Length)
                result[i] = additional[additional_added++];
            else
            {
                if (distribution[additional_added] <= i)
                    result[i] = additional[additional_added++];
                else
                    result[i] = existing[existing_added++];
            }
        }

        return result;
    }

例如:

    int[] existing = new int[] { 12, 13, 14, 15, 16};
    int[] additional = new int[] { 101, 102, 103, 104};

    int[] result = EvenlyDistribute<int>(existing, additional);
    //result = 12, 101, 13, 102, 14, 103, 15, 104, 16

这是如何做到这一点。

var existing = new[] { 12, 13, 14, 15, 16 };
var additional = new [] { 19, 19, 19, 19 };

var lookup =
    additional
        .Select((x, n) => new { x, n })
        .ToLookup(xn => xn.n * existing.Length / additional.Length, xn => xn.x);

var inserted =
    existing
        .SelectMany((x, n) => lookup[n].StartWith(x))
        .ToArray();

这给我的结果类似于 12, 19, 13, 19, 14, 19, 15, 19, 16

唯一不会做的是在第一个位置插入一个值,否则它会均匀分布值。

有关以下均匀(大部分)分布列表中重复项的方法的详细信息。重复项可以在列表中的任何位置,它们将被分发。

  1. 创建所有数字的字典并记录它们在列表中出现的次数
  2. 使用没有任何重复的新列表。对于每个有重复的数字,将其分布在这个新列表的大小上。每次分配均匀。
    public static List<int> EvenlyDistribute(List<int> list)
    {
        List<int> original = list;

        Dictionary<int, int> dict = new Dictionary<int, int>();
        list.ForEach(x => dict[x] = dict.Keys.Contains(x) ? dict[x] + 1 : 1);

        list = list.Where(x => dict[x] == 1).ToList();

        foreach (int key in dict.Where(x => x.Value > 1).Select(x => x.Key))
        {
            int iterations = original.Where(x => x == key).Count();
            for (int i = 0; i < iterations; i++)
                list.Insert((int)Math.Ceiling((decimal)((list.Count + iterations) / iterations)) * i, key);
        }

        return list;
    }

主要用法:

    List<int> test = new List<int>() {11,11,11,13,14,15,16,17,18,19,19,19,19};
    List<int> newList = EvenlyDistribute(test);

输出

19,11,13,19,14,11,19,15,16,19,11,17,18