#numbers #generator #hierarchical #general-purpose #fundamental #simulation #concurrency

no-std deterministic_rand

支持可切换确定性的并发模拟的分层随机数生成器

5 个版本 (破坏性更新)

0.5.0 2024 年 5 月 11 日
0.4.0 2023 年 12 月 10 日
0.3.0 2023 年 12 月 10 日
0.2.0 2023 年 12 月 2 日
0.1.0 2023 年 12 月 2 日

#2521 in 开发工具


用于 优化工具

MIT 许可证

63KB
645 代码行

模块 :: deterministic_rand

experimental rust-status docs.rs Open in Gitpod discord

支持可切换确定性的并发模拟的分层随机数生成器。

此库引入了专为并发模拟设计的分层随机数生成器,提供了可切换确定性的灵活性。

原因

确定性随机数(也称为伪随机数)在算法解决 NP-hard 问题和多玩家游戏等领域的应用之外,还应用于其他各种领域。以下是一些其他值得注意的应用

密码学:在密码学中,确定性随机性对于生成安全密钥、加密随机数和加密算法至关重要。伪随机数生成器(PRNGs)需要产生与真实随机性不可区分的输出,以确保安全性。

模拟和建模:在物理学、生物学或经济学等科学模拟中,确定性随机性用于模拟具有固有不确定性的复杂系统。这使得结果可重复,这对于验证和验证模型至关重要。

计算机图形:在计算机图形的进程式生成中,如地形或纹理生成,确定性随机性可以创建多样且一致的视觉效果。这在视频游戏和模拟中得到了广泛应用。

负载测试:在软件工程中,确定性随机性用于系统的负载和压力测试。通过以可控、可重复的方式模拟用户行为或系统输入,开发人员可以识别和纠正潜在的性能问题。

机器学习:一些机器学习算法,特别是涉及随机过程(如随机梯度下降)的算法,使用确定性随机性以确保结果的可重复性,同时在训练过程中仍从随机性中受益。

统计抽样:在统计学中,伪随机数生成器用于随机抽样和其他统计方法,其中可重复性至关重要。

量子计算模拟:在经典机器上模拟量子计算机通常需要确定性随机性来模拟量子力学的概率性质。

算法艺术:在生成艺术中,确定性随机性有助于创建复杂且有吸引力的模式和图像,这些模式和图像可以重复。

金融建模:在金融领域,确定性随机性用于蒙特卡洛模拟中的风险评估和期权定价,通过生成多个场景来模拟金融市场的行为。

教育工具和演示:在教授概率和随机性概念时,确定性算法允许教育工作者通过可重复的实验来演示原理。

这些应用利用了确定性随机性在不可预测性和可重复性之间的平衡,使其成为许多领域中的多功能工具。

非确定性的来源

