Posts: 2,910
Threads: 305
Joined: Apr 2022
Reputation:
167
Bill consults with me all the time. I gave him just enough string theory to hang himself.
Pete
Shoot first and shoot people who ask questions, later.
Posts: 688
Threads: 125
Joined: Apr 2022
Reputation:
49
08-10-2024, 05:15 AM
(This post was last modified: 08-10-2024, 05:47 AM by SierraKen.)
Hi guys, been a few years and I thought I would play my hand in some programming again. So I saw this thread and tried it out.
I will admit I used Chat GPT for some of it, but someone has to learn someway. I changed it a bit also making the user able to choose
the highest number and also I added more randomness to it by using the seconds in time$ and then dividing that by 10 and then using that variable
to times it by timeSeed. It works pretty good. Probably not as random as RND, but what do you think?
Code: (Select All)
Cls
start:
s$ = Time$
s1$ = Right$(s$, 2)
s2 = Val(s1$)
s = s2 / 10
Print "Type the highest number for me to choose from."
Input "->", most
If most < 1 Or most > 1000000000 Then GoTo start:
timeSeed = Timer ' Get the system time in seconds
timeSeed = timeSeed * s
randomNumber = (timeSeed Mod most) + 1
Print "Random Number: "; randomNumber
Print: Print: Print
GoTo start:
Posts: 213
Threads: 5
Joined: Apr 2022
Reputation:
27
08-10-2024, 09:01 AM
(This post was last modified: 08-11-2024, 03:42 AM by JRace.
Edit Reason: corrected spell-check error
)
(08-10-2024, 05:15 AM)SierraKen Wrote: Hi guys, been a few years and I thought I would play my hand in some programming again. So I saw this thread and tried it out.
I will admit I used Chat GPT for some of it, but someone has to learn someway. I changed it a bit also making the user able to choose
the highest number and also I added more randomness to it by using the seconds in time$ and then dividing that by 10 and then using that variable
to times it by timeSeed. It works pretty good. Probably not as random as RND, but what do you think?
Code: (Select All)
Cls
start:
s$ = Time$
s1$ = Right$(s$, 2)
s2 = Val(s1$)
s = s2 / 10
Print "Type the highest number for me to choose from."
Input "->", most
If most < 1 Or most > 1000000000 Then GoTo start:
timeSeed = Timer ' Get the system time in seconds
timeSeed = timeSeed * s
randomNumber = (timeSeed Mod most) + 1
Print "Random Number: "; randomNumber
Print: Print: Print
GoTo start:
This is very close to a Linear Congruential Generator (LCG) which is basically the formula V = (V*A+C) Mod M.
(see: https://en.wikipedia.org/wiki/Linear_con..._generator).
On the good side: - LCGs are small, simple, and fast. This is why LCGs have been used in microcomputer BASICs since the 8-bit, 4K RAM days, and are still used by QB64.
- Once you understand LCGs, one can be easily thrown together when needed if you don't have a built-in generator available.
BUT: - The quality of an LCG depends heavily on the parameters (the multiplier A, and the modulus value M) that are used. Poorly chosen parameters can give disastrously poor results. (See RANDU: https://en.wikipedia.org/wiki/RANDU)
Often the timer or system clock is used as a startup seed value for the accumulator V ("Randomize Timer"), while the multiplier and modulus were chosen (carefully we hope!) when the function was written and never change.
I'm concerned that using the timer to set the multiplier as well could result in random output quality which is sometimes acceptable, sometimes not.
Posts: 4,692
Threads: 222
Joined: Apr 2022
Reputation:
322
08-10-2024, 12:07 PM
(This post was last modified: 08-10-2024, 12:09 PM by bplus.)
Oh too early in am to concieve of a "disatrously poor" random number? ;-))
@SierraKen you're back!
724 855 599 923 575 468 400 206 147 564 878 823 652 556 bxor cross forever
Posts: 23
Threads: 4
Joined: Mar 2025
Reputation:
1
here is a program I made but it seems to have lots of errors I cannot figure out how to correct these errors.. can you?
Code: (Select All)
' QBasic 64 (QB64 Phoenix Edition compatible) Random Number Generator
' Implements a custom PRNG with initial seeding, digit ciphering, periodic reseeding,
' and a two-stage generation algorithm as per user specifications.
DEFINE INTEGER64 AS LONG ' Use QB64's 64-bit integers for safety in calculations
DECLARE SUB InitializeSeedArray (arr() AS LONG, fillValue AS LONG)
DECLARE FUNCTION ScrambleNumber (num AS LONG, cipherKey AS LONG) AS LONG
DECLARE FUNCTION GenerateSingleInitialRandom (arr() AS LONG, baseVal AS LONG, rootCipherK AS LONG) AS LONG
DECLARE SUB GenerateNextRandom (BYREF currentLast AS LONG, BYREF currentFirst AS LONG, baseVal AS LONG)
DECLARE SUB ReseedProcedure (seedArr() AS LONG, baseVal AS LONG, BYREF outPrevNum1 AS LONG, BYREF outPrevNum2 AS LONG, rootCipherKey AS LONG)
DECLARE SUB SetNewReseedCountdown (BYREF countdown AS INTEGER, minInterval AS INTEGER, maxAdditional AS INTEGER)
' --- Constants ---
CONST ARRAY_SIZE = 16 ' Size of the internal state array for initial/reseed
CONST BASE_MOD = 1000000 ' Generates numbers from 0 to BASE_MOD - 1
CONST INITIAL_ROOT_CIPHER_KEY = 17 ' A fixed root key for scrambling, varied internally
CONST MIN_RESEED_INTERVAL = 500 ' Minimum numbers generated before considering reseed
CONST MAX_RESEED_ADDITIONAL = 500 ' Max additional numbers for random reseed interval (total interval MIN to MIN+MAX_ADD)
' --- Shared Variables ---
DIM SHARED initialArray(ARRAY_SIZE - 1) AS LONG ' Seed array
DIM SHARED prevNum1 AS LONG ' Stores the "last" generated number (Rn)
DIM SHARED prevNum2 AS LONG ' Stores the "first" (second to last) generated number (Rn-1) for the fast algo
' --- Main Program ---
DIM reseedCountdown AS INTEGER
DIM numbersToGenerate AS LONG
DIM i AS LONG
CLS
RANDOMIZE TIMER ' Seed QB64's RND function, used for reseed interval and in reseed procedure
PRINT "Custom Random Number Generator"
PRINT "-----------------------------"
PRINT "Using BASE_MOD ="; BASE_MOD
PRINT "Internal array size ="; ARRAY_SIZE
PRINT
' Initialize the seed array (e.g., with all zeros to test robustness as requested)
InitializeSeedArray initialArray(), 0
PRINT "Initial seed array set (e.g., to all zeros)."
PRINT
' Perform initial generation for prevNum2 (Rn-1) and prevNum1 (Rn)
PRINT "Performing initial generation of two starting numbers..."
' Generate the first number (which will become prevNum2, or Rn-1 for the fast algorithm)
prevNum2 = GenerateSingleInitialRandom(initialArray(), BASE_MOD, INITIAL_ROOT_CIPHER_KEY)
' Modify initialArray slightly before generating the second number.
' This is a simple way to ensure the input to the second call to GenerateSingleInitialRandom is different,
' leading to a different second starting number.
initialArray(0) = (CLNG(initialArray(0)) + prevNum2 + 1) MOD BASE_MOD
IF initialArray(0) < 0 THEN initialArray(0) = initialArray(0) + BASE_MOD ' Ensure positive
' Generate the second number (which will become prevNum1, or Rn for the fast algorithm)
' Use a slightly different cipher key for more diversity in the initial state.
prevNum1 = GenerateSingleInitialRandom(initialArray(), BASE_MOD, INITIAL_ROOT_CIPHER_KEY + 1)
' Ensure prevNum1 and prevNum2 are not the same initially.
' If they were the same, the fast algorithm might produce a degenerate sequence early on.
IF prevNum1 = prevNum2 THEN
prevNum1 = (prevNum1 + 1)
IF prevNum1 >= BASE_MOD THEN prevNum1 = 0 ' Wrap around if it exceeds base
END IF
PRINT "Initial state: R(n-1) ="; prevNum2, ", R(n) ="; prevNum1
PRINT
' Set up the countdown for the first reseed operation
SetNewReseedCountdown reseedCountdown, MIN_RESEED_INTERVAL, MAX_RESEED_ADDITIONAL
INPUT "How many random numbers to generate? ", numbersToGenerate
IF numbersToGenerate <= 0 THEN
PRINT "Invalid input. Defaulting to 100 numbers."
numbersToGenerate = 100
END IF
PRINT
PRINT "Generating random numbers..."
FOR i = 1 TO numbersToGenerate
' Generate the next random number using the faster algorithm
GenerateNextRandom prevNum1, prevNum2, BASE_MOD
PRINT prevNum1 ' Output the generated number
' Handle reseeding countdown
reseedCountdown = reseedCountdown - 1
IF reseedCountdown <= 0 THEN
ReseedProcedure initialArray(), BASE_MOD, prevNum1, prevNum2, INITIAL_ROOT_CIPHER_KEY
SetNewReseedCountdown reseedCountdown, MIN_RESEED_INTERVAL, MAX_RESEED_ADDITIONAL
PRINT "--- RESEED COMPLETE. New state: R(n-1)="; prevNum2; ", R(n)="; prevNum1; " ---"
END IF
NEXT i
PRINT
PRINT "Done."
END
' --- Subroutines and Functions ---
SUB InitializeSeedArray (arr() AS LONG, fillValue AS LONG)
' Fills the given array (passed by reference) with a specific value.
' Uses UBOUND(arr) to determine the array's upper bound, making it flexible.
DIM k AS INTEGER
FOR k = 0 TO UBOUND(arr)
arr(k) = fillValue
NEXT k
END SUB
FUNCTION ScrambleNumber (num AS LONG, cipherKey AS LONG) AS LONG
' Scrambles a number by applying a cipher to its digits.
' Key features:
' 1. Robustness for zero input: If num is 0, it's treated as 1 before scrambling.
' 2. Digit cipher: (digit + position_in_number + key) MOD 10.
' 3. Non-zero digits: If a scrambled digit becomes 0, it's forced to a non-zero value (1-9).
DIM originalNumStr$ AS STRING ' String representation of the number to be scrambled
DIM scrambledNumStr$ AS STRING ' String to build the scrambled number
DIM k AS INTEGER ' Loop counter for digits
DIM digit AS INTEGER ' Current digit being processed
DIM scrambledDigit AS INTEGER ' Digit after applying the cipher
DIM effectiveNum AS LONG ' The number actually used for scrambling (handles num=0 case)
effectiveNum = num
IF effectiveNum = 0 THEN
effectiveNum = 1 ' Core of handling all-zero inputs: treat 0 as 1 for scrambling.
END IF
originalNumStr$ = LTRIM$(STR$(effectiveNum)) ' Convert number to string, remove leading space
scrambledNumStr$ = "" ' Initialize result string
' Iterate through each digit of the number string
FOR k = 1 TO LEN(originalNumStr$)
digit = VAL(MID$(originalNumStr$, k, 1)) ' Get current digit
' Apply the cipher: (digit + position + key) MOD 10
' Position 'k' is 1-based.
scrambledDigit = (digit + k + cipherKey) MOD 10
' Ensure the scrambled digit is not zero to maintain "activity" and avoid all-zero results
IF scrambledDigit = 0 THEN
scrambledDigit = (k MOD 9) + 1 ' Make it 1-9 based on position (arbitrary but non-zero and varied)
END IF
scrambledNumStr$ = scrambledNumStr$ + CHR$(ASC("0") + scrambledDigit) ' Append scrambled digit to result string
NEXT k
ScrambleNumber = VAL(scrambledNumStr$) ' Convert scrambled string back to a long integer
END FUNCTION
FUNCTION GenerateSingleInitialRandom (arr() AS LONG, baseVal AS LONG, rootCipherK AS LONG) AS LONG
' Generates a single random number based on the initial array.
' Process:
' 1. Scrambles each element of arr() using ScrambleNumber.
' 2. Sums these scrambled values.
' 3. Applies a modulo operation (MOD baseVal) to the sum.
DIM currentSum AS _INTEGER64 ' Use 64-bit integer for sum to prevent overflow if array is large or values are big
DIM k AS INTEGER ' Loop counter
DIM scrambledVal AS LONG ' Stores the result of ScrambleNumber for each element
currentSum = 0 ' Initialize sum
' Iterate through the input array
FOR k = 0 TO UBOUND(arr)
' Scramble the current array element.
' Vary the cipherKey slightly for each element using its index (k) for more internal diversity.
scrambledVal = ScrambleNumber(arr(k), rootCipherK + k)
currentSum = currentSum + scrambledVal ' Add scrambled value to the sum
NEXT k
' Final result is (sum MOD baseVal).
' The `+ baseVal) MOD baseVal` pattern ensures the result is always positive and in the range [0, baseVal-1].
GenerateSingleInitialRandom = (currentSum MOD baseVal + baseVal) MOD baseVal
END FUNCTION
SUB GenerateNextRandom (BYREF currentLast AS LONG, BYREF currentFirst AS LONG, baseVal AS LONG)
' Implements the faster random number generation algorithm:
' next = ((last * 2 + Base) - first) MOD Base
' - currentLast corresponds to "last" (Rn, the most recent number).
' - currentFirst corresponds to "first" (Rn-1, the number before currentLast).
DIM intermediateCalc AS _INTEGER64 ' Use 64-bit for intermediate calculation to prevent overflow
DIM nextVal AS LONG ' Stores the newly generated random number
' User-specified formula: ((last * 2 + Base) - first)
' CLNG() ensures proper conversion to _INTEGER64 if not already.
intermediateCalc = (CLNG(currentLast) * 2) + baseVal - CLNG(currentFirst)
' Apply MOD baseVal, ensuring the result is positive and within [0, baseVal-1].
nextVal = (intermediateCalc MOD baseVal + baseVal) MOD baseVal
' Update state for the next iteration:
' The current "last" number becomes the new "first" number.
' The newly generated "nextVal" becomes the new "last" number.
currentFirst = currentLast
currentLast = nextVal
END SUB
SUB ReseedProcedure (seedArr() AS LONG, baseVal AS LONG, BYREF outPrevNum1 AS LONG, BYREF outPrevNum2 AS LONG, rootCipherKey AS LONG)
' Modifies the seed array and regenerates the two state numbers (outPrevNum1, outPrevNum2).
' This is called periodically to re-introduce entropy or change the generator's sequence.
DIM k AS INTEGER
PRINT "" ' Formatting for output
PRINT "--- RESEEDING PROCEDURE ACTIVATED ---"
' Modify the seed array elements to introduce new state.
' This incorporates current state (outPrevNum1, outPrevNum2), element index (k),
' and some randomness from QB64's RND function.
FOR k = 0 TO UBOUND(seedArr)
seedArr(k) = (CLNG(seedArr(k)) + CLNG(outPrevNum1) + CLNG(outPrevNum2) + k + INT(RND * baseVal)) MOD baseVal
IF seedArr(k) < 0 THEN seedArr(k) = seedArr(k) + baseVal ' Ensure positive after MOD
NEXT k
PRINT "Seed array modified for reseed."
' Regenerate prevNum2 (Rn-1) and prevNum1 (Rn) using the modified seed array and initial generation logic.
' Vary the cipher key slightly based on current state (e.g., outPrevNum1) to ensure different sequences post-reseed.
' Using MOD with small primes (101, 97) to vary the key offset in a somewhat unpredictable way.
outPrevNum2 = GenerateSingleInitialRandom(seedArr(), baseVal, rootCipherKey + (outPrevNum1 MOD 101))
' Further modify seedArr or cipher for the second number (outPrevNum1) to ensure diversity.
' This tweak to seedArr(0) ensures the input to the next GenerateSingleInitialRandom call is different.
seedArr(0) = (CLNG(seedArr(0)) + CLNG(outPrevNum2) + 1) MOD baseVal
IF seedArr(0) < 0 THEN seedArr(0) = seedArr(0) + baseVal ' Ensure positive
outPrevNum1 = GenerateSingleInitialRandom(seedArr(), baseVal, rootCipherKey + (outPrevNum2 MOD 97) + 1)
' Ensure prevNum1 and prevNum2 are not the same after reseeding.
IF outPrevNum1 = outPrevNum2 THEN
outPrevNum1 = (outPrevNum1 + 1)
IF outPrevNum1 >= baseVal THEN outPrevNum1 = 0 ' Cycle within [0, baseVal-1]
END IF
PRINT "Reseeding complete."
END SUB
SUB SetNewReseedCountdown (BYREF countdown AS INTEGER, minInterval AS INTEGER, maxAdditional AS INTEGER)
' Sets a new random interval (number of generations) for the next reseed operation.
' The interval will be between minInterval and (minInterval + maxAdditional).
countdown = minInterval + INT(RND * (maxAdditional + 1)) ' RND gives 0 to <1, so +1 for full range
PRINT "(Next reseed in "; countdown; " numbers)"
END SUB
Posts: 1,215
Threads: 162
Joined: Apr 2022
Reputation:
34
(10-01-2023, 12:53 AM)TerryRitchie Wrote: I actually gave this some serious thought about 15 years ago and was going to build a proof of concept around an Arduino build.
Tune a receiver to 1.42 MHz (the hydrogen band) and sample the incoming signal 64 times. Above a certain noise threshold becomes a 1, at or below becomes a 0, then normalize the result to between 0 and .9999 repeating.
I envisioned Wifi chips could have the included 1.42 MHz receiver or a simple receiver circuit built into every motherboard for software to access to get true random numbers.
Never built it. Maybe I should dust this project off. This is interesting, but can it be made to work in cross-platform QB64PE?
|