SQL Server Forums
Profile | Register | Active Topics | Members | Search | Forum FAQ
 
Register Now and get your question answered!
Username:
Password:
Save Password
Forgot your Password?

 All Forums
 General SQL Server Forums
 Script Library
 Random Number
 New Topic  Reply to Topic
 Printer Friendly
Author Previous Topic Topic Next Topic  

Onamuji
Aged Yak Warrior

USA
504 Posts

Posted - 04/15/2002 :  10:38:41  Show Profile  Visit Onamuji's Homepage  Send Onamuji an AOL message  Reply with Quote
Generates a true random number from xxx to zzz. Uses a table variable so it all depends on the boundaries you specify how long it will take.

create procedure random_number
@lower int = 0,
@upper int = 256,
@number int = null output
as
set nocount on

declare @numbers table (number_id int identity(1,1), value int)

if @lower > @upper
begin
set @number = @lower
set @lower = @upper
set @upper = @number
end

set @number = rand((datepart(mm, getdate()) * 100000) + (datepart(ss, getdate()) * 1000) + datepart(ms, getdate()))

while (select count(*)
from @numbers) < (@upper - @lower + 1)
begin
insert into @numbers (value)
select (@upper - @lower + 1) * rand() + @lower
end

select @number = value
from @numbers
order by newid()
go


Arnold Fribble
Yak-finder General

United Kingdom
1957 Posts

Posted - 04/16/2002 :  12:38:54  Show Profile  Reply with Quote
Er, maybe you could explain what this is doing, cause I've looked at it a few times and it still doesn't make sense to me.


Go to Top of Page

Onamuji
Aged Yak Warrior

USA
504 Posts

Posted - 04/16/2002 :  15:24:15  Show Profile  Visit Onamuji's Homepage  Send Onamuji an AOL message  Reply with Quote
you mean comments?! :-p

first it creates a table with @n number of entries where @n = @upper - @lower + 1 ... each entry is a random number between @lower and @upper. next a random entry is selected from the table and returned.

the reason it had to be done this way is that the first call to RAND() usually happened so fast from a calling function (random_password) that it returned the same value since the time didn't really change. So this way it goes and selects a random spot from the table.

Edited by - onamuji on 04/16/2002 15:25:42
Go to Top of Page

Arnold Fribble
Yak-finder General

United Kingdom
1957 Posts

Posted - 04/17/2002 :  04:24:53  Show Profile  Reply with Quote
(Trying not to think about what happens when you want a number between 1 and 1000000...)
If you go to the other extreme, and consider the common case where it's selecting 0 or 1 -- flipping a virtual coin -- then there's still the problem of speed. Try this, for example:

DECLARE @r int, @i int

CREATE TABLE #RandTest (
id int IDENTITY(1,1) PRIMARY KEY,
r int
)

SET NOCOUNT ON

SET @i = 0
WHILE @i < 10000
BEGIN
EXEC random_number 0, 1, @r OUTPUT
--SET @r = FLOOR(RAND()*2.0)
INSERT INTO #RandTest (r) VALUES (@r)
SET @i = @i + 1
END

SET NOCOUNT OFF

select T1.r, T2.r, COUNT(*)
FROM #RandTest AS T1
INNER JOIN #RandTest AS T2 ON T1.id = T2.id - 1
GROUP BY T1.r, T2.r

DROP TABLE #RandTest

 
Creates a table of 10000 samples, then self joins it to compare each value with the next. Now since 0 and 1 are equally likely outcomes, the sequences 00, 01, 10 and 11 should be equally likely if the numbers are independent. However, I get significant skew towards the value staying the same for adjacent samples:

r1 r2 ct
0 0 3588
1 1 3698
1 0 1356
0 1 1357

 
Compare this with what happens when the line using RAND() is uncommented instead of the call to random_number. This is still a noticeable effect when it's selecting from 0 to 9.




Edited by - Arnold Fribble on 04/17/2002 04:26:03
Go to Top of Page

Onamuji
Aged Yak Warrior

USA
504 Posts

Posted - 04/17/2002 :  10:22:18  Show Profile  Visit Onamuji's Homepage  Send Onamuji an AOL message  Reply with Quote
best result: xp_random_number ... write one... heh heh :-p

Go to Top of Page

Arnold Fribble
Yak-finder General

United Kingdom
1957 Posts

Posted - 04/17/2002 :  10:39:04  Show Profile  Reply with Quote
quote:

best result: xp_random_number ... write one... heh heh :-p



Eh?


Go to Top of Page

robvolk
SQLTeam MVY/MIA

USA
12325 Posts

Posted - 04/17/2002 :  10:49:19  Show Profile  Visit robvolk's Homepage  Reply with Quote
Don't know if this helps, but I tried this and it seems to give some decent results:

SELECT convert(varbinary, newID()) ^ 10000

You can use any bitwise operator, and change the 2nd operand to suit your tastes. If you use this, for example:

SELECT convert(varbinary, newID()) & 127

You'll get values between 0 and 127. The only problem with bitwise AND (&) is filling out the bit pattern properly.

Go to Top of Page

Onamuji
Aged Yak Warrior

USA
504 Posts

Posted - 04/17/2002 :  10:59:41  Show Profile  Visit Onamuji's Homepage  Send Onamuji an AOL message  Reply with Quote
great solution ... great possibilities



Edited by - onamuji on 04/17/2002 11:07:49
Go to Top of Page

Onamuji
Aged Yak Warrior

USA
504 Posts

Posted - 04/17/2002 :  11:17:07  Show Profile  Visit Onamuji's Homepage  Send Onamuji an AOL message  Reply with Quote
so then a possibility being:

set nocount on

declare @compare int

if @lower = @upper
begin
set @number = @lower
return
end

