如何以随机顺序显示字典中的项,但相邻的两个项不相同
本文关键字:两个 随机 顺序 显示 字典 | 更新日期: 2023-09-27 18:05:24
首先,实际上有比标题中说明的更多的限制。请她录制。
假设我有一个dictionary<char,int>
,其中key作为项,value表示输出中出现的次数。(有点像加权,但没有替代)例('a',2) ('b',3) ('c',1)
可能的输出是'babcab'
我正在考虑下面的方法来实现它。
- 建立一个包含(累计权重,char)作为条目的新列表。
- 从列表中随机选择一个项目,
- 重新计算累计权重,并将最近绘制的项目权重设置为0。
- 重复。
可能会出现这样的情况:生成了'bacab',但不能做进一步的操作(因为只剩下'b',但权重被设置为0,因为不允许立即重复)。在这种情况下,我丢弃所有的结果并从头开始。
还有其他好的方法吗?
同样,如果我跳过"将相应的权重设置为0"的过程,而是拒绝任何不可行的解决方案呢?我已经得到了"bab"。在下一个循环选择中,我得到了"b",然后我重新绘制过程,直到我得到了不是"b"的东西,然后继续。这个效果更好吗?
这个递归算法怎么样?
- 创建一个所有字符的列表(候选列表),根据它们的权重重复它们。
- 创建一个空字符列表(您的解决方案列表)
- 从候选列表中随机选择一个条目
- 如果选中的项目(字符)与解决方案列表中的最后一个相同,则开始扫描候选列表中的另一个字符(如果需要,将其括起来)。
- 如果在步骤4中找不到这样的字符并且候选列表不为空,则返回
- 将选中的字符追加到解决方案列表中。
- 如果候选列表为空,打印出解决方案并'backtrack',否则转到步骤3。
我不太确定"回溯"步骤,但你应该有一个大致的想法。
试试这个,它应该生成一个(伪)随机排序的元素在您的枚举。我建议将你的字典整理成一个列表:
AKA的字典{b, 2}, {a, 3}变成{b} {b} {a} {a}
public static IEnumerable<T> RandomPermutation<T>(this IEnumerable<T> enumerable)
{
if (enumerable.Count() < 1)
throw new InvalidOperationException("Must have some elements yo");
Random random = new Random(DateTime.Now.Millisecond);
while (enumerable.Any())
{
int currentCount = enumerable.Count();
int randomIndex = random.Next(0, currentCount);
yield return enumerable.ElementAt(randomIndex);
if (randomIndex == 0)
enumerable = enumerable.Skip(1);
else if (randomIndex + 1 == currentCount)
enumerable = enumerable.Take(currentCount - 1);
else
{
T removeditem = enumerable.ElementAt(randomIndex);
enumerable = enumerable.Where(item => !item.Equals(removeditem));
}
}
}
如果您需要额外的排列,只需再次调用它以获得另一个随机排序。虽然这不能得到所有的排列,但你应该能找到一些有用的东西。你也可以用它作为一个基线来得到一个解决方案。
这应该被分割成一些单独的方法,并且可以使用一些重构,但是我们的想法是以这样一种方式来实现它,它不依赖于随机移动东西,直到你得到一个有效的结果。这样你就无法预测要花多长时间
-
将所有字符连接到一个字符串并随机化该字符串
-
循环遍历随机字符串并查找违反
规则的任何字符 - 从字符串 中删除该字符
- 随机选择一个数字。使用这个数字作为"将被删除的字符放在第n个有效位置")
- 循环剩余的字符串,找到第n个有效的位置来放回字符。
- 如果没有有效的左位置,删除字符
从步骤2重复,直到没有违规
使用系统;使用System.Collections.Generic;
RandomString名称空间{类项目{
static void Main(string[] args) { Random rnd = new Random(DateTime.Now.Millisecond); Dictionary<char, int> chars = new Dictionary<char, int> { { 'a', 2 }, { 'b', 3 }, { 'c', 1 } }; // Convert to a string with all chars string basestring = ""; foreach (var pair in chars) { basestring += new String(pair.Key, pair.Value); } // Randomize the string string randomstring = ""; while (basestring.Length > 0) { int randomIndex = rnd.Next(basestring.Length); randomstring += basestring.Substring(randomIndex, 1); basestring = basestring.Remove(randomIndex, 1); } // Now fix 'violations of the rule // this can be optimized by not starting over each time but this is easier to read bool done; do { Console.WriteLine("Current string: " + randomstring); done = true; char lastchar = randomstring[0]; for (int i = 1; i < randomstring.Length; i++) { if (randomstring[i] == lastchar) { // uhoh violation of the rule. We pick a random position to move it to // this means it gets placed at the nth location where it doesn't violate the rule Console.WriteLine("Violation at position {0} ({1})", i, randomstring[i]); done = false; char tomove = randomstring[i]; randomstring = randomstring.Remove(i, 1); int putinposition = rnd.Next(randomstring.Length); Console.WriteLine("Moving to {0}th valid position", putinposition); bool anyplacefound; do { anyplacefound = false; for (int replace = 0; replace < randomstring.Length; replace++) { if (replace == 0 || randomstring[replace - 1] != tomove) { // then no problem on the left side if (randomstring[replace] != tomove) { // no problem right either. We can put it here anyplacefound = true; if (putinposition == 0) { randomstring = randomstring.Insert(replace, tomove.ToString()); break; } putinposition--; } } } } while (putinposition > 0 && anyplacefound); break; } lastchar = randomstring[i]; } } while (!done); Console.WriteLine("Final string: " + randomstring); Console.ReadKey(); } }
}