# PsCombo.ps1 # PowerShell program to generate pseudo random numbers using # a combination of 12 32-bit Multiply With Carry Generators. # # ---------------------------------------------------------------------- # Copyright (c) 2021 Richard L. Mueller # Hilltop Lab web site - http://www.rlmueller.net # Version 1.0 - December 13, 2021 # # This generator has a period of about 2.8 * 10^227, # which is greater than 2^755. # Syntax: # cscript //nologo Combo.vbs # where: # is the number of random values to display (optional). # Count must be a positive integer. The default count is 10. # is an initial seed value (optional). # Seed must be an integer greater than or equal to 0 and less # than 2^32. The default value is based on the system timer. # is an initial carry value (optional). # Carry must be an integer greater than or equal to 0 and less # than 2^32. The default value is based on the system date/time. # Program outputs pseudo random integers greater than or equal to 0 # and less than 2^32. # # You have a royalty-free right to use, modify, reproduce, and # distribute this script file in any way you find useful, provided that # you agree that the copyright owner above has no warranty, obligations, # or liability for such use. Param ( [Int32]$Count, [Int64]$Seed, [Int64]$Carry ) Function Syntax ($ErrNum) { # Subroutine to display error messages and syntax help. Switch ($ErrNum) { 1 {Write-Host "Error, too many arguments." -ForegroundColor Red -backgroundColor Black} 2 {Write-Host "Error, seed must be greater than or equal to 0." -ForegroundColor Red -backgroundColor Black} 3 {Write-Host "Error, seed must be less than 2^32 (4,294,967,296)" -ForegroundColor Red -backgroundColor Black} 4 {Write-Host "Error, count must be positive." -ForegroundColor Red -backgroundColor Black} 5 {Write-Host "Error, carry must be greater than or equal to 0." -ForegroundColor Red -backgroundColor Black} 6 {Write-Host "Error, carry must be less than 2^32 (4,294,967,296)." -ForegroundColor Red -backgroundColor Black} 7 {Write-Host "Error, carry cannot be A - 1 = 4,164,903,689." -ForegroundColor Red -backgroundColor Black} } Write-Host "-----" Write-Host "PsCombo.ps1 - PowerShell program to generate random numbers." Write-Host "Syntax:" Write-Host " PowerShell .\PsCombo.ps1 " Write-Host "where:" Write-Host " is number of random integers to display (optional)." Write-Host " Count must be a positive integer, with no commas." Write-Host " Default value is 10." Write-Host " is a seed value (optional)." Write-Host " Seed must be an integer greater than or equal to 0" Write-Host " and less than 2^32 (no commas). Default value is based" Write-Host " on the system timer." Write-Host " a carry value (optional)." Write-Host " Carry must be an integer greater than or equal to 0" Write-Host " and less than 2^32 (no commas). Default value is based" Write-Host " on the system date and time." Write-Host "Program outputs psuedo random integers greater than or equal" Write-Host "to 0 and less than 2^32 (4,294,967,296)." Write-Host "Copyright(c) 2021 Richard L. Mueller" Write-Host "Version 1.0 - December 13, 2021" Return $True } # Use default if parmeter not specified. # Default count is 10. If ($Count -eq 0) {$Count = 10} # Default Seed is based on the system time since midnight in seconds. If ($Seed -eq 0) { $D1 = [Int16](Get-Date -Format HH) $D2 = [Int16](Get-Date -Format mm) $D3 = [Int16](Get-Date -Format ss) $D4 = $D3 + (60 * ($D2 + (60 * $D1))) $D5 = $D4 * 4294967296 $Seed = [System.Math]::Truncate($D5 / (24 * 60 * 60)) } # The default initial carry is based on the system date and time. If ($Carry -eq 0) { $D1 = [Int16](Get-Date -Format MM) $D2 = [Int16](Get-Date -Format dd) $D3 = [Int16](Get-Date -Format yy) $D4 = [Int16](Get-Date -Format HH) $D5 = [Int16](Get-Date -Format mm) $D6 = [Int16](Get-Date -Format ss) $D7 = ($D6 + (60 * $D5) + (3600 * $D4)) / (24 * 3600) $D8 = $D2 + ($D1 * 30) + ($D3 * 365) + 36505 $D9 = [System.Math]::Truncate(($D7 + $D8) * 100000000000) $D10 = [System.Math]::Truncate($D9 / 4294967296) $Carry = $D9 - (4294967296 * $D10) } # Validate arguments. If ($Args.Count -gt 0) {If (Syntax 1) {Exit}} If ($Seed -lt 0) {If (Syntax 2) {Exit}} If (($Seed - 4294967296) -gt 0) {If (Syntax 3) {Exit}} If ($Count -lt 0) {If (Syntax 4) {Exit}} If ($Carry -lt 0) {If (Syntax 5) {Exit}} If (($Carry - 4294967296) -gt 0) {If (Syntax 6) {Exit}} # Carry cannot be 0 or A - 1 = 4,164,903,689. If ($Carry -eq 4164903689) {If (Syntax 7) {Exit}} # Output the initial seed and carry values. "Initial Seed = " + '{0:n0}' -f $Seed "Initial Carry = " + '{0:n0}' -f $Carry # Display number of integers requested from the psuedo random number generator. "$Count psuedo random integers:" " " # Constants for Multiply With Carry Generators. # $A0 = 4,164,903,690 = ($A0_hi * 2^16) + $A0_lo $A0 = 4164903690 $A0_hi = 63551 $A0_lo = 25354 # $A1 = 4,204,114,314 = ($A1_hi * 2^16) + $A1_lo $A1 = 4204114314 $A1_hi = 64149 $A1_lo = 45450 # $A2 = 4,210,396,968 = ($A2_hi * 2^16) + $A2_lo $A2 = 4210396968 $A2_hi = 64245 $A2_lo = 36648 # $A3 = 4,198,054,089 = ($A3_hi * 2^16) + $A3_lo $A3 = 4198054089 $A3_hi = 64057 $A3_lo = 14537 # $A4 = 4,187,999,619 = ($A4_hi * 2^16) + $A4_lo $A4 = 4187999619 $A4_hi = 63903 $A4_lo = 52611 # $A5 = 4,197,999,714 = ($A5_hi * 2^16) + $A5_lo $A5 = 4197999714 $A5_hi = 64056 $A5_lo = 25698 # $A6 = 4,183,234,104 = ($A6_hi * 2^16) + $A6_lo $A6 = 4183234104 $A6_hi = 63831 $A6_lo = 5688 # $A7 = 4,208,029,890 = ($A7_hi * 2^16) + $A7_lo $A7 = 4208029890 $A7_hi = 64209 $A7_lo = 28866 # $A8 = 4,178,097,609 = ($A8_hi * 2^16) + $A8_lo $A8 = 4178097609 $A8_hi = 63752 $A8_lo = 46537 # $A9 = 4,194,774,690 = ($A9_hi * 2^16) + $A9_lo $A9 = 4194774690 $A9_hi = 64007 $A9_lo = 11938 # $A10 = 4,201,298,934 = ($A10_hi * 2^16) + $A10_lo $A10 = 4201298934 $A10_hi = 64106 $A10_lo = 48118 # $A11 = 4,197,302,403 = ($A11_hi * 2^16) + $A11_lo $A11 = 4197302403 $A11_hi = 64045 $A11_lo = 49283 # $M = 4,294,967,296 # $H = 65,536 [Int64]$M = 4294967296 $H = 65536 # Initialize arrays $S[] and $C[] to zeros, seed and carry for the 12 MWC generators. $S = @(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) $C = @(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) Function InitialValues ($InitSeed, $InitCarry) { # Use one of the Multiply With Carry Generators to # generate initial values for the seed and carry for # all twelve Multiply With Carry Generators. # We need a total of 24 integers greater than or # equal to 0 and less then 2^32. These 768 bits (96 bytes) # represent the initial state of the generator. For ($k = 0;$k -le 11;$k = $k + 1) { $New = MWC $InitSeed $InitCarry $A0_hi $A0_lo $S[$k] = $New[0] $New = MWC $New[0] $New[1] $A0_hi $A0_lo $C[$k] = $New[0] $InitSeed = $New[0] $InitCarry = $New[1] } } Function MWC ([Int64]$X, [Int64]$C, $A_hi, $A_lo) { # pseudo Random Number Generator based on # the Multiply With Carry Generator # Xi = [(A * Xi-1) + Ci-1] Mod M # Ci = Integer[((A * Xi-1) + Ci-1) / M] # Where: # M = 2^32 = 4,294,967,296 # Note: # B = (A * M) - 1 # P = (B - 1) / 2 # A selected so that B and P are both prime. # The period of the generator is P. # The values are broken into high and low 16-bit parts as follows: # A = A_hi * 2^16 + A_lo # lngC = Ci-1 = C_hi * 2^16 + C_lo # lngX = Xi-1 = S_hi * 2^16 + S_lo # lngX and lngC are integers greater than or equal to 0 # and less than M. # Each time this function is called it returns the next integer in # a pseudo random sequence. The return value is an integer greater # than or equal to 0 and less than M. The function also assigns # new values to lngX and lngC. These new values are required # for the next time the function is called. # Note: There are two cases where this generator will have a # period of 1. One case is when the initial Ci = 0 and the # initial Xi = 0. The other case is when the initial Ci = A - 1 # and the initial Xi = M - 1. These trivial cases should be avoided. $X_hi = [System.Math]::Truncate($X / $H) $X_lo = $X - ($X_hi * $H) $C_hi = [System.Math]::Truncate($C / $H) $C_lo = $C - ($C_hi * $H) # $A = (($A_hi * $H) + $A_lo) # $X = (($X_hi * $H) + $X_lo) # $C = (($C_hi * $H) + $C_lo) [Int64]$F1 = $A_hi * $X_hi [Int64]$F2 = ($A_hi * $X_lo) + ($A_lo * $X_hi) + $C_hi [Int64]$F3 = ($A_lo * $X_lo) + $C_lo [Int64]$T1 = [System.Math]::Truncate($F2 / $H) [Int64]$T2 = $M * $T1 [Int64]$Xn = ((($F2 * $H) % $M) + $F3) % $M [Int64]$Cn = [System.Math]::Truncate(($F2 / $H) + $F1) Return @($Xn, $Cn) } Function Combo ([Int64]$X0, [Int64]$C0, [Int64]$X1, [Int64]$C1, [Int64]$X2, [Int64]$C2, ` [Int64]$X3, [Int64]$C3, [Int64]$X4, [Int64]$C4, [Int64]$X5, [Int64]$C5, [Int64]$X6, ` [Int64]$C6, [Int64]$X7, [Int64]$C7, [Int64]$X8, [Int64]$C8, [Int64]$X9, [Int64]$C9, ` [Int64]$X10, [Int64]$C10, [Int64]$X11, [Int64]$C11) { # Function to combine 12 Multiply With Carry Generators modulo 2^32. # Because the period of each of the Multiply With Carry Generators # is prime, the overall period of this combo generator is the # product of the periods of the 12 individal generators. # Period =~ 2.8 * 10^227 =~ 2^755. # Combine numbers from each of the 12 Multiply With Carry # Generators, modulo 2^32. $New = MWC $X0 $C0 $A0_hi $A0_lo $S[0] = $New[0] $C[0] = $New[1] [Int64]$Combo = $S[0] $New = MWC $X1 $C1 $A1_hi $A1_lo $S[1] = $New[0] $C[1] = $New[1] [Int64]$Combo = $Combo + $S[1] $New = MWC $X2 $C2 $A2_hi $A2_lo $S[2] = $New[0] $C[2] = $New[1] [Int64]$Combo = $Combo + $S[2] $New = MWC $X3 $C3 $A3_hi $A3_lo $S[3] = $New[0] $C[3] = $New[1] [Int64]$Combo = $Combo + $S[3] $New = MWC $X4 $C4 $A4_hi $A4_lo $S[4] = $New[0] $C[4] = $New[1] [Int64]$Combo = $Combo + $S[4] $New = MWC $X5 $C5 $A5_hi $A5_lo $S[5] = $New[0] $C[5] = $New[1] [Int64]$Combo = $Combo + $S[5] $New = MWC $X6 $C6 $A6_hi $A6_lo $S[6] = $New[0] $C[6] = $New[1] [Int64]$Combo = $Combo + $S[6] $New = MWC $X7 $C7 $A7_hi $A7_lo $S[7] = $New[0] $C[7] = $New[1] [Int64]$Combo = $Combo + $S[7] $New = MWC $X8 $C8 $A8_hi $A8_lo $S[8] = $New[0] $C[8] = $New[1] [Int64]$Combo = $Combo + $S[8] $New = MWC $X9 $C9 $A9_hi $A9_lo $S[9] = $New[0] $C[9] = $New[1] [Int64]$Combo = $Combo + $S[9] $New = MWC $X10 $C10 $A10_hi $A10_lo $S[10] = $New[0] $C[10] = $New[1] [Int64]$Combo = $Combo + $S[10] $New = MWC $X11 $C11 $A11_hi $A11_lo $S[11] = $New[0] $C[11] = $New[1] [Int64]$Combo = $Combo + $S[11] [Int64]$Temp = [System.Math]::Truncate($Combo / $M) [Int64]$Combo = $Combo - ($M * $Temp) Return $Combo } # Initialize the 12 MWC generators. InitialValues $Seed $Carry # Calculate the requested random integers. For ($k = 1;$k -le $Count;$k = $k + 1) { [Int64]$M = 4294967296 $New = Combo $S[0] $C[0] $S[1] $C[1] $S[2] $C[2] $S[3] $C[3] $S[4] $C[4] $S[5] $C[5] ` $S[6] $C[6] $S[7] $C[7] $S[8] $C[8] $S[9] $C[9] $S[10] $C[10] $S[11] $C[11] $Seed = $New[0] $Carry = $New[1] # Output seed from this iteration. '{0:n0}' -f $Seed # Alternate output statement for research. # " $k : Seed = " + '{0:n0}' -f $Seed + ", Carry = " + '{0:n0}' -f $Carry # Alternate output statement for normalized real numbers, greater or equal to 0 and less than 1. # '{0:n15}' -f ($Seed / $M) }