从一个范围到另一个范围的均匀分布的随机数的节俭转换

如何解决从一个范围到另一个范围的均匀分布的随机数的节俭转换

是否有一种方法可以节省将一个范围的均匀分布的随机数转换为另一范围的均匀分布的随机数?

让我解释一下“ 节俭”的意思。

在给定范围内(例如r∈[0..10))生成随机数的典型方法是采用一些固定的随机位,例如31,这将导致非负随机数小于2147483648。然后确保值小于2147483640(因为2147483648不能被10整除,因此可能导致分布不均)。如果该值大于或等于2147483640,则将其丢弃并重试(获取下31个随机位,依此类推)。如果值小于2147483640,则仅返回除以10的余数。此方法每十进制数字至少消耗31位。由于理论极限是log 2 (10)= 3.321928 ...,所以非常浪费。

如果使用4位代替31,我们可以改善这一点。在这种情况下,每十进制数字将消耗4×1.6 = 6.4位。这虽然更省钱,但仍远非理想。

    public int nextDit() {
        int result;
        do {
            result = next4Bits();
        } while (result >= 10);
        return result;
    }

我们可以尝试一次生成3个十进制数字。由于1024非常接近1000,因此拒绝原始原始随机数的可能性小于以前的情况。生成3个十进制数字后,我们将返回1个数字并保留其余2个数字。

类似以下内容

    private int _decDigits = 0;
    private int _decCount = 0;

    public int nextDit() {
        if (_decCount > 0) {
            // take numbers from the reserve
            int result = _decDigits % 10;
            _decDigits /= 10;
            _decCount -= 1;
            return result;
        } else {
            int result;
            do {
                result = next10Bits();
            } while (result >= 1000);
            // reserve 2 decimal digits
            _decCount = 2;
            _decDigits = result % 100;
            result /= 100;
            return result;
        }
    }

这种方法更省钱:每十进制数字消耗10×1.024 / 3 = 3.41(3)位。

如果我们尝试重用以前丢弃的数字,我们甚至可以走得更远。随机数r∈[0,1024)属于3个范围之一:[0,1000),[1000,1020),[1020,1024)。

如果它属于[0,1000],我们将像以前一样进行操作,保留2个十进制数字(保留十进制数字)并返回1个十进制数字。

如果它落入[1000,1020),我们减去1000转换为范围[0,20)。然后,我们将其除以10得到1位,并得到除以10的余数得到1个十进制数字。将其放入二进制数保留区并返回十进制数字。

如果它属于[1020,1024),我们减去1020转换为[0,4)范围。在这里,我们只得到2位,并将其放入二进制数字保留区。

    // decimal digit reserve
    private int _decDigits = 0;
    private int _decCount = 0;
    // binary digit reserve
    private int _binDigits = 0;
    private int _binCount = 0;

    private int nextBits(int bits,int n) {
        for (int i = 0; i < n; i += 1) {
            bits = (bits << 1) + _bitRandomDevice.nextBit();
        }
        return bits;
    }

    private int next10Bits() {
        // take bits from the binary reserve first,then from _bitRandomDevice
        int result;
        if (_binCount >= 10) {
            result = _binDigits >> (_binCount - 10);
            _binDigits = _binDigits & (1 << (_binCount - 10) - 1);
            _binCount -= 10;
        } else {
            result = nextBits(_binDigits,10 - _binCount);
            _binCount = 0;
            _binDigits = 0;
        }
        return result;
    }

    public int nextDit() {
        if (_decCount > 0) {
            // take numbers from the decimal reserve
            int result = _decDigits % 10;
            _decDigits /= 10;
            _decCount -= 1;
            return result;
        } else {
            int result;
            while (true) {
                result = next10Bits();
                if (result < 1000) {
                    assert result >= 0 && result < 1000;
                    // reserve 2 decimal digits
                    _decCount = 2;
                    _decDigits = result % 100;
                    result /= 100;
                    // return 1 decimal digit
                    return result;
                } else if (result < 1020) {
                    result -= 1000;
                    assert result >= 0 && result < 20;
                    // reserve 1 binary digit
                    _binCount += 1;
                    _binDigits = (_binDigits << 1) + (result / 10);
                    // return 1 decimal digit
                    return result % 10;
                } else {
                    result -= 1020;
                    assert result >= 0 && result < 4;
                    // reserve 2 binary digits
                    _binCount += 2;
                    _binDigits = (_binDigits << 2) + result;
                }
            }
        }
    }

