Algorithm 如何使用random()={0..1}生成任意范围内的数字,以保持一致性和密度?

Algorithm 如何使用random()={0..1}生成任意范围内的数字,以保持一致性和密度?,algorithm,random,entropy,Algorithm,Random,Entropy,在[x..y]范围内生成一个随机数,其中x和y是任意浮点数。使用函数random(),它从P个均匀分布的数字(称之为“密度”)返回范围为[0..1]的随机浮点数。必须保持均匀分布,P也必须缩放 我认为,解决这样的问题是不容易的。为了简化一点,我问你们如何在区间[-0.5..0.5],然后在[0..2],然后在[-2..0]中生成一个数字,保持一致性和密度?因此,对于[0..2],它必须从P*2均匀分布的数字生成一个随机数 显而易见的简单解决方案random()*(x-y)+y不会生成所有可能的数

在[x..y]范围内生成一个随机数,其中x和y是任意浮点数。使用函数random(),它从P个均匀分布的数字(称之为“密度”)返回范围为[0..1]的随机浮点数。必须保持均匀分布,P也必须缩放

我认为,解决这样的问题是不容易的。为了简化一点,我问你们如何在区间[-0.5..0.5],然后在[0..2],然后在[-2..0]中生成一个数字,保持一致性和密度?因此,对于[0..2],它必须从P*2均匀分布的数字生成一个随机数


显而易见的简单解决方案
random()*(x-y)+y
不会生成所有可能的数字,因为所有
abs(x-y)>1.0
案例的密度都较低。许多可能的值将被忽略。记住,random()只返回P个可能数中的一个数。然后,如果你把这个数乘以Q,它只会给你一个P可能值,按Q缩放,但你也必须按Q缩放密度P。

如果我理解你的问题,我会给你一个解决方案:但我会从范围中排除1

N = numbers_in_your_random // [0, 0.2, 0.4, 0.6, 0.8] will be 5

// This turns your random number generator to return integer values between [0..N[;
function randomInt()
{
    return random()*N;
}

// This turns the integer random number generator to return arbitrary
// integer
function getRandomInt(maxValue)
{
    if (maxValue < N)
    {
        return randomInt() % maxValue;
    }
    else
    {
        baseValue = randomInt();
        bRate = maxValue DIV N;
        bMod = maxValue % N;
        if (baseValue < bMod)
        {
            bRate++;
        }
        return N*getRandomInt(bRate) + baseValue;
    }
}

