tinyrand ข้อกำหนด RNG ที่มีน้ำหนักเบาและการใช้งานที่รวดเร็วในการเกิดสนิม tinyrand เป็น no_std และไม่ได้ใช้การจัดสรรกอง
tinyrand ?std ซึ่งหมายความว่ามันสามารถฝังได้-มันทำงานบนไมโครคอนโทรลเลอร์และสภาพแวดล้อมโลหะเปลือย (ไม่มีระบบปฏิบัติการ)Mock สำหรับรหัสทดสอบที่ขึ้นอยู่กับตัวเลขสุ่ม นั่นคือถ้าคุณใส่ใจเกี่ยวกับการครอบคลุมรหัสด้านล่างคือการเปรียบเทียบ prngs ที่โดดเด่นหลายอย่าง
| prng | อัลกอริทึม | แบนด์วิดท์ (GB/S) | |
|---|---|---|---|
rand | Chacha12 | 2.4 | |
tinyrand | มิกซ์มิกซ์ | 6.5 | |
tinyrand | Xorshift | 6.7 | |
fastrand | เครื่องราง | 7.5 | |
tinyrand | เครื่องราง | 14.6 |
TL; DR: tinyrand เร็วกว่า fastrand 2x และเร็วกว่า rand 6x 2x
เป็นไปไม่ได้ที่จะบอกอย่างแน่นอนว่า prng บางอย่างดีหรือไม่ คำตอบคือความน่าจะเป็น อัลกอริธึมทั้งสามยืนขึ้นได้ดีกับการทดสอบมิจฉาทิฐิ แต่ Wyrand และ Splitmix นั้นดีกว่า Xorshift เล็กน้อย (ทดสอบกับตัวอย่าง 30.8 พันล้านตัวอย่าง) นี่หมายความว่า tinyrand สร้างตัวเลขที่ปรากฏแบบสุ่มเพียงพอและน่าจะเหมาะสมสำหรับการใช้งานในแอปพลิเคชันส่วนใหญ่
อัลกอริทึม tinyrand ไม่ปลอดภัยในการเข้ารหัสซึ่งหมายความว่าเป็นไปได้ที่จะเดาหมายเลขสุ่มถัดไปโดยการสังเกตลำดับของตัวเลข (หรือตัวเลขก่อนหน้านี้สำหรับเรื่องนั้น) หากคุณต้องการ CSPRNG ที่แข็งแกร่งขอแนะนำอย่างยิ่งว่าคุณจะไปกับ rand CSPRNG โดยทั่วไปช้ากว่ามากและคนส่วนใหญ่ไม่ต้องการ
cargo add tinyrand จำเป็นต้องมีอินสแตนซ์ Rand เพื่อสร้างตัวเลข ที่นี่เราใช้ StdRand ซึ่งเป็นนามแฝงสำหรับ RNG เริ่มต้น/แนะนำ (ปัจจุบันตั้งค่าเป็น Wyrand แต่อาจเปลี่ยนแปลงในอนาคต)
use tinyrand :: { Rand , StdRand } ;
let mut rand = StdRand :: default ( ) ;
for _ in 0 .. 10 {
let num = rand . next_u64 ( ) ;
println ! ( "generated {num}" ) ;
}ในทำนองเดียวกันเราสามารถสร้างจำนวนประเภทอื่น ๆ :
use tinyrand :: { Rand , StdRand } ;
let mut rand = StdRand :: default ( ) ;
let num = rand . next_u128 ( ) ;
println ! ( "generated wider {num}" ) ; วิธีการ next_uXX สร้างตัวเลขในช่วงที่ไม่ได้ลงชื่อทั้งหมดของประเภทที่ระบุ บ่อยครั้งที่เราต้องการตัวเลขในช่วงเฉพาะ:
use tinyrand :: { Rand , StdRand , RandRange } ;
let mut rand = StdRand :: default ( ) ;
let tasks = vec ! [ "went to market" , "stayed home" , "had roast beef" , "had none" ] ;
let random_index = rand . next_range ( 0 ..tasks . len ( ) ) ;
let random_task = tasks [ random_index ] ;
println ! ( "This little piggy {random_task}" ) ; อีกกรณีการใช้งานทั่วไปคือการสร้าง bool s เราอาจต้องการกำหนดน้ำหนักให้กับผลลัพธ์ไบนารี:
use tinyrand :: { Rand , StdRand , Probability } ;
let mut rand = StdRand :: default ( ) ;
let p = Probability :: new ( 0.55 ) ; // a slightly weighted coin
for _ in 0 .. 10 {
if rand . next_bool ( p ) {
// expect to see more heads in the (sufficiently) long run
println ! ( "heads" ) ;
} else {
println ! ( "tails" ) ;
}
}มีหลายครั้งที่เราต้องการให้ด้ายของเรานอนหลับอยู่พักหนึ่งรอเงื่อนไข เมื่อหลายเธรดกำลังนอนหลับแนะนำโดยทั่วไปพวกเขาจะกลับมาแบบสุ่มเพื่อหลีกเลี่ยงการแตกตื่น
use tinyrand :: { Rand , StdRand , RandRange } ;
use core :: time :: Duration ;
use std :: thread ;
use tinyrand_examples :: SomeSpecialCondition ;
let mut rand = StdRand :: default ( ) ;
let condition = SomeSpecialCondition :: default ( ) ;
let base_sleep_micros = 10 ;
let mut waits = 0 ;
while !condition . has_happened ( ) {
let min_wait = Duration :: ZERO ;
let max_wait = Duration :: from_micros ( base_sleep_micros * 2u64 . pow ( waits ) ) ;
let random_duration = rand . next_range ( min_wait..max_wait ) ;
println ! ( "backing off for {random_duration:?}" ) ;
thread :: sleep ( random_duration ) ;
waits += 1 ;
} การเรียกใช้ Default::default() บน Rand เริ่มต้นด้วยเมล็ดคงที่ นี่เป็นสิ่งที่ยอดเยี่ยมสำหรับการทำซ้ำ แต่ส่งผลให้มีตัวเลข "สุ่ม" เดียวกันซึ่งไม่ใช่สิ่งที่คนส่วนใหญ่ต้องการ
tinyrand เป็นลัง no_std และน่าเศร้าที่ไม่มีวิธีที่ดีพกพาในการสร้างเอนโทรปีเมื่อไม่สามารถตั้งสมมติฐานเกี่ยวกับแพลตฟอร์มพื้นฐานได้ ในแอพพลิเคชั่นส่วนใหญ่อาจมีนาฬิกา แต่มีบางอย่างที่ไม่สำคัญเท่า SystemTime::now().duration_since(SystemTime::UNIX_EPOCH) อาจไม่สามารถใช้ได้เสมอไป
หากคุณมีแหล่งที่มาของเอนโทรปีในการกำจัดของคุณคุณสามารถเพาะพันธุ์ Rrnd ได้เช่นกัน:
use tinyrand :: { Rand , StdRand , Seeded } ;
let seed = tinyrand_examples :: get_seed_from_somewhere ( ) ; // some source of entropy
let mut rand = StdRand :: seed ( seed ) ;
let num = rand . next_u64 ( ) ;
println ! ( "generated {num}" ) ; คุณอาจพิจารณาใช้ getrandom ซึ่งเป็นวิธีการข้ามแพลตฟอร์มสำหรับการดึงข้อมูลเอนโทรปี
หากไม่มีใครสนใจเกี่ยวกับ no_std พวกเขาไม่ควรผูกพันตามข้อ จำกัด ในการเพาะเมล็ดจากนาฬิการะบบคุณสามารถเลือกใช้ std :
cargo add tinyrand-std ตอนนี้เรามี ClockSeed เมื่อกำจัดของเราซึ่งใช้ลักษณะ Rand ClockSeed จับเวลาได้มาจาก u64 โดย Xoring ส่วนบน 64 บิตของการประทับเวลานาโนวินาที (จาก SystemTime ) ด้วย 64 บิตที่ต่ำกว่า มันไม่เหมาะสำหรับการใช้เข้ารหัส แต่จะเพียงพอสำหรับการใช้งานที่มีวัตถุประสงค์ทั่วไปส่วนใหญ่
use tinyrand :: { Rand , StdRand , Seeded } ;
use tinyrand_std :: clock_seed :: ClockSeed ;
let seed = ClockSeed :: default ( ) . next_u64 ( ) ;
println ! ( "seeding with {seed}" ) ;
let mut rand = StdRand :: seed ( seed ) ;
let num = rand . next_u64 ( ) ;
println ! ( "generated {num}" ) ; ลัง tinyrand-std ยังรวมถึงการใช้ Rand ที่มีเมล็ดในท้องถิ่น:
use tinyrand :: Rand ;
use tinyrand_std :: thread_rand ;
let mut rand = thread_rand ( ) ;
let num = rand . next_u64 ( ) ;
println ! ( "generated {num}" ) ; ความครอบคลุมการทดสอบที่ดีบางครั้งอาจเป็นเรื่องยากที่จะบรรลุ ดังนั้นเมื่อแอปพลิเคชันขึ้นอยู่กับการสุ่มหรือแหล่งอื่น ๆ ของการไม่ใช้ tinyrand มาพร้อมกับ RNG จำลองที่ให้การควบคุมอย่างละเอียดเกี่ยวกับการดำเนินการรหัสของคุณ
เยาะเย้ยใช้ Crate alloc เนื่องจากต้องมีการจัดสรรการปิดกอง ดังนั้นการเยาะเย้ยจะถูกแจกจ่ายเป็นแพ็คเกจการเลือกใช้:
cargo add tinyrand-alloc ในระดับรากหญ้า Mock ได้รับการกำหนดค่าโครงสร้างด้วย ผู้ได้รับมอบหมาย จำนวนหนึ่ง ผู้ได้รับมอบหมายคือการปิดที่ถูกเรียกใช้โดยการเยาะเย้ยเมื่อวิธีการที่มีลักษณะเฉพาะถูกเรียกโดยระบบภายใต้การทดสอบ การเยาะเย้ยยังคงรักษาสถานะการเรียกร้องภายในที่ติดตามจำนวนครั้งที่มีการใช้ผู้แทนโดยเฉพาะ ดังนั้นไม่เพียง แต่คุณสามารถเยาะเย้ยพฤติกรรมของลักษณะ Rand เท่านั้น แต่ยังตรวจสอบจำนวนประเภทของกลุ่มวิธีการที่เกี่ยวข้องที่เรียกว่า
ผู้ได้รับมอบหมายจะถูกระบุโดยกรณีทดสอบในขณะที่อินสแตนซ์จำลองจะถูกส่งผ่านไปยังระบบภายใต้การทดสอบเป็นการใช้งาน Rand ปัจจุบันรองรับผู้แทนสามประเภท:
FnMut(&State) -> u128 -เรียกใช้เมื่อวิธีการหนึ่งใน next_uXX() หนึ่งวิธีถูกเรียกในการเยาะเย้ย ( uXX เป็นหนึ่งใน u16 , u32 , u64 , u128 หรือ usize ) ผู้แทนจะส่งคืนหมายเลข "สุ่ม" ต่อไปซึ่งอาจกว้างถึง 128 บิต ความกว้างถูกออกแบบมาเพื่อรองรับ u128 - ประเภทที่กว้างที่สุดที่รองรับโดย Rand หากมีการร้องขอประเภทใดประเภทหนึ่งที่แคบกว่านี้การเยาะเย้ยจะส่งคืนบิตที่ต่ำกว่า (เช่นสำหรับ u32 ค่าล้อเลียนจะถูกตัดทอนโดยใช้ as u32 ภายใต้ฮูด)FnMut(Surrogate, Probability) -> bool -เรียกใช้เมื่อวิธีการ next_bool(Probability) ถูกเรียกFnMut(Surrogate, u128) -> u128 -เมื่อเรียกว่า next_lim หรือ next_range เริ่มต้นด้วยพื้นฐานที่แน่นอนมาเยาะเย้ย next_uXX() เพื่อส่งคืนค่าคงที่ จากนั้นเราจะตรวจสอบว่าการเยาะเย้ยของเราถูกเรียกกี่ครั้ง
use tinyrand :: Rand ;
use tinyrand_alloc :: Mock ;
let mut rand = Mock :: default ( ) . with_next_u128 ( |_| 42 ) ;
for _ in 0 .. 10 {
assert_eq ! ( 42 , rand.next_usize ( ) ) ; // always 42
}
assert_eq ! ( 10 , rand.state ( ) .next_u128_invocations ( ) ) ; แม้ว่าจะเรียบง่ายอย่างน่าอาย แต่สถานการณ์นี้ก็ค่อนข้างธรรมดา สามารถทำได้ด้วยฟังก์ชั่น fixed(uXX)
use tinyrand :: Rand ;
use tinyrand_alloc :: { Mock , fixed } ;
let mut rand = Mock :: default ( ) . with_next_u128 ( fixed ( 42 ) ) ;
assert_eq ! ( 42 , rand.next_usize ( ) ) ; // always 42เนื่องจากผู้ได้รับมอบหมายเป็นการปิดปกติเราจึงสามารถผูกกับตัวแปรในขอบเขตการล้อมรอบ สิ่งนี้ทำให้เรามีการควบคุมพฤติกรรมจำลองของเราเกือบไม่ จำกัด
use tinyrand :: Rand ;
use tinyrand_alloc :: Mock ;
use core :: cell :: RefCell ;
let val = RefCell :: new ( 3 ) ;
let mut rand = Mock :: default ( ) . with_next_u128 ( |_| * val . borrow ( ) ) ;
assert_eq ! ( 3 , rand.next_usize ( ) ) ;
// ... later ...
* val . borrow_mut ( ) = 17 ;
assert_eq ! ( 17 , rand.next_usize ( ) ) ;ผู้ได้รับมอบหมายสามารถกำหนดใหม่ได้ทุกจุดแม้หลังจากที่มีการสร้างและออกกำลังกายแบบจำลอง:
use tinyrand :: Rand ;
use tinyrand_alloc :: { Mock , fixed } ;
let mut rand = Mock :: default ( ) . with_next_u128 ( fixed ( 42 ) ) ;
assert_eq ! ( 42 , rand.next_usize ( ) ) ;
rand = rand . with_next_u128 ( fixed ( 88 ) ) ; // the mock's behaviour is now altered
assert_eq ! ( 88 , rand.next_usize ( ) ) ; ลายเซ็นของผู้แทน next_u128 ใช้การอ้างอิง State ซึ่งจับจำนวนครั้งที่การเยาะเย้ยถูกเรียกใช้ (การนับเพิ่มขึ้นหลังจากการร้องขอเสร็จสมบูรณ์) เรามาเขียนการเยาะเย้ยที่ส่งคืนหมายเลข "สุ่ม" ที่ได้จากสถานะการเรียกร้อง
use tinyrand :: Rand ;
use tinyrand_alloc :: Mock ;
let mut rand = Mock :: default ( ) . with_next_u128 ( |state| {
// return number of completed invocations
state . next_u128_invocations ( ) as u128
} ) ;
assert_eq ! ( 0 , rand.next_usize ( ) ) ;
assert_eq ! ( 1 , rand.next_usize ( ) ) ;
assert_eq ! ( 2 , rand.next_usize ( ) ) ; สิ่งนี้มีประโยชน์เมื่อเราคาดหวังว่าการเยาะเย้ยจะถูกเรียกหลายครั้งและการร้องขอแต่ละครั้งควรส่งคืนผลลัพธ์ที่แตกต่างกัน ผลลัพธ์ที่คล้ายกันสามารถทำได้ด้วยฟังก์ชั่น counter(Range) ซึ่งวนผ่านช่วงของตัวเลขที่ระบุไว้อย่างสะดวกในขอบเขต:
use tinyrand :: Rand ;
use tinyrand_alloc :: { Mock , counter } ;
let mut rand = Mock :: default ( ) . with_next_u128 ( counter ( 5 .. 8 ) ) ;
assert_eq ! ( 5 , rand.next_usize ( ) ) ;
assert_eq ! ( 6 , rand.next_usize ( ) ) ;
assert_eq ! ( 7 , rand.next_usize ( ) ) ;
assert_eq ! ( 5 , rand.next_usize ( ) ) ; // start again โดยการจัดหาผู้แทน next_u128 เราสามารถมีอิทธิพลต่อผลลัพธ์ของวิธีการอื่น ๆ ในลักษณะ Rand เพราะพวกเขาทั้งหมดได้มาจากแหล่งที่มาของการสุ่ม เดียวกัน และในที่สุดจะเรียกผู้แทนของเราภายใต้ประทุน ... ในทฤษฎี! ในทางปฏิบัติสิ่งต่าง ๆ มีความซับซ้อนมากขึ้น
วิธี Rand ที่ได้รับเช่น next_bool(Probability) , next_lim(uXX) และ next_range(Range) ได้รับการสนับสนุนโดยการแจกแจงความน่าจะเป็นที่แตกต่างกัน ตัวอย่างเช่น next_bool ดึงมาจากการแจกแจงของ Bernoulli ในขณะที่ next_lim และ next_range ใช้การกระจายเครื่องแบบที่ปรับขนาดด้วยเลเยอร์ debiasing เพิ่มเติม นอกจากนี้การแมประหว่างการแจกแจงต่าง ๆ เป็นรายละเอียดการใช้งานภายในที่อาจมีการเปลี่ยนแปลง เลเยอร์ debiasing เพียงอย่างเดียวมีการใช้งานหลายอย่างที่ปรับให้เหมาะสมสำหรับประเภทของความกว้างที่แตกต่างกัน กล่าวอีกนัยหนึ่งการแมปจาก next_u128 ถึง next_bool , next_lim และ next_range และ nontrivial; ไม่ใช่สิ่งที่คุณต้องการเยาะเย้ยโดยไม่มีเครื่องคิดเลขและความรู้เกี่ยวกับเลขคณิตแบบแยกส่วน
โชคดีที่ Rand ให้เรา "บายพาส" ฟังก์ชั่นการทำแผนที่เหล่านี้ นี่คือที่ที่ผู้ได้รับมอบหมายอีกสองคนเข้ามาในตัวอย่างต่อไปนี้เราเยาะเย้ยผลลัพธ์ของ next_bool
use tinyrand :: { Rand , Probability } ;
use tinyrand_alloc :: Mock ;
let mut rand = Mock :: default ( ) . with_next_bool ( |_ , _| false ) ;
if rand . next_bool ( Probability :: new ( 0.999999 ) ) {
println ! ( "very likely" ) ;
} else {
// we can cover this branch thanks to the magic of mocking
println ! ( "very unlikely" ) ;
} ผู้แทน next_bool ได้รับการส่งมอบโครงสร้าง Surrogate ซึ่งเป็นทั้งการใช้งาน Rand และผู้รักษาสถานะการร้องขอ ตัวแทนช่วยให้เราได้รับ bool s เช่น:
use tinyrand :: { Rand , Probability } ;
use tinyrand_alloc :: Mock ;
let mut rand = Mock :: default ( ) . with_next_bool ( |surrogate , _| {
surrogate . state ( ) . next_bool_invocations ( ) % 2 == 0
} ) ;
assert_eq ! ( true , rand.next_bool ( Probability ::new ( 0.5 ) ) ) ;
assert_eq ! ( false , rand.next_bool ( Probability ::new ( 0.5 ) ) ) ;
assert_eq ! ( true , rand.next_bool ( Probability ::new ( 0.5 ) ) ) ;
assert_eq ! ( false , rand.next_bool ( Probability ::new ( 0.5 ) ) ) ;ตัวแทนยังช่วยให้ผู้แทนเรียกวิธีการเยาะเย้ยจากภายในเยาะเย้ย
ผู้แทนคนสุดท้ายใช้เพื่อเยาะเย้ยทั้งวิธี next_lim และ next_range เนื่องจาก isomorphism ภายใต้ฮูดผู้แทน next_range ไปยัง next_lim เช่นนั้นสำหรับขอบเขตขีด จำกัด คู่ใด ๆ ( M , N ), M < N , next_range(M..N) = M + next_lim(N - M) นี่คือวิธีการเยาะเย้ยทั้งหมดในทางปฏิบัติ:
use tinyrand :: { Rand , RandRange } ;
use tinyrand_alloc :: Mock ;
enum Day {
Mon , Tue , Wed , Thu , Fri , Sat , Sun
}
const DAYS : [ Day ; 7 ] = [ Day :: Mon , Day :: Tue , Day :: Wed , Day :: Thu , Day :: Fri , Day :: Sat , Day :: Sun ] ;
let mut rand = Mock :: default ( ) . with_next_lim_u128 ( |_ , _| 6 ) ;
let day = & DAYS [ rand . next_range ( 0 .. DAYS . len ( ) ) ] ;
assert ! ( matches! ( day, Day :: Sun ) ) ; // always a Sunday
assert ! ( matches! ( day, Day :: Sun ) ) ; // yes!!!tinyrand ทดสอบอย่างไร? ส่วนนี้อธิบายวิธีการทดสอบ tinyrand สั้น ๆ มันมุ่งเป้าไปที่ผู้ที่ -
กระบวนการทดสอบ tinyrand แบ่งออกเป็นสี่ระดับ:
tinyrand กล่าวอีกนัยหนึ่งรหัสทุกบรรทัดจะถูกใช้อย่างน้อยหนึ่งครั้งความคาดหวังพื้นฐานจะได้รับการรักษาและมี แนวโน้มที่จะ ไม่มีข้อบกพร่องเล็กน้อยtinyrand สิ่งเหล่านี้เป็นการทดสอบสมมติฐานอย่างเป็นทางการที่สันนิษฐานว่าแหล่งที่มานั้นสุ่ม (สมมติฐานว่าง) และมองหาหลักฐานที่จะขจัดสมมติฐานนี้ (สมมติฐานทางเลือก)การทดสอบหน่วยไม่ได้มีวัตถุประสงค์เพื่อยืนยันคุณสมบัติเชิงตัวเลข พวกเขาทำงานได้อย่างหมดจดในธรรมชาติ วัตถุประสงค์ ได้แก่ -
tinyrand ถูกสร้างขึ้นบนปรัชญาว่าหากมีการใช้รหัสบรรทัดที่พิสูจน์ได้ก็ควรลบออก ไม่มีข้อยกเว้นสำหรับกฎนี้true เมื่อเทียบกับ false ในการสร้าง bool s ฟังก์ชั่นสำหรับการทำแผนที่จากการแจกแจงแบบสม่ำเสมอไปยังแบบกำหนดเองนั้นเป็นสิ่งที่ไม่น่าสนใจและต้องใช้เลเยอร์ debiasing tinyrand ใช้วิธีการ debiasing ที่แตกต่างกันขึ้นอยู่กับความกว้างของคำ จุดประสงค์ของการทดสอบการแปลงโดเมนคือการตรวจสอบว่าฟังก์ชันนี้ทำงานได้ตามที่คาดไว้และการสุ่มตัวอย่างการปฏิเสธกำลังเกิดขึ้น อย่างไรก็ตามมันไม่ได้ตรวจสอบคุณสมบัติเชิงตัวเลขของการ debiasing เกณฑ์มาตรฐานสังเคราะห์ใช้ในการใช้เส้นทางร้อนของ tinyrand prngs เปรียบเทียบผลลัพธ์กับไลบรารีเพียร์ เกณฑ์มาตรฐานทดสอบการสร้างตัวเลขที่ความยาวคำต่าง ๆ แปลง/debiasing และการสร้าง bool น้ำหนัก ชุดย่อยของเกณฑ์มาตรฐานเหล่านี้รวมอยู่ในการทดสอบ CI ทำให้ง่ายขึ้นเล็กน้อยในการเปรียบเทียบประสิทธิภาพของ tinyrand ในรุ่นที่กระทำ
tinyrand มาพร้อมกับชุดทดสอบทางสถิติแบบบูรณาการซึ่งได้รับแรงบันดาลใจจากการชอบของ Diehard, Dieharder และ NIST SP 800-22 tinyrand Suite นั้นมีขนาดเล็กกว่าการทดสอบเหล่านี้มาก ความตั้งใจที่จะไม่ทำซ้ำงานที่สำคัญและเข้าถึงได้ง่ายในพื้นที่นี้ แต่เพื่อสร้างเครือข่ายความปลอดภัยที่มีประสิทธิภาพมากในการตรวจจับความผิดปกติทั่วไปและเร็วพอที่จะทำงานทุกครั้ง
รวมการทดสอบต่อไปนี้
Rand โดยการปิดบังค่าของบิตเดียวตรวจสอบว่าจำนวนครั้งที่บิตถูกตั้งค่าเป็น 1 อยู่ในช่วงที่คาดไว้ สำหรับการทดลองครั้งต่อไปแต่ละครั้งหน้ากากจะถูกเลื่อนไปทางซ้ายและสมมติฐานจะถูกทดสอบซ้ำ การทดสอบดำเนินไปหลายรอบ แต่ละรอบประกอบด้วยการทดลอง 64 Bernoulli (หนึ่งรอบสำหรับแต่ละบิตของ u64 )bool ที่มีความน่าจะเป็นที่เลือกจากคำที่ไม่ได้ลงนาม 64 บิต การทดสอบประกอบด้วยชุดของการทดลอง Bernoulli ที่มีการถ่วงน้ำหนัก (สุ่มเลือก) ที่แตกต่างกันในการทดลองแต่ละครั้ง ภายในการทดลองแต่ละครั้ง H0 ยืนยันว่าแหล่งที่มานั้นสุ่ม (เช่นจำนวน 'หัว' อยู่ในช่วงเวลาที่ยอมรับได้ทางสถิติ)u64 S ที่สร้างขึ้นในการทดลองแยกต่างหาก ในการทดลองแต่ละครั้งเราคิดว่าค่าของบิตแต่ละบิตนั้นมีความน่าจะเป็น 0.5 โดยตรวจสอบว่าจำนวนครั้งที่บิตถูกตั้งค่าเป็น 1 อยู่ในช่วงที่คาดหวัง สำหรับแหล่งที่มาสุ่มจำนวน 1s (และ 0s) เป็นไปตามกระบวนการ Bernoulli การทดสอบของ tinyrand แต่ละครั้งนั้นไม่เพียง แต่ใช้กับ PRNG ของตัวเองเท่านั้น แต่ยังรวมถึงการใช้งานที่ผิดพลาดโดยเจตนาซึ่งใช้เพื่อตรวจสอบประสิทธิภาพของการทดสอบ การทดสอบจะต้องล้มเหลวอย่างต่อเนื่องในการปฏิเสธ H0 สำหรับ PRNG ที่ถูกต้องและยอมรับ H1 สำหรับการทำผิดพลาด
การทดสอบทางสถิตินั้นได้รับการเพาะจากค่าสุ่ม การสุ่มใช้ในการเพาะเมล็ด PRNGs ภายใต้การทดสอบ (การทดลองทุกครั้งมีการเพาะเมล็ดอย่างอิสระ) กำหนดน้ำหนักให้กับการทดลอง Bernoulli เลือกช่วงจำนวนเต็มสำหรับการทดสอบฟังก์ชั่นการแปลงและการ debiasing ค่าควบคุมสำหรับการทดสอบการชนและอื่น ๆ เราใช้แพ็คเกจ rand เป็นตัวควบคุม PRNG เพื่อให้ข้อบกพร่องใน tinyrand ไม่สามารถล้มล้างการทดสอบโดยไม่ได้ตั้งใจในลักษณะที่ปิดบังตัวเอง การทดสอบจะถูกเพาะเพื่อให้ในขณะที่พวกเขาดูเหมือนจะอยู่ในการทัศนศึกษาแบบสุ่มผ่านพื้นที่พารามิเตอร์การเลือกพารามิเตอร์ของพวกเขานั้นสามารถกำหนดได้ทั้งหมดและสามารถทำซ้ำได้ นี่เป็นสิ่งสำคัญเนื่องจากความเป็นไปได้ของข้อผิดพลาด Type I (ปฏิเสธสมมติฐานว่าง) ซึ่งไม่ได้รับอนุญาตให้เกิดขึ้นเป็นระยะ ๆ โดยเฉพาะอย่างยิ่งในสภาพแวดล้อม CI กล่าวอีกนัยหนึ่ง การทดสอบการสุ่มไม่สามารถปล่อยให้มีโอกาส ได้
วิธีหนึ่งในการทดสอบสมมติฐานการสุ่มคือการเลือกชุดพารามิเตอร์ (เช่นช่วงการสร้างจำนวนเต็ม M .. N หรือความน่าจะเป็นที่จะได้รับ true จากการแจกแจง Bernoulli) และดำเนินการในระยะยาว . เหตุผลก็คือยิ่งตัวอย่างมีขนาดใหญ่เท่าไหร่ก็ยิ่งมีโอกาสมากขึ้นเท่านั้นที่จะมีความผิดปกติที่ตรวจพบได้ โดยทั่วไปแล้วจะไม่มีประสิทธิภาพมากนักสำหรับการจำความผิดปกติบางชนิดที่อาจส่งผลกระทบต่อ PRNGs ภายใต้เงื่อนไขที่เฉพาะเจาะจงมากเท่านั้น ตัวอย่างเช่นฟังก์ชั่น debiasing ที่เขียนได้ไม่ดีอาจทำงานได้ดีสำหรับช่วงจำนวนเต็มขนาดเล็กส่วนใหญ่และแม้แต่ฟังก์ชั่นขนาดใหญ่ (บางอย่างที่อยู่ใกล้กับพลังของสอง) หากการทดสอบเลือกพารามิเตอร์ที่ไม่พึงประสงค์อาจไม่พบความผิดปกติไม่ว่าจะทดสอบพารามิเตอร์เหล่านั้นอย่างละเอียดเพียงใด
วิธีที่ดีกว่าในการทดสอบ PRNG คือการแนะนำความหลากหลายในระบอบการทดสอบ - ดำเนินการทดลองขนาดเล็กจำนวนมากที่มีพารามิเตอร์ที่แตกต่างกันมากกว่าการทดลองที่มีขนาดใหญ่มาก นี่คือสิ่งที่การทดสอบทางสถิติ tinyrand ทำ - ดำเนินการทดลองหลายครั้งด้วยพารามิเตอร์ที่เลือกแบบสุ่ม (แต่กำหนด) สิ่งนี้จะเปิดเผยปัญหาการเปรียบเทียบหลายครั้งทันที พิจารณา prng ที่เหมาะสม ที่สุด มันมักจะสร้างตัวเลขที่จะปรากฏ "สุ่ม" ตามมาตรการที่ตกลงกันไว้ แต่ในบางครั้งมันจะสร้างผลลัพธ์ที่จะปรากฏว่าไม่มีการสุ่มโดยการวัดเดียวกัน แม้แต่แหล่งที่มาในอุดมคติก็จะสร้างความยาวหรือศูนย์ได้เช่น ในความเป็นจริงความล้มเหลวที่จะทำเช่นนั้นก็จะทำให้มันไม่ได้สุ่ม น่าเสียดายที่สิ่งนี้จะสร้างค่า p ที่จะล้มเหลวแม้กระทั่งการทดสอบที่ผ่อนคลายที่สุด ... ในบางจุด นี่เป็นปัญหาสำหรับการทดสอบสมมติฐานเดี่ยว แต่มันก็รุนแรงขึ้นตามสัดส่วนในการทดสอบสมมติฐานหลายครั้ง
การทดสอบ tinyrand ในตัวจัดการปัญหานี้โดยใช้วิธีการแก้ไขตามลำดับ Holm-Bonferroni การแก้ไข Holm-Bonferroni ยับยั้งข้อผิดพลาด Type I ในขณะที่รักษาพลังทางสถิติที่ดี-การปราบปรามข้อผิดพลาด Type II ดูเหมือนว่าจะทำงานได้ดีสำหรับความต้องการของ tinyrand โดยเฉพาะอย่างยิ่งเมื่อเห็นว่าการทดลองจำนวนโดยทั่วไปจะถูกเก็บไว้ในช่วง 100-1000 (การทดสอบ tinyrand ได้รับการออกแบบให้รวดเร็วมากซึ่งทำให้มีการปฏิบัติตามจำนวนการทดลอง - โดยหลักการแล้วการทดสอบทางสถิติทั้งหมดควรเสร็จสิ้นภายในไม่กี่วินาทีเพื่อให้พวกเขาได้รับคำสั่งเป็นส่วนหนึ่งของการพัฒนาตามปกติ)
ชุดทดสอบ Dieharder ขยายแบตเตอรี่แบบดั้งเดิมของ Marsaglia มันมาพร้อมกับการทดสอบจำนวนมากและใช้เวลานาน (~ 1 ชั่วโมง) เพื่อให้เสร็จสมบูรณ์ tinyrand มียูทิลิตี้สำหรับการปั๊มเอาต์พุตแบบสุ่มไปยัง Dieharder ซึ่งโดยทั่วไปจะทำงานบนพื้นฐานเฉพาะกิจ แบตเตอรี่ที่ตายแล้วควรทำงานเมื่อ PRNG ผ่านการเปลี่ยนแปลงวัสดุซึ่งหายาก - เมื่อมีการใช้อัลกอริทึม PRNG โดยทั่วไปจะยังคงไม่ถูกแตะต้องเว้นแต่ว่าจะถูก refactored หรือพบข้อบกพร่องบางอย่าง Dieharder มีประโยชน์มากกว่าสำหรับการสร้างและทดสอบ PRNGs ทดลองด้วย tinyrand การทดสอบอีกสามระดับนั้นเพียงพอสำหรับการบำรุงรักษาแพ็คเกจ tinyrand
เพื่อเรียกใช้ tinyrand กับ Dieharder:
cargo run --release --bin random -- wyrand 42 binary 1T | dieharder -g 200 -a คำสั่งข้างต้นใช้ Wyrand PRNG ซึ่งมีหมายเลข 42 สร้างเอาต์พุตไบนารีมากกว่า 1 ล้านล้านคำ 64 บิต มันถูก stdout ไปยัง dieharder (ในทางปฏิบัติ Dieharder จะบริโภคต่ำกว่า 31 พันล้านตัวเลข)
คำเตือน: Dieharder ไม่มีกลไกสำหรับการจัดการกับข้อผิดพลาดประเภท I ในการทดสอบสมมติฐานหลายครั้ง - บางส่วนเนื่องจากการทดสอบแตกต่างกันในประเภทไม่ใช่แค่ในพารามิเตอร์ ผู้ตาย จำกัด การทดสอบสมมติฐานในขอบเขตของการทดสอบแต่ละครั้ง ไม่มีสมมติฐานที่ครอบคลุมซึ่งจัดประเภท PRNG ว่าพอดีหรือไม่เหมาะตามจำนวนการทดสอบที่ผ่านหรือปรับระดับความเชื่อมั่นให้เป็นบัญชีสำหรับข้อผิดพลาดประเภท I