在 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
。
唯一不会做的是在第一个位置插入一个值,否则它会均匀分布值。
有关以下均匀(大部分)分布列表中重复项的方法的详细信息。重复项可以在列表中的任何位置,它们将被分发。
- 创建所有数字的字典并记录它们在列表中出现的次数
- 使用没有任何重复的新列表。对于每个有重复的数字,将其分布在这个新列表的大小上。每次分配均匀。
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
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
。
唯一不会做的是在第一个位置插入一个值,否则它会均匀分布值。
有关以下均匀(大部分)分布列表中重复项的方法的详细信息。重复项可以在列表中的任何位置,它们将被分发。
- 创建所有数字的字典并记录它们在列表中出现的次数
- 使用没有任何重复的新列表。对于每个有重复的数字,将其分布在这个新列表的大小上。每次分配均匀。
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