随机数生成器是非确定性的最明显来源,但它不是唯一的。程序中随机性的来源包括

  • 随机数生成器(例如,rand
  • 并行性(例如,rayon
  • 标准库(例如,HashMap和HashSet的键)
  • 系统时间
  • 内存地址
  • 数据库查询结果
  • 量子随机性

deterministic_rand提供了处理前三个随机性来源的方法。

基本用例

最简单的用例。只需生成一个随机数。

#[ cfg( not( feature = "no_std" ) ) ]
{
  // `Rng`` is re-exported from `rand` and `Hrng` stands for hierarchical random number generators.
  use deterministic_rand::{ Rng, Hrng };
  // Make master random number generator with a seed.
  let hrng = Hrng::master_with_seed( "master1".into() );
  // Get a reference to the current random number generator using a reference counter and mutex.
  let rng_ref = hrng.rng_ref();
  // Lock it producing a guard.
  let mut rng = rng_ref.lock().unwrap();
  // Generate a number.
  let got : u64 = rng.gen();
  // If determinism is enabled then sequence of generated rundom numbers will be the same.
  #[ cfg( feature = "determinism" ) ]
  assert_eq!( got, 8185996568056992464 );
}

如何处理由并行性引起的非确定性

为了解决由并行性引起的非确定性,HRNG为每个数据流通道创建一个子随机数生成器。关键是不要将生成器绑定到线程ID,而是绑定到批次ID。这确保了无论哪个线程处理工作,随机数的序列都保持一致,并且完全由批次ID决定。

内部,分层随机数生成器使用专用RNG来产生后代。这确保了无论何时创建子生成器,结果都是一致的。此外,这种方法通过最小化父生成器和子生成器在共享资源上的并发冲突来提高性能。

如果您没有批次ID,请考虑对您的项目进行编号,并使用键作为批次ID。

// Import necessary traits and modules from the `rayon` and `deterministic_rand` crates.
use rayon::prelude::*;
use deterministic_rand::{ distributions::Uniform, Rng, Hrng };

// Define a range for random number generation between -1.0 and 1.0.
let range = Uniform::new( -1.0f64, 1.0 );

// Create a master hierarchical random number generator (HRNG).
let manager = Hrng::master();

// Launch a parallel iteration over a range of numbers (0 to 999).
let got = ( 0..1000 )
.into_par_iter()
.map
(
  | i |
  {
    // For each barch, create a child HRNG tied to the current batch ID.
    let child = manager.child( i );
    // Get a reference to current RNG.
    let rng = child.rng_ref();
    // Lock the RNG to ensure safe access in the concurrent context.
    let mut rng = rng.lock().unwrap();

    // Initialize a counter for each iteration.
    let mut count = 0;
    // Perform 10,000 random draws.
    for _ in 0..10_000
    {
      // Sample two numbers from the range and calculate their positions.
      let a = rng.sample( &range );
      let b = rng.sample( &range );

      // If the point (a, b) lies within a unit circle, increment the count.
      if a * a + b * b <= 1.0
      {
        count += 1;
      }
    }

    // Return the count for this iteration.
    count
  }
)
// Sum the counts from all iterations.
.sum::< u64 >();

// Calculate an approximation of Pi using the Monte Carlo method.
let got_pi = 4. * ( got as f64 ) / ( ( 10_000 * 1000 ) as f64 );

// If determinism is enabled, assert that the calculated value of Pi matches the expected result.
#[ cfg( not( feature = "no_std" ) ) ]
#[ cfg( feature = "determinism" ) ]
assert_eq!( got_pi, 3.1410448 );

// Print the calculated value of Pi.
println!( "PI = {got_pi}" );

如何处理STD非确定性

在标准库中,随机性也可能是一个因素;例如,迭代HashMap或HashSet的键是非确定的。为了实现确定性枚举,您可以使用迭代器的deterministic_rand::IfDeterminismIteratorExt扩展。通过在处理键之前应用if_determinism_then_sortif_determinism_then_sort_by,您可以确保一致的顺序。当确定性关闭时,if_determinism_then_sort_by方法充当无操作(no-op),但当确定性打开时,它执行排序。

// Import the necessary modules from the standard library and the `deterministic_rand` crate.
use std::collections::HashMap;
use deterministic_rand::IfDeterminismIteratorExt;

// Create a HashMap with three key-value pairs.
let map: HashMap<_, _> = HashMap::from_iter( [ ( 1, "first" ), ( 2, "second" ), ( 3, "third" ) ] );

// Convert the HashMap into an iterator, apply deterministic sorting to the keys,
// and then map each (key, value) pair to just the value.
let keys: Vec< _ > = map
.into_iter()
.if_determinism_then_sort_by( | ( a, _ ), ( b, _ ) | a.cmp( &b ) )
.map( | e | e.1 )
.collect();

// If the 'determinism' feature is enabled, assert that the sorted keys match the expected order.
// This is a conditional compilation check that ensures the code block is compiled and run only
// if the 'determinism' feature is enabled.
#[ cfg( feature = "determinism" ) ]
assert_eq!( keys, vec![ "first", "second", "third" ] );

将它们添加到您的项目中

cargo add deterministic_rand

从存储库中试用

git clone https://github.com/Wandalen/wTools
cd wTools
cargo run --example sample_deterministic_rand_trivial

依赖关系

~1.9–2.7MB
~49K SLoC