Showing posts with label number. Show all posts
Showing posts with label number. Show all posts

Tuesday, 31 July 2012

Generating random numbers

One thing that is always difficult in a system is generating a truly random number.  Computers aren't random, they're very logical, therefore this is inherently difficult.  Having said that, there's always a way to calculate a number which is "random enough".  There is no function for this in Uniface, so I'm going to look at a few different ways to achieve this.


1) perform - the only way we could find to do this originally was to build a random number function in C++ and then call out to it from our Uniface program.  Something like this...


  perform "GetRandomNumber" ;call 3gl function which returns 0-32767
  rand = $1 / 32767


2) $uuid - since the Uniface Unique Identifer function was added, this has given an alternative method.  This is largely based on the current timestamp and either includes the processor ID or the ethernet address, depending on the operating system you are using.  The value returned is a 32 character hexadecimal string, so we need to remove the non-numeric characters.  



  rand = "0.%%$replace($replace($uuid,1,'&',"",-1),1,"-","",-1)%%%" * 1



We actually found that this was not random enough on non-Windows systems, as the last part of the identifier is the same throughout each transaction, so we have used characters from 3 identifiers.


3) DIY - you could also create your own random number generator, after all, these are just mathematical formulas.  The C++ "rand" function that we utilise in method (1) is a simple Linear Congruential Generator.  This takes an initial seed value and then uses it to create the next number in the sequence, which is then used as the seed for the next number.  The key is finding a combination of values that gives an evenly distributed spread of numbers, to ensure that the numbers appear suitably random.


  $$rand = ((214013 * $$rand) + 2531011) % 4294967296
  rand = $$rand / 4294967296


As you can see, this relies on the seed value already being populated, which I've stored in a global register in this example.  This could be set in the application shell execute trigger, maybe using $uuid or a time based numeric.  


Often these algorithms return a subset of the bits in order to improve the spread, but it is not possible to do extraction at the bit level in Uniface, as far as I'm aware.  Another algorithm that is popular (and generally considered better) is a Mersenne Twister, but this uses bit-shifting techniques that I don't think are possible in Uniface either.


So let's test the performance of these different methods of 2,000,000 iterations...


1) perform = 00:10.00, 00:10.00, 00:10.01 (10 seconds)
2) $uuid = 00:32.31, 00:32.44, 00:32.39 (over 32 seconds)
3) DIY = 00:34.75, 00:34.72, 00:34.72 (under 35 seconds)


As you can see, the original perform is the quickest method (although we've found that generally using a 3GL function does not hold up very well under load and these tests are only as a single user).  It can be hard to support a 3GL function across multiple platforms, but this solution is mathematically the most random method.  Out of the alternatives, $$uuid is quite simple but does not give a good spread of random numbers, not compared with the DIY method.  


It should be emphasised that none of these methods are truly random, and therefore should not be used for cryptographic purposes.  They should be suitable for simple things though, like simulating a dice throw.


Hopefully one day Uniface will provide it's own $rand or $random function - a native function should perform the best and would hopefully be implemented in a way that was suitably random with a decent spread.


Summary: If it's feasible to use a perform then this is the best way to go, both for speed and randomness.  However, you may wish to consider building your own random number generator, possibly using a Linear Congruential algorithm.

Saturday, 2 June 2012

Validating a numeric string

Today I wanted to validate a numeric string, in the format "YYYYMMDD".  I wasn't particularly worried about checking that there were only 12 months in the year, 28-31 days in the month, or whether the year was in a sensible range, I just wanted to make sure that I'd found a string of 8 numeric digits.


So my first thought was to check the length using $length, an easy place to start...



  if $length(str) = 8 )
    ;good start
  endif



Next I went on to think about checking it was numeric, $number being the first function that popped into my head which seemed relevant...



  if $length(str) = 8 & $number(str) = str )
    ;pretty good
  endif



But then I remember that this matches even if the string contains spaces, which I didn't want (nor do I think is correct, personally), so I had to add a check for that...



  if $length(str) = 8 & $number(str) = str & $scan(str," ") = 0 )
    ;even better
  endif



And then I thought about other "numeric" characters that were allowed.  Depending on your language settings, this could include plus (+), minus (-), fullstop (.) and comma (,) - there may be more as well. 


It was at this point that I thought I must be doing something wrong, this clearly wasn't the best way of doing this.  


Then I remember a suggestion a colleague of mine had made to me when I started this blog - syntax strings.  It's very easy to use a syntax string to pattern match for numbers...



  if ( $length(str) = 8 & str = '#*' )
    ;much better
  endif


I won't go into the details of syntax strings as this is well documented, but suffice to say that a hash (#) means a numeric digit (0-9) and star (*) means any number of them (0-n).  Combined with my length check, this seemed right.  

But then it occurred to me that I could improve this even further...


 if ( str = '########' )
    ;perfect
  endif


In this case I have specified that I require 8 numeric digits, which is exactly what I wanted to check for.  Obviously using a syntax string here has simplified my code and made it much more readable, it's clear to another developer what I'm trying to achieve.

Now usually I'd have settled for the code that was using $length, $number$ and $scan, which would have sufficed and seemed pretty logical as I was writing it.  But noticing how much more elegant this code was got me thinking... How much better does this code perform?  Surely better than all those function calls!

I took the last 3 of my code blocks and ran them 2,000,000 times, giving the following results...

  • $length+$number+$scan = 00:07.70, 00:07.70, 00:07.65 (almost 8 seconds)
  • $length+syntax string = 00:06.33, 00:06.25, 00:06.27 (over 6 seconds)
  • Just the syntax string = 00:05.00, 00:05.09, 00:05.00 (about 5 seconds)


As you can see not only is this code more elegant and therefore more maintainable, but it also performs better.  Given then difference is only a second or two over 2 million iterations, it's not really much of a consideration by itself, but they say every little helps!

Thanks to Dave W for the inspiration which led to this post.

Summary: Don't always jump to using Uniface functions for validation and other checks, it can often be simpler and more efficient to use syntax strings to perform pattern matches.