Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Getting a random number wihout RND.
#21
Bill consults with me all the time. I gave him just enough string theory to hang himself.

Pete Big Grin
Shoot first and shoot people who ask questions, later.
Reply
#22
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:
Reply
#23
(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.
Reply
#24
Oh too early in am to concieve of a "disatrously poor" random number? ;-))

@SierraKen you're back! Smile
  724  855  599  923  575  468  400  206  147  564  878  823  652  556 bxor cross forever
Reply
#25
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
Reply
#26
(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?
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  Springs2 (random graphic art) mstasak 4 519 11-13-2025, 12:44 PM
Last Post: Dav
  Unique Random Array Program eoredson 5 818 07-10-2025, 10:29 AM
Last Post: DANILIN
  Prime Number Generator SierraKen 8 1,733 12-28-2024, 01:52 AM
Last Post: eoredson
  Random Object Wandering TerryRitchie 1 718 09-29-2024, 03:38 PM
Last Post: TerryRitchie
  Funny Random Sentence Generator SierraKen 5 3,473 09-12-2024, 05:57 PM
Last Post: DANILIN

Forum Jump:


Users browsing this thread: