真正的随机数通常对普通人来说似乎不随机,因为随机生成的序列将被解释为结构。是否有任何算法生成一组心理上“似乎”随机的数字,即使它们不是?
答案 0 :(得分:20)
这是一个算法:
使用任何Pseudorandom Number Generator创建非随机数字序列(因为它们是由种子和算法预先确定的)。
使用Diehard battery (source)中的一系列统计测试来取消主观和历史上被认为有偏见的子序列。
输出将是“在心理上”似乎“随机的数字,即使它们不是”。
此外,还有人类随机性判断的认知模型。一个这样的模型(二进制序列)基于Kolmogorov复杂性,但它不会给你一个算法,因为Kolmogorov复杂度不可计算。但是,它可能会给你更多关于找到“心理随机数”的想法。
<强>抽象强>
我们提出了一个使用算法思想的人类随机性判断的统计帐户 复杂。我们展示了现有的衡量标准 序列的随机性对应于生成非随机序列的假设 通过特定的概率有限状态自动机, 并使用此作为帐户的基础,该帐户根据程序的长度评估随机性 适用于乔姆斯基等级不同层次的机器。这种方法产生的模型比响应更好地预测人类判断 同一实验的其他参与者
Griffiths,T。L.,&amp; Tenenbaum,J。B.(2003)。概率,算法 复杂性和主观随机性。在R. Alterman&amp; D. Kirsh (编辑),第25届认知年会论文集 科学学会(第480-485页)。新泽西州Mahwah:Erlbaum。 [PDF]
答案 1 :(得分:3)
没有一个系列的东西,在心理上 - 对每个人来说都是随机的,但事实并非如此。
我们知道这个实验,当人们被告知写下随机看的一系列硬币或骰子结果时。他们经常倾向于避免连续剧,但他们为什么这样做呢?难道他们没有足够的时间,工具和统计知识来产生更好的结果吗?至少如果你把足够多的学生从高中数学教育中解脱出来,他们应该能够检测出错误的编曲系列,给予足够的时间和足够长的系列。
但即使没有这个,一个受过良好教育的人也可以通过破碎的发电机设计来检测出一个糟糕的系列。
想想一个骰子,在那里你总是洗牌6个数字(2,6,1,4,3,5)并按顺序挑选它们,然后再次洗牌(6,3,2,4,1,5)然后选择下一个6.
这种方法永远不会导致串联的3个相同的数字,但在某些游戏中它可能非常有用,例如连续3x6,当然,随着时间的推移,很明显,它永远不会发生,只是你曾经玩这个游戏,你知道,它通常每小时发生2-3次。
很早就会很明显的另一个案例是轮盘赌,人们相信他们错误的系统会开始赢得胜利,并且很早就打破了银行。这样的系统通常依赖于这样的假设,例如“如果你有一长串黑色,下注白色,因为它需要发生,黑色系列越长,就越多”。如果你赌博有这样的期望,并且发电机满足你的想法,对于赢家,银行和聪明的观察者来说,这将是非常明显的,正在发生的事情。
检测有缺陷的RNG取决于您的知识,系列的长度以及您可以花费在问题上的努力,如果您可以通过这种方式获得或减少很多钱,这可能会增加。
没有共同的期望,独立于观众的知识。即使数字1到6应该 - 从长远来看 - 应该同样经常发生,需要知道,直觉不知道。
从短篇小说中,没有人能够发现它是否有缺陷。从更长的系列中,可以检测到破碎的RNG。
故意破坏RNG的整个想法,将其调整为受过严重教育的偏见,是有缺陷的。如果你需要一些音乐洗牌器而不需要过早重复,那就为这个东西寻找一个更好的名字。
答案 2 :(得分:1)
首先要考虑的是你如何使用数字。翻转大量的硬币会给出相当不错的50/50分割,翻转两三个硬币很容易让你在太多时间内给出一个恼人的非显而易见的随机3头或尾巴。滚动大量的骰子寻找的将会产生非常不均匀的结果,有时根本没有任何骰子,有时骰子的一半是骰子。滚动更多骰子将有助于此。一般来说,你产生的数字越多,人们就会越快乐。
如果你只生成一些数字,我会从真正随机的数字开始,看看是什么让你烦恼,并修复它。一件简单的事情就是根据之前出现的频率来降低数字出现的几率。或者更简单地说,不要两次返回相同的数字。但通常有微妙之处。如果您返回1到10的数字,但1-4计数单向和5-10另一个,123451234512345的序列将非常烦人(80%低,20%高)。如果范围是1-2和3-10则不会(你得到预期的40%低,60%高)。更糟糕的是,如果你有竞争性的掷骰子,重要的是相关结果。每个骰子的数字本身可能看起来是随机的,但如果10次中有9次骰子比另一次骰子高,则骰子似乎会被加载。
还有更多这样的事情。如果有人正在玩游戏,他们掷骰子看他们移动了多远,然后滚动看他们得到了多少钱,一套在他们自己心理上随意的卷可以给某人没有动作和大量的钱,反之亦然,而且看起来很严重。
因此,我会生成一个随机数或一组数字,如果在特定情况下看起来不够随机,则拒绝它们。 (你可能想要在一定比例的时间内拒绝它,否则你可能会完全放弃随机性。)(如果你很聪明,你可以提前调整几率,以尽量减少你的再生次数要做的。)这个问题并没有真正的解决方案。
实际上,我可以想到两个通用的解决方案。一个是:只是不要使用随机数。另一种是产生大量的它们,不要让任何人看到序列的细节。如果你有一个拥有5000名士兵的游戏,每一方都有10%的几率击球,那么整体效果(一方486倒,另一方倒512)将在心理上随意。只是不要让任何人看看10个人的镜头结果。 (“我有30次射门而不是一次击中!”“他在5次射击中有3次命中!”)
另外:我写了上面的内容,然后意识到这里可能存在道德问题。如果您正在生成随机地图或执行许多其他操作,那么操纵随机数生成的结果可能是一个非常好的主意。但是,游戏中的玩家可能拥有真实随机数的权利。如果你有一个扑克游戏,并且一个真正的随机交易给玩家一个皇家同花顺,你的程序没有业务决定那太极端和重新定价。决定以50%的成功机会进行攻击的战争游戏玩家不应该被这样一个事实所蒙蔽:因为之前他已连续赢得3场50-50场比赛,他的真实赔率现在只有20-80 。这里的道德方面可能会填满我没有时间写的书,但要非常清楚。
答案 3 :(得分:0)
您的问题有两个要求。
您需要更好地定义这两个要求。我建议我们定义这样的要求:
我提出以下算法:
NEXT = RandomStartingNumber
X = empty list
loop {
NEXT = HASH(NEXT)
add NEXT to X
}
这会使数据看起来是随机的,而实际上可以很容易地由计算机计算出来。
答案 4 :(得分:-3)
下面的RNG代码(用c#编写)会生成一些非常随机的数字:
public class Randomizer
{
/// <summary>
/// An array of unsigned integers containing 100 pre-rolled randomly generated numbers.
/// </summary>
private uint[] randBuf = new uint[100];
/// <summary>
/// The index of the last read number out of the <see cref="Atlana.Random.Randomizer.randBuf">randBuf</see> array.
/// </summary>
private uint index = 0;
/// <summary>
/// Original value of the seed used to initialize the number array.
/// </summary>
private uint origSeed;
/// <summary>
/// Initializes a new instance of the Randomizer class. Uses <paramref name="seed">seed</paramref> to initialize the number array.
/// </summary>
/// <param name="seed">Integer value used to seed the number array.</param>
public Randomizer(int seed)
{
this.origSeed = (uint)seed;
this.Initialize((uint)seed);
}
/// <summary>
/// Invokes the randomizer to refresh the list of random values.
/// </summary>
public void ReRoll()
{
this.Roll();
}
/// <summary>
/// Generates a random unsigned integer number value between the specified range of <paramref name="min">min</paramref> and <paramref name="max">max</paramref>.
/// </summary>
/// <param name="min">An unsigned integer representing the minimum value of the return value.</param>
/// <param name="max">An unsigned integer representing the maximum value of the return value.</param>
/// <returns>An unsigned integer containing a number between <paramref name="min">min</paramref> and <paramref name="max">max</paramref>.</returns>
public uint Range(uint min, uint max)
{
uint y = this.Random();
if (y < min)
{
y = y * min;
}
if (y > max)
{
y = y % (max + 1);
}
return y;
}
/// <summary>
/// Generates a random integer number value between the specified range of <paramref name="min">min</paramref> and <paramref name="max">max</paramref>.
/// </summary>
/// <param name="min">An integer representing the minimum value of the return value.</param>
/// <param name="max">An integer representing the maximum value of the return value.</param>
/// <returns>An integer containing a number between <paramref name="min">min</paramref> and <paramref name="max">max</paramref>.</returns>
public int Range(int min, int max)
{
return Convert.ToInt32(this.Range(Convert.ToUInt32(min), Convert.ToUInt32(max)));
}
/// <summary>
/// Generates a random number between 0 and 100.
/// </summary>
/// <returns>A randomly generated integer value between 0 and 100.</returns>
public int Percent()
{
return this.Range(0, 100);
}
/// <summary>
/// Retrieves a value from the random number array.
/// </summary>
/// <returns>A randomly generated unsigned integer</returns>
private uint Random()
{
if (this.index == 0)
{
this.Roll();
}
uint y = this.randBuf[this.index];
y = y ^ (y >> 11);
y = y ^ ((y << 7) + 3794);
y = y ^ ((y << 15) + 815);
y = y ^ (y >> 18);
this.index = (this.index + 1) % 100;
return y;
}
/// <summary>
/// Initializes the number array from a seed provided by <paramref name="seed">seed</paramref>.
/// </summary>
/// <param name="seed">Unsigned integer value used to seed the number array.</param>
private void Initialize(uint seed)
{
this.randBuf[0] = seed;
for (uint i = 1; i < 100; i++)
{
this.randBuf[i] = (uint)(this.randBuf[i - 1] >> 1) + i;
}
}
/// <summary>
/// Checks to prevent <see cref="System.ArithmeticException">ArithmeticException</see>.
/// </summary>
private void OverflowCheck()
{
foreach (uint u in this.randBuf)
{
if (u > (uint.MaxValue / 3794))
{
this.Initialize(this.origSeed + this.index);
break;
}
}
}
/// <summary>
/// Refreshes the list of values in the random number array.
/// </summary>
private void Roll()
{
this.OverflowCheck();
for (uint i = 0; i < 99; i++)
{
uint y = this.randBuf[i + 1] * 3794U;
this.randBuf[i] = (((y >> 10) + this.randBuf[i]) ^ this.randBuf[(i + 399) % 100]) + i;
if ((this.randBuf[i] % 2) == 1)
{
this.randBuf[i] = (this.randBuf[i + 1] << 21) ^ (this.randBuf[i + 1] * (this.randBuf[i + 1] & 30));
}
}
}
}
以下是使用上述类的0-50之间的一系列数字的示例:
7 2 6 4 17 1 48 18 46 14 44 32 37 12 48 12 14 47 15 10 5 12 15 9 9 15 27 47 5 5
2 16 20 48 50 22 31 39 40 20 41 27 35 50 46 21 8 34 24 6