Announcement

Collapse
No announcement yet.

Random number?

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

  • Random number?

    So I'm using RND(0, 31) at a rate of 17 times a second. It tends to show some anomalies at that rate. I'm getting a result of 15 or greater most of the time even when I use OPTIMIZE TIMER. Any ideas?
    I'm working on a game where the x position is determined by the random number. Currently it will cause my sprite to move slightly to the right of the center of the region and loiter around that area. If this was a coin toss then I'd get more heads than tails. I have to inject some course correction values to get the sprite to back up. Kind of weird behavior. I typically don't use the RND function this way.

    The only way I can get the sprite even close to x=0 is if I get a result from this RND(-31, 31) and hack off the -31 to 0 numbers from the result. Obviously I'm not trying to do a coin toss. I'm just trying to do incremental but random movement of the sprite.

    Here is the method I'm using:

    Code:
      MyX = RND(0, 31)
    
      SELECT CASE MyX
          CASE < OldMyX
              DECR OldMyX
          CASE > OldMyX
              INCR OldMyX
      END SELECT
    
      MyX = OldMyX
    
      IF MyX > 31 THEN
          MyX = 31
      END IF
      IF MyX < 0 THEN
          MyX = 0
      END IF
    
      OldMyX = MyX
    Like I said this method produces 15 or greater most of the time.

    Well I think I have something that works ok.
    Code:
      RANDOMIZE TIMER
      IF Direction = 0 THEN
          MyX = OldMyX + RND(-5, 10)
      ELSE
          MyX = OldMyX + RND(-10, 5)
      END IF
    
      IF MyX >= 300 THEN
          Direction = 1
      END IF
    
      IF MyX <= 1 THEN
          Direction = 0
      END IF
    
      OldMyX = MyX
    It produces the rambling I was looking for. Thanks

  • #2
    Nothing wrong with the RND(), it's your logic. It will initially creep up to around the mid point (15), but once there it is bound to hover there because you are just incrementing or decrementing by 1 according to whether the RND is greater or less than the current value regardless of how far from that point the actual random value is.

    Once you get a few numbers above 15, most RNDs will walk back down, Once you get a few numbers below 15, most RNDs will walk back up.

    Comment


    • #3
      Originally posted by Jim Fritts View Post
      So I'm using RND(0, 31) at a rate of 17 times a second. It tends to show some anomalies at that rate. I'm getting a result of 15 or greater most of the time even when I use OPTIMIZE TIMER. Any ideas?
      Code:
      MyX = (RND(0, 999) MOD 32



      The world is strange and wonderful.*
      I reserve the right to be horrifically wrong.
      Please maintain a safe following distance.
      *wonderful sold separately.

      Comment


      • #4
        Like I said this method produces 15 or greater most of the time.
        "15 or greater" when you should probably be using "greater than 15"
        The range (0,31) has 32 elements. The lower half is (0,15) and the upper half if (16,31) with each half having 16 members.
        If you use ""15 or greater" then you're counting the top 17 elements out of 32 so you'd expect it to occur more often.

        Comment


        • #5
          Full code not shown... are you constantly re-calling RANDOMIZE TIMER (appears that way in one of your posts)? Typically you would only want to call the RANDOMIZE function once.
          <b>George W. Bleck</b>
          <img src='http://www.blecktech.com/myemail.gif'>

          Comment


          • #6
            Thanks Guys,
            This is what I'm using now. It provides 5 random speed changes forward and 5 random speed changes reverse.

            Code:
              MyTimes = RND(0, 4)
            
              RANDOMIZE TIMER
              SELECT CASE Direction
                  CASE 0
                      SELECT CASE MyTimes
                          CASE 0
                              MyX = OldMyX + RND(0,  8)
                          CASE 1
                              MyX = OldMyX + RND(0,  9)
                          CASE 2
                              MyX = OldMyX + RND(0, 10)
                          CASE 3
                              MyX = OldMyX + RND(0, 11)
                          CASE 4
                              MyX = OldMyX + RND(0, 12)
                      END SELECT
            
                  CASE 1
                      SELECT CASE MyTimes
                          CASE 0
                              MyX = OldMyX + RND( -8, 0)
                          CASE 1
                              MyX = OldMyX + RND( -9, 0)
                          CASE 2
                              MyX = OldMyX + RND(-10, 0)
                          CASE 3
                              MyX = OldMyX + RND(-11, 0)
                          CASE 4
                              MyX = OldMyX + RND(-12, 0)
                      END SELECT
              END SELECT
            
              IF MyX >= 290 THEN
                  MyX = 289
                  Direction = 1
              END IF
            
              IF MyX <= 1 THEN
                  MyX = 0
                  Direction = 0
              END IF
            
              OldMyX = MyX
            Now I just need to randomize the direction at a random interval. That's so random. ha ha

            Comment


            • #7
              Jim, you are still calling RANDOMIZE every time you ask for a random number (based on what you wrote). You want to put the RANDOMIZE in the beginning of your PBMAIN and then you should not need to call it ever again.
              <b>George W. Bleck</b>
              <img src='http://www.blecktech.com/myemail.gif'>

              Comment


              • #8
                Thanks George,
                I'm just reseeding the RND function from the timer to insure a random number every time not a pseudo random number what you would get if you used RANDOMIZE TIMER once.

                Here we have it. Random motion (speed and direction change)
                Code:
                  MyInterval = RND(0, 135)
                
                  SELECT CASE MyInterval
                      CASE 15
                          'change direction
                          DECR Direction
                          IF Direction < 0 THEN
                              Direction = 1
                          END IF
                      CASE 120
                          'change direction
                          DECR Direction
                          IF Direction < 0 THEN
                              Direction = 1
                          END IF
                  END SELECT
                
                  MyTimes = RND(0, 4)
                
                  RANDOMIZE TIMER
                  SELECT CASE Direction
                      CASE 0
                          SELECT CASE MyTimes
                              CASE 0
                                  MyX = OldMyX + RND(0,  8)
                              CASE 1
                                  MyX = OldMyX + RND(0,  9)
                              CASE 2
                                  MyX = OldMyX + RND(0, 10)
                              CASE 3
                                  MyX = OldMyX + RND(0, 11)
                              CASE 4
                                  MyX = OldMyX + RND(0, 12)
                          END SELECT
                
                      CASE 1
                          SELECT CASE MyTimes
                              CASE 0
                                  MyX = OldMyX + RND( -8, 0)
                              CASE 1
                                  MyX = OldMyX + RND( -9, 0)
                              CASE 2
                                  MyX = OldMyX + RND(-10, 0)
                              CASE 3
                                  MyX = OldMyX + RND(-11, 0)
                              CASE 4
                                  MyX = OldMyX + RND(-12, 0)
                          END SELECT
                  END SELECT
                
                  IF MyX >= 290 THEN
                      MyX = 289
                      Direction = 1
                  END IF
                
                  IF MyX <= 1 THEN
                      MyX = 0
                      Direction = 0
                  END IF
                
                  OldMyX = MyX

                Comment


                • #9
                  Reseeding with RANDOMIZE every time gives the SAME number just about every time. It is a special mode because it is called many times before TIMER changes.
                  Code:
                  #compile exe
                  #dim all
                  
                  function pbmain () as long
                    local RndDist(), Cnt as long
                    local Rslt as string
                    randomize timer
                    dim RndDist(31)
                    Rslt = "One RANDOMIZE TIMER at start." + $crlf
                    for Cnt = 1 to 32^3
                      incr RndDist(rnd(0, 31))
                    next
                    for Cnt = 0 to 31 'let's see how many times each number randomly happened
                      Rslt += str$(RndDist(Cnt)) + $crlf
                    next
                    ? Rslt
                    reset RndDist()
                    Rslt = " A RANDOMIZE TIMER every call of RND()." + $crlf
                    for Cnt = 1 to 32^3
                      randomize timer
                      incr RndDist(rnd(0, 31))
                    next
                    for Cnt = 0 to 31 'let's see how many times each number randomly happened
                      Rslt += str$(RndDist(Cnt)) + $crlf
                    next
                    ? Rslt
                  end function
                  Now I'll look at code in your last post.

                  Cheers,
                  Dale

                  Comment


                  • #10
                    Dale and George,
                    "TIMER returns the number of seconds since midnight as a Double-precision floating-point value.
                    The resolution is about 1/100 of a second on NT-based platforms, or 1/18th of a second on earlier platforms."

                    Currently I'm running the code at 17 times a second so the TIMER value should be fine as a random number seed as long as it is incrementing faster than I display the results.

                    Note: I'm not randomizing via timer for every call to RND just every time through the routine. Obviously if you randomize via timer every time you use RND or you randomize via timer > 100 times a second then the randomization will fail and will slide into pseudo random as Dale noticed.

                    Comment


                    • #11
                      Originally posted by Jim
                      I'm just reseeding the RND function from the timer to insure a random number every time not a pseudo random number what you would get if you used RANDOMIZE TIMER once.
                      Jim, Rnd generates a sequence of 2^32 Dwords and outputs them in double precision.

                      Imagine that sequence as a circle. It is carved in stone according to the algorithm employed. When we use Randomize Timer we enter the circle at an unknown position. Each request of Rnd moves us one position further on the circle and the returned value is a function of the previous value, giving us a pseudo-random number as you rightly said.

                      Reseeding Rnd with Randomize Timer simply jumps to another position on the circle, which, remember, is carved in stone. The value returned is no more random by doing so and is a pointless exercise.

                      I'm not randomizing via timer for every call to RND just every time through the routine.
                      The danger with that is we could 'drop' into the circle in a region that we have already been and find ourselves with a partial repeated sequence. Although Timer is double precision and it is recommended we use a single precision for Randomize, at the end of the day Rnd will clip to 32 bits because it uses 32-bit arithmetic internally. There is no danger of an 'overlap' if we use Randomize Timer at the beginning of our code as George recommends.

                      Comment


                      • #12
                        Jim,

                        If you want a high reseeding rate, this approach may be useful to you. It does need a slight delay between reseeding calls and in this case the use of StdOut does that. With a range of 32 you will never get a decent random distribution but it will generally be good enough in most instances if you control the reseeding.
                        Code:
                        ' ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
                        
                            #include "\basic\include\win32api.inc"
                        
                            MACRO FUNCTION reseed
                              MACROTEMP seed
                              LOCAL seed as DWORD
                              ! rdtsc                   ' get number from OS
                              ! bswap eax               ' reverse byte order
                              ! mov seed, eax           ' save reg to variable
                            END MACRO = seed            ' return it
                        
                        ' ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
                        
                        FUNCTION PBmain as LONG
                        
                            LOCAL seed as DWORD
                            LOCAL cnt  as DWORD
                        
                            ! mov cnt, 0                ' zero the counter
                        
                            Do
                        
                              seed = reseed             ' generate a seed
                              StdOut format$(seed)      ' display it
                              ' SleepEx 10,1              ' comment this out if duplicates are not a problem
                              ! add cnt, 1              ' increment the counter
                        
                            Loop while cnt < 100
                        
                            waitkey$
                        
                        End FUNCTION
                        
                        ' ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
                        hutch at movsd dot com
                        The MASM Forum

                        www.masm32.com

                        Comment


                        • #13
                          Dave and Steve Thanks

                          Dave,
                          "When we use Randomize Timer we enter the circle at an unknown position." "Reseeding Rnd with Randomize Timer simply jumps to another position on the circle". So you think there is only one data circle to use and RANDOMIZE TIMER doesn't change the data circle and only changes the entry point on the data circle. I couldn't say that since I don't know what the creator did. I'd like to believe that Bob built in more variability than that.

                          I'd like to think that getting another circle or jumping to an unknown location in a preexisting data circle is more random than continuing with the original circle. So if like you say that the circle is closed and unchanging then Bob should not have called it "Seed the random number generator." or provide this to get the built in sequence "RANDOMIZE CVS(CHR$(255,255,255,255))".

                          He should have called it "Change the start position of the unchangeable pseudo random sequence" What I'm suggesting is that he should never have called it a generator.

                          But of course if it is a true generator then Bob's description would be accurate, RANDOMIZE TIMER would work to produce a new sequence (data circle) and RANDOMIZE CVS(CHR$(255,255,255,255)) would be needed to generate the original default sequence (data circle).

                          Comment


                          • #14
                            Originally posted by Jim
                            So you think there is only one data circle to use and RANDOMIZE TIMER doesn't change the data circle, It just changes the entry point on the data circle.
                            Yes. RANDOMIZE CVS(CHR$(255,255,255,255)) proves an unchanging data circle.

                            The vast majority of generators provide only one data circle going from Linear congruential generators, Marsaglia's Multiply-with-carry, Vigna's xorshift*/xorshift+ generators and so on. It is only in the last few years where we can 'dial in' a sequence number as well as seeding data. I have coded two such generators elsewhere: PCG and Middle Square Weyl Sequence RNG. Both of them have a sequence period of 2^64, are unbelievably fast and pass PractRand to at least 1TB on my machine. I'd port them over to PB but both use 64-bit unsigned integers and Bob Zale never got around to providing them even though Lance Edwards said they were on the 'to do list' in 2001.

                            Originally posted by Steve
                            If you want a high reseeding rate, this approach may be useful to you.
                            I don't think that we should be putting forward methods of a high reseeding rate when it is questionable whether Jim actually needs it. I don't think that Jim's wanting it stands up to scrutiny.

                            On a side note, Steve, I saw your rdtsc method just over a week ago in some other code of yours and initially, it struck me as being neat. However, that did not stand up to scrutiny either. "... you will never get a decent random distribution ..." is putting it mildly; the seeds generated are nothing like being random.

                            What quality of randomness should seeds have? I have Intel RdRand on my CPU but I do not have RdSeed, which was introduced on later chips. Intel says "RDSEED is intended for seeding a software PRNG of arbitrary width. RDRAND is intended for applications that merely require high-quality random numbers." You cannot take seeding a PRNG more seriously than that.

                            Actually, if the internal state of a PRNG is only 32-bit, as with PB's Rnd, then RdRand is OK. On the other hand, if the internal state of a PRNG is 128-bit, say, then we should, if we can, use 4 x 32-bit RdSeeds rather than 4 x 32-bit RdRands. Intel describe the difference here.

                            Comment


                            • #15
                              Actually David I wasn't the one that was trying to convince me to not use the RANDOMIZE TIMER. For me it works fine at 17 displays a second.

                              This is what I see when looking at RANDOMIZE CVS(CHR$(255,255,255,255))

                              CHR$(255,255,255.255) is the default sequence.

                              CHR$(0,0,0,0) is another sequence I'll call A
                              CHR$(0,0,0,1) is another sequence I'll call B
                              .
                              .
                              .
                              CHR$(255,255,255.255) is another sequence we call default.

                              Which would be ~4.295 Billion possible pseudo random sequences (data circles) with that one generator routine. Nothing wrong with that. Hey if I was going all in I'd probably look at white noise or listen to chalk screeching across a blackboard. ha ha
                              Last edited by Jim Fritts; 8 Mar 2019, 04:58 PM.

                              Comment


                              • #16
                                Originally posted by Jim
                                Actually David I wasn't the one that was trying to convince me to not use the RANDOMIZE TIMER.
                                I don't understand that statement - too many negatives floating about.

                                For me it works fine at 17 displays a second.
                                That is good news then until you get an overlap one day, forget what I wrote above and wonder what is going on.

                                CHR$(255,255,255.255) is the default sequence.
                                No, it isn't - it is the default seed and 32-bit, note.

                                It is also worth noting that TIMER only has 5,529,600 possible values equating to 2^22.399, falling well short of 2^32.

                                Comment


                                • #17
                                  Ok so I can use 5.5 million of the ~4.295 Billion pseudo code sequences. Still not shabby. Better than me counting 1, 2, 3, 4,....5.5 million.

                                  Comment


                                  • #18
                                    Ha, ha. So, 0.13% isn't shabby? I see that we reside in polar opposition. I should have mentioned earlier that when it comes to random number generation I am a bit of a perfectionist. If we only want a handful of random numbers then a very poor Linear congruential generator will sufice but I could not bring myself to use it. Irrational? Yep.

                                    With the PCG family I use PCG32 which outputs 32 bits but we have access to 2^63 sequences. On my machine it rocks at 595MHz and passes PractRand to 1TB. The author pushed it to 16TB.

                                    This is the function I use to generate Dwords.
                                    Code:
                                    Function pcg32.rand() As Ulong
                                      Dim As Ulongint oldstate = this.state
                                      this.state = oldstate * 6364136223846793005ULL + this.sequence
                                      Dim As Ulong xorshifted = ((oldstate Shr 18u) xor oldstate) Shr 27u
                                      Dim As Ulong rot = oldstate Shr 59u
                                      Return (xorshifted Shr rot) Or (xorshifted Shl ((-rot) And 31))
                                    End Function
                                    Ulongint is 64-bit unsigned so is a 'bummer' for PowerBASIC.

                                    Comment


                                    • #19
                                      That's ok David I feel your pain. Building games is not my cup of tea either. Ha ha I'm just doing it as an add on for my Pixel Pipe Demo app. Nothing fancy.

                                      Comment


                                      • #20
                                        Since I quoted a FreeBASIC function I should mention that there is no such thing as a perfect BASIC language. I would love to have extended precision in FreeBASIC but double precision is as good as it gets. It uses the 80-bit Floating Point Unit so it does beg a question. I use both PB and FB and when I code in one of them I often find myself wanting something that the other one has. Maybe I should knuckle down and learn C. Nah, that isn't going to happen - I prefer readable code and not having to wear a hair shirt.

                                        Comment

                                        Working...
                                        X