请在2024 Clojure 状态调查中分享您的想法!

欢迎!请参阅关于页面以获取更多有关此工作方式的信息。

0
集合
编辑

我知道懒序列可以以分块的方式进行处理。也就是说,如果我的程序需要从懒序列中获取n个元素,实际可能实现多于n个。

我是否可以假设,如果在同一版本的Clojure中再次运行相同的代码,则确定会实现完全相同的元素?我会这样认为,但我不想在不知道的情况下假设。

(我在这包括额外的信息,以防有人对为什么这很重要感兴趣,但我觉得了解我的用例并非回答问题的必要条件。所以请随意跳过此段落。我正在运行使用伪随机数生成器的模拟。这些涉及到如果走动遇到“目标”则被截断的随机游走。预先作为懒序列生成游走很方便,然后在找到目标时停止实现其元素。通常,我会扔掉中间结果,如原始随机游走。它们存在于一种数据结构中,但最后我没有使用它们。然而,我有时想要返回并重新运行模拟,以便检查中间结果。保存用于模拟运行的种子的操作很容易,然后使用它来设置随机数生成器以从相同的状态开始。然而,分块可能导致调用随机数生成器的次数多于严格必要。那没关系,但如果分块会根据代码和Clojure版本之外的某种东西而变化,那么重新运行带有相同种子的模拟不一定保证产生相同的结果。目前这不是一个大问题。我可以通过轻微的性能损失将中间数据变为非懒的。但是,我仍在想这是否是必要的。)

2 答案

+1

选择了
 
最佳答案

你不应该对延迟序列何时实现做出任何假设,所以这不是保证的。

如果你想控制生成过程,可以使用循环/递归或reduce with reduced,或其他类似的技术来获得这种控制。

好的! 我会做到的。 谢谢。
+1

这对我来说没有意义

这可能意味着使用相同的种子重新运行模拟不一定能产生相同的结果

函数被评估的次数似乎无关紧要(例如,对于性能[例如昂贵的函数计算]或其他情况,如纠缠副作用等)。由于你正在从序列中抽取结果,并在某个标准下停止,因此产生这些结果的进程应以相同的输入可重复执行。如果那个迭代过程依赖于自己的PRNG,并使用特定的种子值初始化,并且状态转换函数依赖于一个不可变上下文以及PRNG的当前状态(例如“功能纯”但具有具有状态性PRNG的良性副作用),那么我看不出来你如何可以创建不同的状态序列。如果块生成决定运行22次以上必要次数,它不会影响前面的10次。这10个值(比如说,当消费序列时,第10个值是满足停止标准的时候足够了)被缓存,并且与任何后续的值完全独立,所以序列的不变性得以保持。只要为每个生成的序列隔离一个PRNG,我认为你应该有完整的不独立性和可重复性(当控制因素如PRNG实现、运行时、clojure版本等时)。

我已经进行了超过十年的离散事件模拟工作(主要是确定性的,尽管有时候初始条件是随机的)。可重现性,特别是对于比较验证,以及如您所述,对系统演化(例如中间状态)进行细粒度检查非常重要。如果您控制了种子、PRNG实例以及其他之前提到的因素,这将导致可重现的历史。

如前所述,如果您仍然担心,您可以通过使用迭代和transduce/reduce/into来定义一个不会分块(或生成中间序列)的贪婪迭代过程,以保持熟悉度、性能和控制。我通常是出于性能考虑走这条路,尽管我的模拟在“帧”数量的基数上往往较小,而在状态转换函数复杂性上更为显著。

by
编辑 by
谢谢@Tom。我觉得我没有清晰地说明的是,我想运行一系列独立的模拟(具有不同的参数),而不重新初始化PRNG。(不重新初始化是一个很好的方法,让PRNG确保新数字与先前数字无关。即任何PRNG都可以信任它做到这一点——这是一个不同的话题。)

假设我用一个种子初始化了一个PRNG。然后进行了一个模拟,生成了一个懒序列steps1。我做了一些类似于'take 10000'的事情。每一步都有一个PRNG调用,所以我现在将PRNG推进了10000+k状态,其中k是分块引起的额外处理。然后,使用相同的PRNG但不上种子,从这一点开始,我做了一个不同的模拟,创建了一个懒序列steps2,并在它上面运行'take 10000'。然后,我扔掉了steps2,但我以后想更仔细地检查它,所以我又重新运行了第一模拟(或者也许只是将PRNG向前推进10000+k次,如果我知道k是多少),然后再运行第二个模拟来重新创建steps2。

但是根据@alexmiller的说法,我不应该假设k总是相同的。如果k可能是不同的,那么即使先运行第一个模拟然后再运行第二个模拟也不能保证我真的在重新创建steps2。

如果我不使用懒惰,那么我将确切知道PRNG调用创建steps1的次数——在这个例子中是10000次——要重新创建steps2,只需要给PRNG提供已知的种子,然后扔掉前10000个数字,这不会花费太多时间。然后,我可以在第二个模拟中运行以创建steps2。(这可能是比所需更多的细节。)

另一个选择是读取PRNG的状态并将其存储,然后使用它来初始化它,如果需要重新创建运行。我正在使用PRNG (Well19937c),它的内部状态比我用来初始化它的长种子要大。

(我昨晚清除掉了惰性。我想这可能带来了一些小的性能提升,但大多数处理时间都花在每一步都会运行的例程中,所以差别不大。)







https://www.sciencedirect.com/science/article/pii/S0378475416300829




分解块:哇,这很有趣。 谢谢。
注意从PRNG生成下一个种子作为长数据将WELL19937c的624位状态在运行之间降低到64位状态。

有趣。 看起来Apache Commons Math API的实现允许你获取int数组并将其作为种子存储,如果你想持久化状态,因此将int数组作为种子存储而不是降级到long空间可能是可行的,以防止精度丢失。 感谢您的记录。
谢谢。 是的,我在用同一个种子生成了一系列长实验后开始想知道这一点。 我认为可能需要循环PRNG几十万次才能重新运行后期实验集之一。

我还没有看到读取状态的方法。 你在看什么类? 我可能缺少相关的接口或类层次结构中的某些东西。 你指的是`v` "字节池"字段在`Abstract Well`中吗? 我不得不检查源代码,但这听起来可能是我需要的。 或者你在看Apache Commons Math 4吗? 它似乎还在建设中,所以我一直在使用Math 3.6.1,它的组织方式非常不同。(请随意不要回答——这并不是你的工作为我查找Apache Commons中的内容。)
getStateInternal()在Apache Commons PRNG的开发版本中。
...