gpt4 book ai didi

c# - 为什么 System.Random.Sample 会抛出 IndexOutOfRangeException?

转载 作者:行者123 更新时间:2023-11-30 16:39:43 26 4
gpt4 key购买 nike

我正在 Unity (2017.1.1f1) 中的 .NET System.Random 实例上调用 Next()。它从 protected 函数 Sample() 中抛出 IndexOutOfRangeException ,它不接受任何参数并返回一个介于 0 和 1 之间的 double 。可能导致此行为的原因是什么?

这里是异常的详细信息

System.IndexOutOfRangeException: Array index is out of range.

at System.Random.Sample () [0x0003e] in /Users/builduser/buildslave/mono/build/mcs/class/corlib/System/Random.cs:91

at System.Random.Next (Int32 maxValue) [0x00017] in /Users/builduser/buildslave/mono/build/mcs/class/corlib/System/Random.cs:112

at Quicksilver.SysIRand.RandInt (Int32 max_exclusive) [0x00008] in F:\SVNHome\gemrush\GemRush\Assets\Source\Shared\Utility\IRand.cs:38

at Quicksilver.IEnumerableExt.SelectRandom[Skill] (IEnumerable`1 collection, IRand rand, Int32 count) [0x00070] in F:\SVNHome\gemrush\GemRush\Assets\Source\Shared\Utility\IEnumerableExt.cs:61

(上面还有13层调用栈)

这是一个多线程环境,但每个线程都有自己专用的 System.Random 实例。从下面的代码中可以看出,传递给 Next() 的参数必须为 1 或更高。

这个错误在一个复杂的自动化测试脚本中被抛出大约 1 小时,因此运行多次来测试理论是昂贵的,并且任何改变 RNG 调用方式的修改都将阻止重现。 (如果错误以某种方式涉及线程之间的意外交互,那么它可能根本无法重现。)

由于它在测试脚本中花费了一个小时,因此绝大部分调用此方法都不会引发错误。

直接使用随机数的函数在这里:

    // Chooses count items at random from the enumeration and returns them in an array
// The order of selected items within the array is also random
// If the collection is smaller than count, the entire collection is returned (in random order)
public static T[] SelectRandom<T>(this IEnumerable<T> collection, IRand rand, int count = 1)
{
if (count <= 0) return new T[0]; // Optimization for trivial case

T[] keep = new T[count];
int found = 0;
foreach (T item in collection)
{
if (found < count)
{
// Take the first #count items, in case that's all there are

// Move a random item of those found so far (including the new one)
// to the end of the array, and insert the new one in its place
int r = rand.RandInt(found + 1);
keep[found++] = keep[r];
keep[r] = item;
}
else
{
// Random chance to replace one of our previously-selected elements
++found;
if (rand.RandInt(found) < count) // probability desired/found
{
// Replace a random previously-selected element
int r = rand.RandInt(count);
keep[r] = item;
}
}
}
if (found < count)
{
// The collection was too small to get everything requested;
// Make a new, smaller array containing everything in the collection
T[] all = new T[found];
Array.Copy(keep, all, found);
return all;
}
return keep;
}

此行抛出错误:

                if (rand.RandInt(found) < count)    // probability desired/found

IRand 是一个非常薄的 System.Random 包装器的接口(interface); IRand.RandInt() 仅返回 Random.Next()。

编辑

以下是 Random 实例的创建和分发方式:

   private void Start()
{
SysIRand[] rngs = new SysIRand[parallelTesters];
if (parallelTesters > 0) rngs[0] = new SysIRand(new System.Random(548913));
if (parallelTesters > 1) rngs[1] = new SysIRand(new System.Random(138498));
if (parallelTesters > 2) rngs[2] = new SysIRand(new System.Random(976336));
if (parallelTesters > 3) rngs[3] = new SysIRand(new System.Random(793461));
if (parallelTesters > 4) rngs[4] = new SysIRand(new System.Random(648791));
if (parallelTesters > 5) rngs[5] = new SysIRand(new System.Random(348916));
if (parallelTesters > 6) rngs[6] = new SysIRand(new System.Random(8467168));
if (parallelTesters > 7) rngs[7] = new SysIRand(new System.Random(6183569));
for (int i = 8; i < parallelTesters; ++i)
{
rngs[i] = new SysIRand(new System.Random(7 * i * i + 8961 * i + 129786));
}

for (int t = 0; t < parallelTesters; ++t)
{
SysIRand rand = rngs[t];

IBot bot = BotFactory.DrawBot(rand);

BotTester tester = new BotTester(rand, bot);
tester.testerID = t + 1;
tester.OnMessage += (str) => UponMessage(tester.testerID + " ~ " + str);
tester.OnError += (str) => UponError(tester.testerID + " ~ " + str);
tester.OnGameAborted += UponGameAborted;
tester.OnDebugMoment += UponDebugMoment;

testers.Add(tester);
}

(在这次运行中,parallelTesters 的值为 3)

每个 BotTester 只使用传递给其构造函数的 Random 实例。每个线程都是私有(private)的 BotTester,从 BotTester.RunGame() 开始:

    public bool RunGame(GameMode mode, int maxGames = 1, long maxMilliSeconds = 100000000, int maxRetries = 5000)
{
if (threadRunning) return false;
thread = new Thread(() => ThreadedRunGame(mode, maxGames, maxMilliSeconds, maxRetries));
thread.Start();
return true;
}

最佳答案

唯一有意义的解释是你认为你正在访问Random()实例线程安全的,用你自己的话来说,每个线程都有自己的Random()实例,但看起来您正在访问相同的 Random() 实例,而它仍在计算过程中。这是Unity中的实现。 Sample() 只是调用 InternalSample()

private int InternalSample()
{
int inext = this.inext;
int inextp = this.inextp;
int index1;
if ((index1 = inext + 1) >= 56)
index1 = 1;
int index2;
if ((index2 = inextp + 1) >= 56)
index2 = 1;
int num = this.SeedArray[index1] - this.SeedArray[index2];
if (num < 0)
num += int.MaxValue;
this.SeedArray[index1] = num;
this.inext = index1;
this.inextp = index2;
return num;
}

如您所见,您可以获得 IndexOutOfRangeException 的地方是有限的,即当您访问 this.SeedArray 时。这是 SeedArray 的定义。

public class Random
{
private int[] SeedArray = new int[56];
....
}

它已经分配给了 56 个元素,在 InternalSample 方法的实现中你可以看到每次调用 index1index2 总是限制为最多 55 除非多次调用 InternalSample 方法。

关于c# - 为什么 System.Random.Sample 会抛出 IndexOutOfRangeException?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52638030/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com