Announcement

Collapse

Forum Guidelines

This forum is for finished source code that is working properly. If you have questions about this or any other source code, please post it in one of the Discussion Forums, not here.
See more
See less

Rnd2

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • Rnd2

    IF you have been here before THEN GOTO post #4 for another idea (Needs PB9 and/or CC5)

    RND2 is simply John Gleason's rndExt function introduced at post #6 in Walter Henn's Random Number (RND) Problem thread here with some functionality similar to PB's RND.

    RND2 is a bit cheeky but not carved in stone, is it?

    Blue text use following indicates a macro name.

    Simplest form: Print RND2 or x = RND2. This is equivalent to RND's floating point mode. As with RND a default sequence is used and with no argument generates the next number in sequence. This is also true if RND has a positive argument but not so for RND2 - a positive argument has a different role, covered later in ToggleScope.

    With RND we have one sequence and a user seed determines a starting position in that sequence. With RND2 we have a multiplier which sets up a unique sequence and two user seeds determine where in that sequence we start from.

    To ensure a different starting position with RND we could use Randomize (same as Randomize Timer). Recently, John Gleason sent me a large batch of multipliers and I have used the 384 largest for RND2. The period for the method used here is dependent upon the multiplier and the multipliers used are greater than the one used with rndExt. The two user seeds may not be any old seeds and must adhere to rules but they are not that restrictive. To make life simple RND2 uses default seeds and to ensure a different start RND2 firstly chooses one of the 384 multipliers at random. The first seed is then 'shuffled' by making use of the Time Stamp Counter. The chosen multiplier, 'shuffled' first seed and default second seed are then fed into the generator. This further 'shuffles' the first seed and 'shuffles' the second seed.

    To achieve this we simply use the macro Randomise ie 's' instead of 'z'.

    Notice that we do not have 'Randomise n' where n is a numeric expression. RND fixes a multiplier, it is assumed, and varies the seed. The above tack, which does make life easier, fixes the seeds, initially, and varies the multiplier.

    The generator has a period close to 2^63. It is used more than once in RND2, as it is in rndExt, thus reducing the effective period which is about 2^61. On my machine RND's sequence in a tight loop will be used up in about 330 seconds. If RND2 were as fast as RND its sequence would be used up in about 5617 years. It is not as fast so you may need to change the motherboard's battery.

    The default seed for RND can be duplicated by Randomize Cvs(Chr$(255,255,255,255)). With RND2 we use the macro DefaultSequence. This simply uses the two default seeds, mentioned above, and the largest multiplier from our list of 384.

    RND has a special effects mode. RND(0) repeats the last number generated and so does RND2(0) and the macro RepeatLastRND2.

    If RND has a negative argument then the generator is re-seeded giving then a fixed sequence or, rather, a fixed starting point. For example, Print RND(-123) which, of course, is effectively the same as Randomize -123: Print RND. We do not have a 'Randomise n' but we do have access to 384 multipliers and we get to them via RandomiseInt(n) with n = 1,2,...,384. The 'Int' is to remind us an integer parameter is required.

    In general, re-seeding may lead to sequence overlapping and should then be avoided. However, if we are comparing two algorithms, for example, then having them 'fed' with the same sequence may be desirable.

    With RND we can re-seed from a random position. For example, t = Timer: Randomize t. Later we can 'Randomize t' again.

    With RND2 we use RepeatLastSequence.

    In practice we could have something like the following pseudo code:

    Code:
    RND                              RND2
     
        t = Timer
        Randomize t                      Randomise
        Execute algorithm 1              Execute algorithm 1
        Randomize t                      RepeatLastSequence
        Execute algorithm 2              Execute algorithm 2
    With the fixed sequence approach we would use a RandomiseInt(n) twice but the second instance could be replaced with RepeatLastSequence. RepeatLastSequence then works with both fixed starting positions and random starting positions.

    Whilst in this vein there is a final command which does not have a RND equivalent. Suppose we have some initialization code which makes use of random numbers and we then want to consider two algorithms. The first part would probably need to be random but the two algorithms may be better tested with the same sequence. To this end Bookmark can be used just prior to algorithm 1 to capture the chosen multiplier and the two current user seeds. To return to the bookmark just prior to algorithm 2 we can use either GotoBookmark or RepeatLastSequence - they are identical in execution.

    Pseudo code example:

    Code:
    Randomise
    Initialize
    Bookmark
    Execute algorithm 1
    GotoBookmark ( or RepeatLastSequence )
    Execute algorithm 2
    With the exception of RepeatLastRND2 all of the above commands return either false if no error or true in the event of an error.

    ToggleScope is also a departure from RND. RND2's default scope is, as with RND, [0,1). Numbers generated after the first use of ToggleScope fall in the range (0,1), and back to [0,1] when invoked again. This additional scope was introduced by John Gleason in RndExt. However, in RndExt the generator is used to assign the sign which, obviously, has an effect on the period. More importantly, care would have to be taken when using ToggleScope and RepeatLastSequence else we lose synchronisation. Both of these issues can be eliminated by giving ToggleScope its own generator. It also has its own set of multipliers apart rom the 384 already used and the sequence is set up in exactly the same way as Randomise. ToggleScope is the macro for RND2 with a positive parameter.

    RND's Integer Range mode, RND(a,b) is replicated with RND2 as RND2(a,b).

    How fast?

    On my machine 10,000,000 iterations of RND take about 321 milliseconds. RND2 takes about 587ms and with the enhanced scope takes about 637ms. With all the bells and whistles stripped out of RND2, number generation and nothing else, we are looking at about 460ms. On the one hand this shows how fast the generator is but, on the other hand, it shows how fast RND is - it still has the bells and whistles.

    The function RND2 is in post #2 and PBMAIN gives it a little work out.

    In this particular case nothing much is gained by the new compilers so the code is left as compiled with PBCC4.

    Final comment: Thanks John.
    Last edited by David Roberts; 7 Sep 2008, 11:36 AM. Reason: Forgot to use CODE for the pseudo code sections.

  • #2
    Code:
    #COMPILE EXE
    #DIM ALL
    #TOOLS OFF
    
    #INCLUDE "WIN32API.INC"
    
    %NOMMIDS  = 1
    %NOGDI    = 1
                 
    '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    ' Rnd2 Macros
    MACRO ToggleScope = Rnd2(1)
    MACRO RepeatLastRND2 = Rnd2(0)             
    MACRO RandomiseInt(prm1) = Rnd2(-prm1)
    MACRO Randomise = Rnd2(-385)
    MACRO DefaultSequence = Rnd2(-386)
    MACRO RepeatLastSequence = Rnd2(-387)
    MACRO GotoBookmark = Rnd2(-387)
    MACRO Bookmark = Rnd2(-388)
    '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
    
    '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    ' System Timer Macros
    GLOBAL qFreq, qOverhead, qStart, qStop AS QUAD
    GLOBAL f AS STRING
    
    MACRO InitializeTimer
      f = "#####.##"
      QueryPerformanceFrequency qFreq
      QueryPerformanceCounter qStart ' Intel suggestion. First use may be suspect
      QueryPerformanceCounter qStart ' So, wack it twice <smile>
      QueryPerformanceCounter qStop
      qOverhead = qStop - qStart     ' Relatively small
    END MACRO
    
    MACRO StartTimer = QueryPerformanceCounter qStart
    MACRO StopTimer = QueryPerformanceCounter qStop
    
    MACRO sTimeTaken = USING$(f,(qStop - qStart - qOverhead)*1000/qFreq) + "ms"
    MACRO nTimeTaken = (qStop - qStart - qOverhead)*1000/qFreq
    
    '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    DECLARE FUNCTION Rnd2( OPT LONG , LONG ) AS EXT
    
    FUNCTION PBMAIN( ) AS LONG
    LOCAL i, j AS LONG, x AS EXT
                                                                                     
      InitializeTimer
          
      PRINT "No chosen sequence":PRINT
      FOR i = 1 TO 5
        PRINT Rnd2
      NEXT
      PRINT
      PRINT "DefaultSequence":PRINT
      DefaultSequence
      FOR i = 1 TO 5
        PRINT Rnd2
      NEXT
      PRINT
      PRINT:PRINT "**********":PRINT
      
      PRINT "RandomiseInt(123)":PRINT
      RandomiseInt(123)
      FOR i = 1 TO 5
        PRINT Rnd2
      NEXT
      PRINT
      PRINT "RepeatLastSequence":PRINT
      RepeatLastSequence
      FOR i = 1 TO 5
        PRINT Rnd2
      NEXT
      PRINT:PRINT "**********":PRINT
      
      PRINT "Randomise":PRINT
      Randomise
      FOR i = 1 TO 5
        PRINT Rnd2
      NEXT
      PRINT
      PRINT "RepeatLastSequence":PRINT
      RepeatLastSequence
      FOR i = 1 TO 5
        PRINT Rnd2
      NEXT
      PRINT:PRINT "**********":PRINT
      
      PRINT "Randomise, ToggleScope":PRINT
      Randomise
      ToggleScope
      FOR i = 1 TO 10
        PRINT Rnd2
      NEXT
      PRINT
      PRINT "ToggleScope, RepeatLastSequence":PRINT
      ToggleScope
      RepeatLastSequence
      FOR i = 1 TO 10
        PRINT Rnd2
      NEXT
      PRINT:PRINT "**********":PRINT
      
      PRINT "Repeat last number generated";RepeatLastRND2
      PRINT:PRINT "**********":PRINT                                                             
      
      PRINT "Choose from range 1 to 49":PRINT
      FOR j = 1 TO 5
        FOR i = 1 TO 6
          PRINT USING$("###",Rnd2(1, 49));
        NEXT
        PRINT
      NEXT
      PRINT:PRINT "**********":PRINT
      
      PRINT "Bookmark/GotoBookmark example":PRINT
      Randomise
      FOR i = 1 TO 5
        PRINT Rnd2
      NEXT
      PRINT
      PRINT "Bookmark":PRINT
      Bookmark
      FOR i = 1 TO 5
        PRINT rnd2
      NEXT
      PRINT
      PRINT "GotoBookmark":PRINT
      GotoBookmark
      FOR i = 1 TO 5
        PRINT rnd2
      NEXT
      PRINT:PRINT "**********":PRINT
        
      PRINT "10,000,000 iterations":PRINT
      
      StartTimer
        FOR i = 1 TO 10000000
          x = RND
        NEXT i
      StopTimer
      
      PRINT "Rnd:       ";sTimeTaken
      
      Randomise
      StartTimer
        FOR i = 1 TO 10000000
          x = Rnd2
        NEXT i
      StopTimer
      
      PRINT "Rnd2 [0,1):";sTimeTaken
      
      RepeatLastSequence
      ToggleScope
      StartTimer
        FOR i = 1 TO 10000000
          x = Rnd2
        NEXT i
      StopTimer
      
      PRINT "Rnd2 (1,1):";sTimeTaken
      
      waitkey$
    
    END FUNCTION
    
    FUNCTION Rnd2( OPT One AS LONG, Two AS LONG ) AS EXT
    
    '-----------------------------------------------------------------------------------------
    'creates an EXT random test value from 0 to +0.9999... (or -0.9999... to +0.9999...)
    'where each of the 18 digits are statistically random.
    'Period is ~2^61 EXT values before repeating--many years.
    '-----------------------------------------------------------------------------------------
    STATIC eqp, oneTime, FirstTogScopePass, rndL AS LONG, eq AS QUAD
    STATIC mwcSoph, SavedmwcSoph, mwcSophFF AS DWORD
    STATIC mwcRandom, mwcCarry, SavedmwcRandom, SavedmwcCarry, mwcRandomFF, mwcCarryFF AS LONG
    STATIC TogScope AS LONG, ptrMultiplier, ptrToggleMultipliers AS DWORD PTR
    STATIC rndE AS EXT, RandomizeYet AS LONG
    LOCAL Range AS LONG, Seed AS SINGLE
    
      IF VARPTR(One) <> 0 THEN ' at least one optional parameter passed otherwise belt down to 'If OneTime = 0'
        
        IF VARPTR(Two) = 0 THEN ' just the one then
          
          IF One = 0 THEN ' Repeat the last number generated
            
            FUNCTION = rndE ' will, of course, be zero if no rndE calculated yet
            EXIT FUNCTION
            
          ELSEIF One < 0 THEN
          
            ptrMultiplier = CODEPTR(multiplier) ' to list of prime constant pre-sets
            mwcRandom = &ha5b218da ' can be any LONG > 1 or < -1
            mwcCarry  = &h3fe8700c ' can be any positive LONG less than mwcSoph and > 1
            
            SELECT CASE AS LONG One
              
              CASE -384 TO -1 ' RandomiseInt
                
                mwcSoph = @ptrMultiplier[ABS(One) - 1]
                SavedmwcSoph = mwcSoph
                SavedmwcRandom = mwcRandom
                SavedmwcCarry = mwcCarry
                          
              CASE -385 ' Randomise                                                                      
                
                IF NOT RandomizeYet THEN
                  Seed = TIMER*2^32/86400 ' 60*60*24
                  RANDOMIZE Seed
                  RandomizeYet = %True
                END IF
                mwcSoph = @ptrMultiplier[RND(0,383)]
                !dw &h310f              ;time stamp counter To vary seed1
                !Add mwcRandom, eax     ;vary seed1
                GOSUB sRandomLong ' Shuffle mwcRandom a bit more and shuffle mwcCarry
                SavedmwcSoph = mwcSoph
                SavedmwcRandom = mwcRandom
                SavedmwcCarry = mwcCarry
                
              CASE -386 ' DefaultSequence
                
                mwcSoph = 4221732234???
                mwcRandom = &ha5b218da
                mwcCarry  = &h3fe8700c
                SavedmwcSoph = mwcSoph
                SavedmwcRandom = mwcRandom
                SavedmwcCarry = mwcCarry
                
              CASE -387 ' RepeatLastSequence
                
                IF mwcSoph = 0 THEN ' there wasn't a last sequence so use default
                  mwcSoph = 4221732234???
                  mwcRandom = &ha5b218da
                  mwcCarry  = &h3fe8700c
                  SavedmwcSoph = mwcSoph
                  SavedmwcRandom = mwcRandom
                  SavedmwcCarry = mwcCarry
                ELSE
                  mwcSoph = SavedmwcSoph
                  mwcRandom = SavedmwcRandom
                  mwcCarry = SavedmwcCarry
                END IF
                
              CASE -388 ' Bookmark
                
                SavedmwcSoph = mwcSoph
                SavedmwcRandom = mwcRandom
                SavedmwcCarry = mwcCarry
              
              CASE ELSE
                
                FUNCTION = -1 ' Error
                EXIT FUNCTION
                
            END SELECT
            
          ELSE 'make it 50/50 + and -
          
            TogScope = NOT TogScope
            
            IF FirstTogScopePass = 0 THEN
              FirstTogScopePass = 1
              ptrToggleMultipliers = CODEPTR(ToggleMultipliers)
              IF NOT RandomizeYet THEN
                Seed = TIMER*2^32/86400 ' 60*60*24
                RANDOMIZE Seed
                RandomizeYet = %True
              END IF
              mwcSophFF = @ptrToggleMultipliers[RND(0,11)] ' 50/50 + and - uses its own random sequence and stays with it
              mwcRandomFF = &ha5b218da
              mwcCarryFF  = &h3fe8700c
              !dw &h310f              ;time stamp counter To vary seed1
              !Add mwcRandomFF, eax   ;vary seed1
              GOSUB sRandomFF ' Shuffle mwcRandomFF a bit more and shuffle mwcCarryFF
            END IF
            
          END IF 'One = 0
          
          FUNCTION = 0 ' OK
          EXIT FUNCTION
          
        ELSE ' both optional parameters passed so
        
          Range = %True ' and fall through code
          
        END IF ' VarPtr(Two) = 0
        
      END IF ' VarPtr(One) <> 0
      
      IF OneTime = 0 THEN ' otherwise start crunching
        OneTime = 1
        IF mwcSoph = 0 THEN ' the parameter section above has yet to be entered, unless RepeatLastRND2,
          mwcSoph = 4221732234??? ' so use default configuration and fall through code
          mwcRandom = &ha5b218da
          mwcCarry  = &h3fe8700c
          SavedmwcSoph = mwcSoph
          SavedmwcRandom = mwcRandom
          SavedmwcCarry = mwcCarry
        END IF
        eqp = VARPTR( eq )
      END IF
      
      
      GOSUB sRandomLong 'sub to generate LONG rnd value
      POKE LONG, eqp, rndl 
      DO 'DO LOOP runs avg. of 2.3 loops / Rnd2. It's needed for perfectly linear distribution
        GOSUB sRandomLong
        ! And rndL, &h0fffffff
        POKE LONG, eqp + 4, rndl
      LOOP WHILE eq > 999999999999999999
      
      rndE = eq / 1000000000000000000 ' divide it to make its range 0 to .999999999999999999
        
      ' Cannot leave yet - couple of questions to ask
      IF Range THEN
        IF  Two < One THEN SWAP One, Two
        rndE = INT( (Two - One + 1)*rndE + One )
      ELSEIF TogScope THEN
        GOSUB sRandomFF 'make it 50/50 + and -
        IF rndL > 0 THEN rndE = -rndE
      END IF
      
      FUNCTION = rndE
      
    EXIT FUNCTION
    
    ' Engine by John Gleason
    sRandomLong: 'here we generate a random LONG from 0 to &hffffffff
    !mov eax, mwcSoph
    !mov ecx, mwcRandom
    !mul ecx
    !Add eax, mwcCarry
    !adc edx, 0
    !mov mwcRandom, eax
    !mov mwcCarry,  edx
    !mov rndL, eax
    RETURN 'done generating, rndL holds final value
    
    sRandomFF: ' As sRandomLong but used exclusively by TogScope
    ! mov eax, mwcSophFF
    ! mov ecx, mwcRandomFF
    ! mul ecx
    ! Add eax, mwcCarryFF
    ! adc edx, 0
    ! mov mwcRandomFF, eax
    ! mov mwcCarryFF, edx
    ! mov rndL, eax
    RETURN
    
    ToggleMultipliers:
    !DD 4221474930???, 4221475530???, 4221475614???, 4221476664???, 4221477015???, 4221477093???
    !DD 4221477489???, 4221477834???, 4221478689???, 4221478848???, 4221479685???, 4221479778???
    
    ' Ruined for choice by that man again, John Gleason
    multiplier:
    !DD 4221480339???, 4221480549???, 4221480579???, 4221480663???, 4221481263???, 4221481914???, 4221482613???, 4221482670???
    !DD 4221483033???, 4221483405???, 4221484758???, 4221485325???, 4221485910???, 4221485925???, 4221486498???, 4221487443???
    !DD 4221488559???, 4221489459???, 4221490755???, 4221490794???, 4221491259???, 4221491949???, 4221492453???, 4221493668???
    !DD 4221494070???, 4221494178???, 4221495243???, 4221496245???, 4221496938???, 4221497178???, 4221497475???, 4221497895???
    !DD 4221498069???, 4221498099???, 4221498153???, 4221499248???, 4221499413???, 4221499884???, 4221500409???, 4221501990???
    !DD 4221502998???, 4221503544???, 4221503964???, 4221505260???, 4221507753???, 4221508200???, 4221508875???, 4221509904???
    !DD 4221509949???, 4221510765???, 4221512160???, 4221512919???, 4221513444???, 4221514263???, 4221515289???, 4221515325???
    !DD 4221515445???, 4221520290???, 4221520554???, 4221521718???, 4221521844???, 4221522618???, 4221524268???, 4221524439???
    !DD 4221524964???, 4221525570???, 4221526335???, 4221526608???, 4221527565???, 4221528030???, 4221528099???, 4221528255???
    !DD 4221528675???, 4221533160???, 4221533169???, 4221533193???, 4221533343???, 4221535059???, 4221535143???, 4221535674???
    !DD 4221537060???, 4221537138???, 4221538230???, 4221538968???, 4221539343???, 4221539799???, 4221540048???, 4221540270???
    !DD 4221540279???, 4221540774???, 4221541563???, 4221541680???, 4221541929???, 4221542283???, 4221543339???, 4221543390???
    !DD 4221543504???, 4221544818???, 4221544914???, 4221545025???, 4221546045???, 4221546768???, 4221547230???, 4221547569???
    !DD 4221549219???, 4221550308???, 4221550563???, 4221551088???, 4221551433???, 4221551460???, 4221552075???, 4221552879???
    !DD 4221552894???, 4221553764???, 4221554763???, 4221554958???, 4221555114???, 4221556344???, 4221557439???, 4221557898???
    !DD 4221557943???, 4221558348???, 4221558810???, 4221560484???, 4221560628???, 4221560733???, 4221561378???, 4221561879???
    !DD 4221562518???, 4221562899???, 4221564303???, 4221564585???, 4221564654???, 4221566130???, 4221566613???, 4221569700???
    !DD 4221570165???, 4221570639???, 4221570660???, 4221573558???, 4221574185???, 4221574365???, 4221574575???, 4221575349???
    !DD 4221576675???, 4221576969???, 4221577479???, 4221578124???, 4221578553???, 4221579039???, 4221579393???, 4221581049???
    !DD 4221581973???, 4221582468???, 4221582795???, 4221583113???, 4221583740???, 4221584445???, 4221584478???, 4221584583???
    !DD 4221585630???, 4221588705???, 4221590124???, 4221590175???, 4221590295???, 4221590589???, 4221591480???, 4221591555???
    !DD 4221591708???, 4221591774???, 4221591888???, 4221592413???, 4221592614???, 4221593094???, 4221593295???, 4221593955???
    !DD 4221594573???, 4221596799???, 4221598914???, 4221599334???, 4221600195???, 4221600384???, 4221600888???, 4221602253???
    !DD 4221602379???, 4221604428???, 4221605268???, 4221605613???, 4221605634???, 4221606255???, 4221606264???, 4221606303???
    !DD 4221606375???, 4221607620???, 4221608643???, 4221608718???, 4221608973???, 4221610023???, 4221610200???, 4221611724???
    !DD 4221612249???, 4221612660???, 4221613068???, 4221613320???, 4221613998???, 4221614298???, 4221614808???, 4221615168???
    !DD 4221615174???, 4221617064???, 4221618120???, 4221618714???, 4221618744???, 4221620364???, 4221620430???, 4221620658???
    !DD 4221622959???, 4221623070???, 4221623889???, 4221624450???, 4221624939???, 4221625434???, 4221625605???, 4221626340???
    !DD 4221626835???, 4221626895???, 4221627129???, 4221628809???, 4221629538???, 4221630465???, 4221631734???, 4221632805???
    !DD 4221633780???, 4221633933???, 4221634779???, 4221635040???, 4221635439???, 4221636588???, 4221637875???, 4221638013???
    !DD 4221638244???, 4221638673???, 4221639135???, 4221639420???, 4221639705???, 4221639798???, 4221640134???, 4221640284???
    !DD 4221640995???, 4221641088???, 4221641184???, 4221641943???, 4221644190???, 4221644658???, 4221646134???, 4221647289???
    !DD 4221648000???, 4221649440???, 4221650313???, 4221652239???, 4221653394???, 4221653994???, 4221654009???, 4221655173???
    !DD 4221656073???, 4221656160???, 4221657090???, 4221657375???, 4221657579???, 4221658050???, 4221658089???, 4221659550???
    !DD 4221659715???, 4221659805???, 4221660045???, 4221660555???, 4221661440???, 4221662523???, 4221662655???, 4221663060???
    !DD 4221663300???, 4221663354???, 4221663939???, 4221664254???, 4221664620???, 4221665280???, 4221665349???, 4221666090???
    !DD 4221667080???, 4221667590???, 4221668130???, 4221668478???, 4221668625???, 4221670179???, 4221670200???, 4221670578???
    !DD 4221671529???, 4221671718???, 4221672750???, 4221674115???, 4221676560???, 4221677718???, 4221678195???, 4221679899???
    !DD 4221681219???, 4221683115???, 4221684654???, 4221685398???, 4221685830???, 4221686454???, 4221686694???, 4221688359???
    !DD 4221690588???, 4221690948???, 4221691479???, 4221692103???, 4221693153???, 4221694200???, 4221694569???, 4221694998???
    !DD 4221695028???, 4221695073???, 4221696114???, 4221696249???, 4221696594???, 4221696744???, 4221696774???, 4221696879???
    !DD 4221697623???, 4221698295???, 4221699195???, 4221699324???, 4221699348???, 4221699825???, 4221699975???, 4221702369???
    !DD 4221703209???, 4221703464???, 4221703659???, 4221704703???, 4221704718???, 4221706098???, 4221708114???, 4221708708???
    !DD 4221709890???, 4221709959???, 4221711558???, 4221711843???, 4221712458???, 4221712668???, 4221714948???, 4221714999???
    !DD 4221717123???, 4221717219???, 4221717573???, 4221717678???, 4221718110???, 4221718200???, 4221718434???, 4221718488???
    !DD 4221718644???, 4221720504???, 4221721668???, 4221722193???, 4221722568???, 4221722718???, 4221723513???, 4221723588???
    !DD 4221723645???, 4221723708???, 4221724410???, 4221724470???, 4221725298???, 4221727518???, 4221727935???, 4221728259???
    !DD 4221728379???, 4221728430???, 4221729435???, 4221729624???, 4221729828???, 4221731115???, 4221732060???, 4221732234???
    
    END FUNCTION
    Last edited by David Roberts; 1 Sep 2008, 05:01 AM.

    Comment


    • #3
      Discussion here

      Comment


      • #4
        Added: Method Range

        From post #1 above.
        If RND2 were as fast as RND its sequence would be used up in about 5617 years.
        Of course, in practice it would take much longer if we actually did anything with the numbers generated.

        It is not a big leap to think of a sequence which lives for the life of a PC as opposed to the life of an application. With such a sequence every sub-sequence would be brand spanking new, never seen before and never to be used again. That is not strictly true. If we roll a dice 100 times we should not be surprised to see a six, say, a few times or if we rolled a dice twice 1000 times we should not be surprised to see a three, say, followed by a five, say, a few times. With a period suchas RND2, or RND for that matter, sub-sequences of 20, say, will probably exist more than once. The larger the sub-sequence the less likely it will exist more than once. This is the nature of randomness and the point of this exercise is that whilst we may recognise a sub-sequence it cannot be the same sub-sequence.

        To achieve a sequence which is forever 'open' all we need to do is to simply remember the last seed(s) used. To this end, the easiest method is to dump to a file.

        This sequence is available to any application and it may be used by more than one running application. If a second application opens before the first has closed then it will use the same seeds. This is probably not an issue but we will have a problem if the last application to close has used less random numbers than other closed applications had used. In this case the seeds used in future will have been used before. If a counter was dumped along with the seeds and each application incremented this counter then we can ensure that the seeds are overwritten only if the last application's counter exceeded the counter on file.

        The above concept may be written using a Function but it will have to have code to handle initialization and termination and this will impact on its use between those stages, albeit small. On the other hand if we used a Class available in PB9 and CC5 then we can write initialization and termination code which has no impact between those stages via the Class Methods Create and Destroy respectfully. The termination code determines whether the latest version of the file is to be overwritten or not.

        The code differs to post #1 above in that it generates random numbers and nothing else - no bells and whistles.

        On the very first time an object is created if the file "c:\rand.dat" [ or whatever you decide to use ] is not found then a file will be created.

        The generator's multiplier is 'hard wired' in the Class and I have used one which is not used by post #1 above - a different sequence then.

        Copy the folowing code and Save As "CPRNG.inc" wherever you keep your include files.

        Code:
        ' CPRNG.inc
         
        Macro CreateRandObject
          Local Rand As IPRNG
          Let Rand = Class "CPRNG"
        End Macro
         
        Macro DestroyRandObject
          Rand = Nothing
        End Macro
         
        Class CPRNG
         
          Instance mwcSoph As Dword
          Instance mwcRandom, mwcCarry As Long
          Instance UsageCounter As Quad
         
          Class Method Create
            Local fn As Long
            fn = FreeFile
            mwcSoph = 4221479778???
            If IsFile("c:\rand.dat") Then ' change path to suit
              Open "c:\rand.dat" For Input As #fn
                Input #fn, mwcRandom, mwcCarry, UsageCounter
              Close #fn
            Else
              mwcRandom = &ha5b218da
              mwcCarry = &h3fe8700c
              UsageCounter = 0
              Open "c:\rand.dat" For Output As #fn ' again change path to suit
                Write #fn, mwcRandom, mwcCarry, UsageCounter
              Close #fn
            End If
          End Method
         
          Class Method Destroy
            Local fn, dummy As Long, Counter As Quad
            fn = FreeFile
            Open "c:\rand.dat" For Input As #fn
              Input #fn, dummy, dummy, Counter
            Close #fn
            If UsageCounter > Counter Then
              Open "c:\rand.dat" For Output As #fn ' and again
                Write #fn, mwcRandom, mwcCarry, UsageCounter
              Close #fn
            End If
          End Method
         
          Interface IPRNG
         
            Inherit IUnknown
         
            Method Number As Ext
              Local TmwcSoph As Dword
              Local TmwcRandom, TmwcCarry As Long
              Local eqp, rndl As Long, eq As Quad
         
              eqp = VarPtr( eq )
         
              ' Temporary assignments for asm code
              TmwcSoph = mwcSoph
              TmwcRandom = mwcRandom
              TmwcCarry = mwcCarry
         
              GoSub sRandomLong
              Incr UsageCounter
              Poke Long, eqp, rndl
              Do
                GoSub sRandomLong
                Incr UsageCounter
                ! And rndL, &h0fffffff
                Poke Long, eqp + 4, rndl
              Loop While eq > 999999999999999999
         
              ' Update Instance variables for next execution
              mwcRandom = TmwcRandom
              mwcCarry = TmwcCarry
         
              Method = eq / 1000000000000000000
         
            Exit Method
         
              sRandomLong:
                ! mov eax, TmwcSoph
                ! mov ecx, TmwcRandom
                ! mul ecx
                ! Add eax, TmwcCarry
                ! adc edx, 0
                ! mov TmwcRandom, eax
                ! mov TmwcCarry,  edx
                rndl = TmwcRandom
              Return
         
            End Method
            
            Method Range( a As Long, b As Long ) As Long
              If b < a Then Swap a, b
              Method = Int( ( b - a + 1) * Me.Number + a )
            End Method
        
          End Interface
         
        End Class
        The object creation and destruction is handled by the macros CreateRandObject and DestroyRandObject respectfully.

        In use we would have something like

        Code:
        #Compile Exe
        #Dim All
         
        #Include "CPRNG.inc" ' change to your path
         
        Function PBMain
         
          CreateRandObject
         
          ' Your masterpiece here which gets random numbers from Rand.Number
         
          DestroyRandObject
         
        End Function
        How fast?

        From post #1
        On my machine 10,000,000 iterations of RND take about 321 milliseconds. RND2 takes about 587ms and with the enhanced scope takes about 637ms. With all the bells and whistles stripped out of RND2, number generation and nothing else, we are looking at about 460ms.
        Try this.

        Code:
        #Compile Exe
        #Optimize speed
        #Dim All
        #Tools Off
        #Register None
         
        #Include "WIN32API.INC"
         
        %NOMMIDS  = 1
        %NOGDI    = 1
         
        #Include "CPRNG.inc" ' change to your path
         
        Function PBMain
         
          Local i,j As Long, x As Ext, qFreq, qTimer As Quad
         
          CreateRandObject
         
          QueryPerformanceFrequency qFreq
         
          Tix qTimer
          For i = 1 To 10000000
            x = Rnd
          Next
          Tix End qTimer
          Print Using$("#####.##",qTimer*1000/qFreq) + "ms"
         
         
          Tix qTimer
          For i = 1 To 10000000
            x = Rand.Number
          Next
          Tix End qTimer
          Print Using$("#####.##",qTimer*1000/qFreq) + "ms"
          
          Print: Print "Choose from range 1 to 49":Print
          For j = 1 To 5
            For i = 1 To 6
              Print Using$("###", Rand.Range(1, 49));
            Next
            Print
          Next
          
          DestroyRandObject
         
          WaitKey$
         
        End Function
        NB: In the above it assumed that the Time Stamp Counter and the Performance Counter have the same frequency. If not then just use Tix for comparison.

        On one run I got

        323.39ms
        553.70ms

        This is not bad considering that Instance variables cannot be referenced by name as an operand assembler opcode and temporary variables are used in their stead.

        We can have a repeat sequence scenario for comparing two algorithms. One way would be to save the data on Rand.dat, create an object, use it with the first algorithm, destroy it and then overwrite Rand.dat with the saved data and then create another object for the second algorithm. However, if the second algorithm used less random numbers than the first we would have a sequence position that had been passed. We could code to avoid that but a much simpler approach is to use a duplicate object along the lines of the following pseudo code.
        Code:
        Local Rand1, Rand2 As IPRNG
        Let Rand1 = Class "CPRNG"
        Let Rand2 = Class "CPRNG"
        Execute Algorithm 1 using Rand1.Number
        Execute Algorithm 2 using Rand2.Number
        Rand1 = Nothing
        Rand2 = Nothing
        It does not matter what order we destroy the objects. The seeds and usage count in Rand.dat would reflect the final state of whichever object used the most random numbers.

        Any comments, tips, criticism can be posted at the same discussion set up for post #1 here.
        Last edited by David Roberts; 8 Sep 2008, 09:32 AM. Reason: Removed CurrentUsageCounter - no longer used

        Comment


        • #5
          Added a Method Range. Shied away from features to avoid 'If something' in generator code as in post #1. That could be done in post #1 via a multi-function approach and there is no real difference with a multi-method approach but it 'feels' different using the one interface.

          Usage: Rand.Range(a, b) outputting as Long but could be Quad.
          Last edited by David Roberts; 8 Sep 2008, 09:44 AM. Reason: Syntax this time - oh dear <smile>

          Comment


          • #6
            The CPRNG.inc macros create a local instance of an object. In the above an instance is created locally in PBMain. If we wanted to generate random numbers in a function, for example, as well then we would need to create another instance. However, if the second instance is created before the instance in PBMain is destroyed then it would use the same starting point as the first instance.

            We can avoid repetition if the PBmain instance is destroyed before calling Test and created again, if needed, on returning.

            Of course, we could have a function calling a function, which is what we have with PBMain , and it could get a bit messy.

            Another solution is to create a global instance illustrated in the following.

            Rand is declared globally and assigned in PBMain.

            Code:
            #Compile Exe
            #Optimize speed
            #Dim All
            #Tools Off
            #Register None
            
            #Include "WIN32API.INC"
            
            %NOMMIDS  = 1
            %NOGDI    = 1
            
            #Include "CPRNG.inc"
            
            Global Rand As IPRNG
            
            Function PBMain
            
              Local i As Long
              
              Let Rand = Class "CPRNG"
              
              Print "In PBMain"
              For i = 1 To 5
                Print rand.number
              Next
              Print
              
              Test
                
              Rand = Nothing
              
              WaitKey$
            
            End Function
            
            Function Test As Long
             
             Local i as long
             
             Print "In Test"
             For i = 1 To 5
               Print rand.number
             Next
             
            End Function
            Last edited by David Roberts; 9 Sep 2008, 08:38 AM.

            Comment


            • #7
              Hi David,

              Your code at post #2 do not work as desired, see below
              Code:
              **********
              
              Randomise, ToggleScope
              
              -.595117101522795
               .920743669071494
              -.520147503025958
              -.316013233723124
               7.20697677982881E-3
               .897971030503953
               .250192549348052
              -.22613654066091
              -.14576787743641
               .49641197236139
              
              ToggleScope, RepeatLastSequence
              
               .595117101522795
               .920743669071494
               .520147503025958
               .316013233723124
               7.20697677982881E-3
               .897971030503953
               .250192549348052
               .22613654066091
               .14576787743641
               .49641197236139
              
              **********
              "The trouble with quotes on the Internet is that you can never know if they are genuine." - Abraham Lincoln.

              Comment


              • #8
                I think it does but if you just wanted to repeat the last sequence then remove the statement marked *

                Code:
                PRINT "Randomise, ToggleScope":PRINT
                Randomise
                ToggleScope
                FOR i = 1 TO 10
                  PRINT Rnd2
                NEXT
                PRINT
                PRINT "ToggleScope, RepeatLastSequence":PRINT
                ToggleScope                                                          ' * Remove to just repeat
                RepeatLastSequence
                FOR i = 1 TO 10
                  PRINT Rnd2
                NEXT
                PRINT:PRINT "**********":PRINT
                Added: I'd forgotten how this worked - six years ago!

                Note by John Gleason: "The significant changes to your original code are: 1) toggleScope uses only one generator so it cannot un-toggle then repeat the last sequence anymore. To repeat it
                must be in the same toggle state." from here post #3.

                Revision:
                Code:
                PRINT "Randomise, ToggleScope":PRINT
                Randomise
                ToggleScope
                FOR i = 1 TO 10
                    PRINT Rnd2
                NEXT
                PRINT
                WAITKEY$
                PRINT "RepeatLastSequence":PRINT
                RepeatLastSequence
                FOR i = 1 TO 10
                    PRINT Rnd2
                NEXT
                PRINT:PRINT "**********":PRINT
                from here opening post.
                Last edited by David Roberts; 21 Aug 2014, 11:06 PM.

                Comment

                Working...
                X