根据你在评论中的回答,我仍然不明白你到底需要什么。 AFAIU你有两件数据:场景和高度;并且您想要生成复合(场景,高度)元素的排列。我认为你要么需要:
生成所有可能的permuations的恰好一次
随机列表随机生成不同permuations
长(可能是无限的)流所以在这里是一些可能有用的代码。
首先让我们定义一些样板:
public class Scene
{
public readonly string Something;
public Scene(string something)
{
Something = something;
}
// something else
}
public struct CompoundSceneData
{
public readonly Scene Scene;
public readonly float Height;
public CompoundSceneData(Scene scene, float height)
{
Scene = scene;
Height = height;
}
}
当然你Scene
类是最有可能更为复杂。 CompoundSceneData
是一个表示场景+高度的单个项目的结构。
#1生成所有可能的permuations随机列出恰好一次:
// Fisher–Yates shuffle of indices 0..size
int[] GenerateRandomIndicesPermutation(int size)
{
int[] permutation = Enumerable.Range(0, size).ToArray();
Random rnd = new Random();
for (int cur = size; cur >= 2; cur--)
{
int swapPos = rnd.Next(cur);
int tmp = permutation[swapPos];
permutation[swapPos] = permutation[cur - 1];
permutation[cur - 1] = tmp;
}
return permutation;
}
List<CompoundSceneData> GenerateAllRandomPermutationsOnce(Scene[] scenes, float[] heights)
{
int scenesCount = scenes.Length;
int heightsCount = heights.Length;
int totalCount = scenesCount * heightsCount;
List<CompoundSceneData> permutations = new List<CompoundSceneData>(totalCount);
foreach (var compoundIndex in GenerateRandomIndicesPermutation(totalCount))
{
int sceneIndex = compoundIndex % scenesCount;
int heightIndex = compoundIndex/scenesCount;
permutations.Add(new CompoundSceneData(scenes[sceneIndex], heights[heightIndex]));
}
return permutations;
}
void TestUsageAllOnce()
{
Scene[] scenes = new Scene[] { new Scene("Scene #1"), new Scene("Scene #2") };
float[] heights = new float[] { 0.1f, 0.2f, 0.3f };
// this is effectively endless loop
foreach (CompoundSceneData sceneData in GenerateAllRandomPermutationsOnce(scenes, heights))
{
// will be called excactly 2*3 = 6 times
DrawScene(sceneData);
}
}
有几个关键概念有:
如果我们有N个场景和M的高度有将是N M置换,并给定范围[0,N M-1]中的数字,您可以选择一对。例如,2 * N + 5表示第5个场景+第2个高度(在基于0的索引(!)中)。
因此,如果我们想要生成N个场景和M个高度的不同对的序列,那么足以生成数字[0,N * M-1]的随机序列并将其用作指数序列
有一个众所周知的Fisher–Yates shuffle算法来创建一个随机排列。
#2随机生成不同permuations无限流:
IEnumerable<CompoundSceneData> GenerateInfiniteRandomStream(Scene[] scenes, float[] heights)
{
Random rnd = new Random();
while (true)
{
int sceneIndex = rnd.Next(scenes.Length);
int heightIndex = rnd.Next(heights.Length);
yield return new CompoundSceneData(scenes[sceneIndex], heights[heightIndex]);
}
}
void TestUsageInfinite()
{
Scene[] scenes = new Scene[] { new Scene("Scene #1"), new Scene("Scene #2") };
float[] heights = new float[] { 0.1f, 0.2f, 0.3f };
// this is effectively endless loop
foreach (CompoundSceneData sceneData in GenerateInfiniteRandomStream(scenes, heights))
{
DrawScene(sceneData);
// this is the only thing that will stop the loop
if (IsEndOfGame)
break;
}
}
void TestUsageInfinite2()
{
Scene[] scenes = new Scene[] { new Scene("Scene #1"), new Scene("Scene #2") };
float[] heights = new float[] { 0.1f, 0.2f, 0.3f };
List<CompoundSceneData> fixedSizeList = GenerateInfiniteRandomStream(scenes, heights).Take(100).ToList();
foreach (CompoundSceneData sceneData in fixedSizeList)
{
// this will be called 100 times (as specified in Take)
DrawScene(sceneData);
}
}
这里唯一有趣的事情是一个C#的特征yield return
的使用。该功能允许从看起来顺序的代码创建数据流(IEnumerable
)。
请注意,对于解决方案#2,不能保证每个组合(场景+数据)只会出现一次(N * M)个项目。它只是生成随机组合,只有长期运行才具有良好的统计特性。也有可能实现这种保证,但它使代码显着复杂化,并且用户可能不会注意到。
请使用'scenes','test'和'test2'的定义扩展您的代码示例,以便我们可以知道它们的类型。另外,特定的编译器错误可能会有帮助 – SergGr
基本上这是我的问题。 在我的程序中,我有不同的场景,在这个场景里面,我也需要改变一个变量。 我有8个场景和5个高点 我想随机选择1个高1场景,以我拥有所有可能的组合,随机和不重复的方式。 我创建了两个不同的数组,我洗牌场景数组,然后通过for循环,我采取了一个场景和1高。问题是,我有随机的场景,但高位将始终是相同的顺序,如果我洗牌高位,显然我不再有一个平衡的变量组合。 – CornelioQuinto
所以我试图做的是使用一个函数来创建两个数组的组合,然后创建40个新的数组长度= 2 dinamically,每个数组将会是我的组合之一..听起来很愚蠢,但可能阅读上面的消息,可能会有更巧妙的解决方案 – CornelioQuinto