// This will return random number in range [lower, upper[ with the same density as random()
function extendedRandom(lower, upper)
{
    diff = upper - lower;
    ndiff = diff * N;
    baseValue = getRandomInt(ndiff);
    baseValue/=N;
    return lower + baseValue;
}
N=numbers\u在您的\u random/[0,0.2,0.4,0.6,0.8]中将为5
//这将使随机数生成器返回[0..N[]之间的整数值;
函数randomInt()
{
返回random()*N;
}
//这将使整数随机数生成器返回任意值
//整数
函数getRandomInt(maxValue)
{
如果(最大值
好吧,
[0..1]*2==[0..2]
(仍然统一)

[0..1]-0.5==[-0.5..0.5]

我想知道你在哪里经历过这样的面试

更新:好吧,如果我们想开始关心乘法精度的降低(这很奇怪,因为在最初的任务中,不知何故你并不关心这个问题,而假装我们关心“值的数量”,我们可以开始迭代。为此,我们还需要一个函数,它将返回
[0..1]中均匀分布的随机值
-这可以通过删除
1.0
值来完成。之后,我们可以将整个范围切成足够小的等分,不必担心精度下降,随机选择一个(我们有足够的随机性),并使用[0..1]函数为所有部分选择一个数字,但最后一个部分除外


或者,您可以想出一种方法来编码足够多的值,只需为代码生成随机位,在这种情况下,您并不真正关心它是[0..1]还是{0,1}。

在真实数学中:解决方案就是提供的:

return random() * (upper - lower) + lower
问题是,即使有浮点数,也只有一定的分辨率。因此,您可以应用上述函数并添加另一个按比例缩放到缺失部分的random()值

如果我举一个实际的例子,我的意思就会变得很清楚:

例如,取0..1中的random()返回值,精度为2位,即0.XY,下限为100,上限为1100

使用上述算法,得到的结果是0.XY*(1100-100)+100=XY0.0+100。 您永远不会看到结果是201,因为最后一个数字必须是0

这里的解决方案是再次生成一个随机值并将其加上*10,这样你就有了一位数的精度(这里你必须注意不要超过给定的范围,这可能会发生,在这种情况下,你必须放弃结果并生成一个新的数)

也许您必须重复它,重复的频率取决于random()函数提供了多少个位置,以及您对最终结果的期望值

在标准IEEE格式中,精度有限(即双53位)。因此,当您以这种方式生成一个数字时,您永远不需要生成多个额外的数字

但你必须小心,当你添加新的数字时,你不会超过给定的上限。有多种解决方案:首先,如果你超过了上限,你从新开始,生成一个新的数字(不要切断或类似,因为这会改变分布)

第二种可能性是检查丢失的低位范围的间隔大小,以及
找到中间值,并生成一个合适的值,保证结果是合适的。

< P>你必须考虑从每个调用到RNG的熵量。这里有一些我刚写的C代码,说明了如何从低熵源中积累熵,最后以高熵随机值结束。
using System;
using System.Collections.Generic;
using System.Security.Cryptography;

namespace SO_8019589
{
  class LowEntropyRandom
  {
    public readonly double EffectiveEntropyBits;
    public readonly int PossibleOutcomeCount;
    private readonly double interval;
    private readonly Random random = new Random();
    public LowEntropyRandom(int possibleOutcomeCount)
    {
      PossibleOutcomeCount = possibleOutcomeCount;
      EffectiveEntropyBits = Math.Log(PossibleOutcomeCount, 2);
      interval = 1.0 / PossibleOutcomeCount;
    }
    public LowEntropyRandom(int possibleOutcomeCount, int seed)
      : this(possibleOutcomeCount)
    {
      random = new Random(seed);
    }
    public int Next()
    {
      return random.Next(PossibleOutcomeCount);
    }
    public double NextDouble()
    {
      return interval * Next();
    }
  }

  class EntropyAccumulator
  {
    private List<byte> currentEntropy = new List<byte>();
    public double CurrentEntropyBits { get; private set; }
    public void Clear()
    {
      currentEntropy.Clear();
      CurrentEntropyBits = 0;
    }
    public void Add(byte[] entropy, double effectiveBits)
    {
      currentEntropy.AddRange(entropy);
      CurrentEntropyBits += effectiveBits;
    }
    public byte[] GetBytes(int count)
    {
      using (var hasher = new SHA512Managed())
      {
        count = Math.Min(count, hasher.HashSize / 8);
        var bytes = new byte[count];
        var hash = hasher.ComputeHash(currentEntropy.ToArray());
        Array.Copy(hash, bytes, count);
        return bytes;
      }
    }
    public byte[] GetPackagedEntropy()
    {
      // Returns a compact byte array that represents almost all of the entropy.
      return GetBytes((int)(CurrentEntropyBits / 8));
    }
    public double GetDouble()
    {
      // returns a uniformly distributed number on [0-1)
      return (double)BitConverter.ToUInt64(GetBytes(8), 0) / ((double)UInt64.MaxValue + 1);
    }
    public double GetInt(int maxValue)
    {
      // returns a uniformly distributed integer on [0-maxValue)
      return (int)(maxValue * GetDouble());
    }
  }

  class Program
  {
    static void Main(string[] args)
    {
      var random = new LowEntropyRandom(2);  // this only provides 1 bit of entropy per call
      var desiredEntropyBits = 64; // enough for a double
      while (true)
      {
        var adder = new EntropyAccumulator();
        while (adder.CurrentEntropyBits < desiredEntropyBits)
        {
          adder.Add(BitConverter.GetBytes(random.Next()), random.EffectiveEntropyBits);
        }
        Console.WriteLine(adder.GetDouble());
        Console.ReadLine();
      }
    }

  }
}
使用系统;
使用System.Collections.Generic;
使用System.Security.Cryptography;
名称空间SO_8019589
{
LowEntropyRandom类
{
公共只读双效PropBybits;
公共只读int-PossibleOutcomeCount;
私有只读双间隔;
private readonly Random=new Random();
公共LowEntropyRandom(int possibleotComount)
{
PossibleOutcomeCount=PossibleOutcomeCount;
EffectiveEntropyBits=Math.Log(可能是eoutcomeCount,2);
间隔=1.0/可能的平均计数;
}
公共LowEntropyRandom(int-PossibleoutComCount,int-seed)
:此(可能为EOUTCOUNT)
{
随机=新随机(种子);
}
公共int Next()
{
返回random.Next(PossibleOutcomeCount);
}
公共双下位双工(
( random() * (Y-X) + X ) + ( random() / 10 ^ (PREC-trunc(lg(Y-X))) )
( random() * (1500-500) + 500 ) + ( random() / 10 ^ (20-trunc(lg(1000))) )
( random() * 1000 + 500 ) + ( random() / 10 ^ (17) )
- 0.4663164 -> PREC=7
- 0.2581916 -> PREC=7
- 0.9147385 -> PREC=7
- 0.129141  -> PREC=6 -> 7, correcting by the average of the other tries
randomBit() { return random() >= 0.5; }
randomInt(N) { while ((val = random2(ceil(log2(N)))) >= N); return val; }
rand2D(N) { return random2(N) + random(); }
randD(V) { while ((val = rand2D(ceil(log2(V)))) >= V); return val; }
rand(L, U) { return L + randD(U - L); }
const p;

function rand(x, y)
  r = random()
  if y-x <= 1
    return x + r*(y-x)
  else
    low = r*(p-1)*(y-x)/p
    high = low + (y-x)/p
    return x + low + rand(low, high)