Generating Random Numbers in ColdFusion

There was a post last week on cf-talk about random number generation with ColdFusion, and in particular, the use of the randomize() function. The ColdFusion documentation indicates that you should call randomize() before calling rand() in order to ensure highly random numbers (though the same applies to randRange()). The problems are:

  1. The randomize() function only takes in an int. The best way to generate a unique number is by calling getTickCount() which returns a long. The two functions cannot be used together.
  2. If you don’t use a unique number to seed the random number generator, then your results are anything but random. Each time you call rand() or randRange() without reseeding the random number generator, you will get predictable results. In other words, the algorithm for generating random numbers is, ironically enough, predictable, so the only way to get (pseudo) random numbers is by using a unique, dynamic seed. The current time in milliseconds is a perfect choice (and is what gets used by default).

As Sam Neff pointed out on cf-talk, it seems better not to call randomize() in order to allow the ColdFusion server to seed the random number generator with the current time in milliseconds — a long value that cannot otherwise be passed in — and I tend to agree. The problem with this, however, is synchronization since two requests made at the exact same time will cause the same “random” number to be generated. Fortunately, there’s a good solution.

Sam also suggested creating an instance of java.util.Random, storing it in the application scope, and using it to generate your random numbers. It automatically gets seeded with the current time in milliseconds, and will keep returning highly random numbers each time you call it. If you create a new instance of it on each request, you will get fairly random numbers since it will get re-seeded with the current time in milliseconds, however by storing it in your application scope, you can seed it once, then get highly random numbers by calling one of its next methods to get the next pseudorandom, uniformly distributed value from its internal sequence. This sounds like a pretty good solution to me. It also eliminates the possibility of creating two random number generators at the exact same time. If two random number generators using the current time in milliseconds as seeds are instantiated simultaneous, the “random” numbers they generate will be identical. Oops.

In order to make the solution described above a little easier, I threw together a Randomize component which does the following:

  1. Creates a single instance of java.util.Random at the time it’s instantiated and continues to use it for generating additional random numbers. By putting the Random component in some persistent scope, you can ensure that the same instance of Random is being used, and hence your numbers will be highly random.
  2. Allows you to seed the random number generator, and happily accepts ints or longs, which means you can pass in the result of calling getTickCount(). (Just don’t try to pass in a decimal of any kind — it won’t like that.)
  3. Allows you to easily set bounds for your random numbers using the setBounds() and setMax() functions. These can be changed without having to create a new instance of the Randomize component.
  4. Provides a nextBoolean() function for getting back random booleans.

If none of this made much sense to you, the bottom line is that if you need to generate a lot of highly random numbers, create an instance of this component, store it in a persistent scope, and call next() on it whenever you need a random number.

The code is here:

<cfcomponent output="no"><cfset my = structNew()/><cfset my.rnd = createObject("java", "java.util.Random").init()/><cfset my.min = 0/><cfset my.max = 0/><cfset my.cnt = 0/><!-- Set the minimum and maximum. ---><cffunction name="setBounds" access="public" returnType="void" output="no"><cfargument name="min" type="numeric" required="true"/><cfargument name="max" type="numeric" required="true"/><cfset my.min = arguments.min/><cfset my.max = arguments.max/></cffunction><!--- Set the maximum.  The minimum is automatically 0. ---><cffunction name="setMax" access="public" returnType="void" output="no"><cfargument name="max" type="numeric" required="true"/><cfset my.max = arguments.max/></cffunction><!--- Sets the seed.  Accepts ints and longs (but no decimals). ---><cffunction name="setSeed" access="public" returnType="void" output="no"><cfargument name="seed" type="string" required="true"/><cfset var l = createObject("java", "java.lang.Long").init(arguments.seed)/><cfset my.rnd.setSeed(l.longValue())/></cffunction><!--- Returns the next highly random number. ---><cffunction name="next" access="public" returnType="numeric" output="no"><cfif my.min + my.max eq 0><cfreturn my.rnd.nextInt()/><cfelse><cfreturn (my.rnd.nextInt(javaCast("int",((my.max+1) - my.min))) + my.min)/></cfif></cffunction><!--- Returns a random boolean. ---><cffunction name="nextBoolean" access="public" returnType="boolean" output="no"><cfreturn my.rnd.nextBoolean()/></cffunction></cfcomponent>

3 Responses to Generating Random Numbers in ColdFusion

  1. Looks like you can setBounds(-10,10) and then next() will not return a bounded number. Should setBounds() check that min is ge 0?

  2. Laurence Middleton says:

    Hmm, I don’t think that next() does the expected thing.My guess of the desired result: if A, B are integers for min and max, and A != B, we want a random int from A to B inclusive. If A==B (special case: both are zero), we want a random unbounded int.But we won’t get that from the code above, because it checks for max+min == 0 instead of max – min == 0.Also min and max are “numeric” but not necessarily integer; if the user sets min=0.9 and max=1.2, the only returned value is 0.9, rather than 1, or a float in the range [0.9, 1.2].The Java docs for Random (http://java.sun.com/j2se/1.5.0/docs/api/java/util/Random.html) describe getFloat() which returns a floating point number on [0,1) — i.e. GTE zero and strictly LT one. Maybe if min and/or max is set to a non-integer number and they are unequal, the component’s next() should return a float by scaling a getFloat() result to the appropriate range?The easier course is to enforce min and max to be integer rather than just numeric (a pity type=”int” is not available to cfargument) — no reason to demand non-negative values.While we’re at it, it would make sense to initialize min = Integer.MIN_VALUE and max = Integer.MAX_VALUE since that’s what the unbounded range is already. Hmmm, maybe a resetRange() method of the component could do this for us? And then if a user sets max equal to min, next() will just return min, which is after all in the range albeit not usefully random. 🙂

  3. Ivory Sampson says:

    Hi, I think this code you put is great. I am new to coldfusion. I work part time at the City College of NY, and I am a CS student there. I want to be a better programmer so I took a job as a college assistant with the tech dept. I am supposed to generate a random number each time someone sends us an email request for service. But in this code, I need to do a search of the database to make sure that I dont repeat any of the numbers that were already generated. How do I do that?Would you I use this with your code?SELECT MovieTitle, PitchText, Summary DateInTheatersFROM FilmsWHERE MovieTitle LIKE ‘%#FORM.MovieTitle#%’ORDER BY MovieTitleI got this from the macromedia Coldfusion MX book, page 269.How would I incorporate this into your code? Or would it be different?Thank you.Ivory