if @lower > @upper
begin
set @number = @lower
set @lower = @upper
set @upper = @number
end

- proper bit map
set @compare = power(ceiling(log(@upper)/log(2)), 2) - 1
set @number = convert(int, convert(varbinary, newid()) & @compare)

-- until a valid number is returned
while @number < @lower or @number > @upper
begin
set @number = convert(int, convert(varbinary, newid()) & @compare) + @lower
end
go


Go to Top of Page

Arnold Fribble
Yak-finder General

United Kingdom
1957 Posts

Posted - 04/17/2002 :  11:23:01  Show Profile  Reply with Quote

create procedure random_number
@lower int = 0,
@upper int = 256,
@number int = null output
as
set nocount on

declare @r float

if @lower > @upper
begin
set @number = @lower
set @lower = @upper
set @upper = @number
end

set @number = FLOOR(RAND(CONVERT(varbinary, NEWID()) & -1) *
(@upper-@lower+1)) + @lower
go



Go to Top of Page

Onamuji
Aged Yak Warrior

USA
504 Posts

Posted - 04/17/2002 :  11:31:07  Show Profile  Visit Onamuji's Homepage  Send Onamuji an AOL message  Reply with Quote
anything is possible ;-)

Go to Top of Page

robvolk
SQLTeam MVY/MIA

USA
12325 Posts

Posted - 04/17/2002 :  11:31:07  Show Profile  Visit robvolk's Homepage  Reply with Quote
I added a little to the original, I'm sticking with bitwise OR (^):

SELECT (convert(varbinary, newid()) ^ 127) % 100

It will give you values from -99 to +99. You can add an offset to it, or use ABS() on it.

Go to Top of Page

Arnold Fribble
Yak-finder General

United Kingdom
1957 Posts

Posted - 04/17/2002 :  11:45:45  Show Profile  Reply with Quote
Rob, this will pick 0 too frequently for small modulus values.
If you put the ABS around the convert it will fix most of the problem.
Why the ^ 127, though?


Edited by - Arnold Fribble on 04/17/2002 11:54:05
Go to Top of Page

robvolk
SQLTeam MVY/MIA

USA
12325 Posts

Posted - 04/17/2002 :  12:45:07  Show Profile  Visit robvolk's Homepage  Reply with Quote
Yeah, it really seems to like those zeros

I only used 127 to fill out 7 bits with 1's. If I used 100, for example, I was getting a lot of even numbers and not too many odds. Try using 10; all I got was 0, 2, 8, and 10 as values. Actually, that was using bitwise AND instead of OR. It doesn't appear to matter as much using OR. You can use any number at all really.

It works much better with larger mod values (10,000+), which you could mod again with the upper limit:

SELECT ((convert(varbinary, newid()) ^ 127) % 1234567) % 10

I was really looking for a way to do it without needing any interative code; it's certainly random enough for me. I also tried this:

SELECT ((convert(varbinary, newid()) ^ convert(int, rand(9) * 100)) % 1234567) % 100

And it produces pretty nice distributions of all the possible values (on multiple runs) except for the zeros. I also tried it with a sequence table:

SELECT ((convert(varbinary, newid()) ^ convert(int, rand(seq) * 100)) % 1234567-seq) % 10
FROM Sequence WHERE seq<=100


And those damn zeros still show up!

Go to Top of Page

Arnold Fribble
Yak-finder General

United Kingdom
1957 Posts

Posted - 04/17/2002 :  15:08:15  Show Profile  Reply with Quote
It's because % produces values in the range -n+1 ... n-1.
n%m for negative n will produce values in the range -m+1..0
n%m for non-negative n will produce values in the range 0..m-1
So the zeros show up twice.

quote:

Try using 10; all I got was 0, 2, 8, and 10 as values. Actually, that was using bitwise AND instead of OR.


Odd, that


Go to Top of Page

php95saj
Starting Member

United Kingdom
43 Posts

Posted - 04/23/2002 :  07:24:35  Show Profile  Reply with Quote
If I wanted to get a random number between 0 and 10000000, what would be the best way to do it. I try this:
CONVERT(INT, ((rand((datepart(mm, getdate()) * 100000) + (datepart(ss, getdate()) * 1000) + datepart(ms, getdate())))* 100000000))

php95saj

Go to Top of Page

Arnold Fribble
Yak-finder General

United Kingdom
1957 Posts

Posted - 04/23/2002 :  07:57:34  Show Profile  Reply with Quote
You mean 0 and 99999999.
GETDATE is treated as a constant throughout a select, so this only works in situations where one value per select is required.
GETDATE can only change 300 times a second at most since that's the precision of datetime.
The initial value emitted by RAND correlates very strongly with the seed value.*

CONVERT(int, RAND(CONVERT(binary(4), NEWID())) * 100000000)


* If you plot n vs RAND(n), you'll find that you get a sawtooth wave with a period somewhere near 53668. e.g. (Numbers is a table of integers, as usual)

SELECT n, RAND(n) r
FROM Numbers
WHERE n BETWEEN 15373 AND 69040

 
Oh, marvellous! I've just noticed that
RAND(2147483563) to RAND(2147483647) and
RAND(-2147483563) to RAND(-2147483648) are all the same (and equal to RAND(0)).
And since the period of the wave doesn't divide the 32-bit space exactly, plugging a uniform-distributed 32-bit seed into RAND doesn't produce a uniform distributed float.


Edited by - Arnold Fribble on 04/23/2002 09:05:56
Go to Top of Page
  Previous Topic Topic Next Topic  
 New Topic  Reply to Topic
 Printer Friendly
Jump To:
SQL Server Forums © 2000-2009 SQLTeam Publishing, LLC Go To Top Of Page
This page was generated in 0.16 seconds. Powered By: Snitz Forums 2000 Version 3.4.03