此方法每十进制数字消耗约3.38 ...位。这是我能找到的最节约的方法,但它仍然浪费/丢失了随机性来源的一些信息。

因此,我的问题是:是否存在一种通用方法/算法将一个任意范围[0,s]的均匀分布的随机数(后来称为源数)转换为另一个任意范围[0,t的均匀分布的随机数)(后来称为目标编号),每个目标编号仅消耗log s (t)+ C源代码?其中C是一些常数。 如果没有这种方法,为什么呢?是什么导致无法达到理想极限?

节俭的目的是减少对RNG的呼叫次数。当我们使用True RNG时,尤其值得这样做,因为吞吐量通常很有限。

对于“节俭优化”,它们基于以下假设:

  • 给定均匀随机数r∈[0,N),在检查 r r > = M 之后,我们可以假定它均匀地分布在[M,N)中。
  • 给定统一随机数r∈[A,B),导出的随机数(r + C)均匀分布在[A + C,B + C)中。即我们可以对随机数添加或减去任何常量以改变其范围。
  • 给定均匀随机数r∈[0,N),其中N = P×Q,导出的随机数(r%P)均匀地分布在[0,P)中,而(r / P)均匀地分布在[0,P]中[0,Q)。即我们可以将一个统一的随机数分成几个。
  • 给定统一的随机数p∈[0,P)和q∈[0,Q),导出的随机数(q×P + p)均匀分布在[0,P×Q)中。即我们可以将统一的随机数组合成一个。

解决方法

您的目标最终是在不浪费随机性的情况下,只给定 p 侧边的骰子来滚动 k 侧边的骰子。

从这个意义上说,B。Kloeckner在“ Simulating a dice with a dice”中的引理3中,除非“除以 k 的每个素数也除以 p ,否则这种浪费是不可避免的。 >”。因此,例如,如果 p 是2的幂(并且任何随机位块都与以2个数量的面的幂滚动骰子相同)和 k 的主要因素不是2,最好的办法是任意获取,几乎不会浪费随机性。

此外,除了分批处理比特以减少“比特浪费”(另请参见Math Forum)之外,还有{em> {em> {em}}中讨论的随机抽取技术,在我的Devroye and Gravel 2015-2020中。

另请参阅问题:Note on Randomness Extraction,尤其是我在那的答案。

,

继续添加更多数字。这是一些用于计算预期收益的Python(对于n的特定值,此方法比您的方法要稍差一些,因为它不会保存剩余的比特,但足以说明我的意思):

import math


def expected_digits(n,b):
    total = 0
    p = 1
    while n >= b:
        p *= 1 - (n % b) / n
        total += p
        n //= b
    return total


def expected_yield(k):
    return expected_digits(2 ** k,10) / k


print(expected_yield(10))
print(expected_yield(30))
print(expected_yield(100000))
print(math.log10(2))

输出为

0.294921875
0.2952809327592452
0.301018918814536
0.3010299956639812

并且您会看到,100000个二进制数字(倒数第二行)非常接近Shannon限制(最后一行)。

从理论上讲,我们将arithmetic decoder应用于所有输出数字对无限位流(解释为0到1之间的随机数)具有相同概率的情况。渐近效率趋于完美,但是您抽取的样本越多,算术运算就越重。这往往是个权衡。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 &lt;select id=&quot;xxx&quot;&gt; SELECT di.id, di.name, di.work_type, di.updated... &lt;where&gt; &lt;if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 &lt;property name=&quot;dynamic.classpath&quot; value=&quot;tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-