6

What is the fastest and simplest way to generate fixed length random numbers in Go?

Say to generate 8-digits long numbers, the problem with rand.Intn(100000000) is that the result might be far less than 8-digits, and padding it with leading zeros doesn't look like a good answer to me.

I.e., I care about the the quality of the randomness more in the sense of its length. So I'm thinking, for this specific problem, would the following be the fastest and simplest way to do it?

99999999 - rand.Int63n(90000000)

I.e., I guess Int63n might be better for my case than Intn. Is it ture, or it is only a wishful thinking? Regarding randomness of the full 8-digits, would the two be the same, or there is really one better than the other?

Finally, any better way than above?

UPDATE:

Please do not provide low + rand(hi-low) as the answer, as everyone knows that. It is equivalent of what I'm doing now, and it doesn't answer my real question, "Regarding randomness of the full 8-digits, would the two be the same, or there is really one better than the other? "

If nobody can answer that, I'll plot a 2-D scatter plot between the two and find out myself...

Thanks

xpt
  • 20,363
  • 37
  • 127
  • 216
  • 1
    What is your use case? To me, it seems like you want a string made of 8 digits more than a number *per se*? This answer may help: http://stackoverflow.com/questions/22892120/how-to-generate-a-random-string-of-a-fixed-length-in-golang – Ezra Mar 15 '16 at 02:38
  • @Ezra, the question is actually inspired by that one, which despite several iteration of performance improvement, still can't get rid of that loop. Yes, I do want string made of 8 digits more than a number per se. Even hex digits are fine with me. The point is, my above solution can finish in one shot, which I believe will be much much faster than the fastest solution there, which is far from simple. – xpt Mar 15 '16 at 02:47
  • 1
    There is nothing wrong with padding with zeros: A random number may start with one or more zeros if padded and is still totally random. – Volker Mar 15 '16 at 06:18
  • 1
    @Volker, There is nothing wrong with my personal taste not wanting the padding. You may like `00000023`, or even `00000004` as the answers but I ***personally don't***. I've *already* state that clearly in OP so I don't see the point your arguing about that -- It all comes down to personal taste, so please don't force yours onto others. – xpt Mar 16 '16 at 03:13
  • Please excuse if I hurt you feelings. I just didn't know that random numbers are more a matter of taste than of math. Sorry. – Volker Mar 16 '16 at 06:20
  • Regarding your Update: Can you define what makes good "randomness"? There are several aspects and the whole concept of randomness is hard. What do you mean exactly by "better randomness"? If you think that judging "randomness" by looking at a 2-D scatter is good enough than the proposed low + rand(hi-low) is more than good enough. – Volker Mar 16 '16 at 07:57
  • @Volker, It's OK. I know math is math, but I was hoping for 8 different digits, more or less, while the proposed low + rand(hi-low) can randomly give me numbers like 10000023, or 10000004 etc. I know mine isn't any better. Oh, well, I guess it is what it is then. Time to move on. – xpt Mar 16 '16 at 22:02

4 Answers4

16

This is a general purpose function for generating numbers within a range

func rangeIn(low, hi int) int {
    return low + rand.Intn(hi-low)
}

See it on the Playground

In your specific case, trying to generate 8 digit numbers, the range would be (10000000, 99999999)

sberry
  • 128,281
  • 18
  • 138
  • 165
  • 1
    this gives same number all the time. – VINNUSAURUS Aug 16 '22 at 14:08
  • 1
    If you are running outside the Go playground you can set the seed the PRNG before calling `rand.Intn` like `rand.Seed(time.Now().UnixNano())`. This will yield different, though still consistent between runs, values as well since the playground uses a hard-coded datatime. – sberry Oct 17 '22 at 19:16
3

It depend on value range you want to use.

  1. If you allow value range [0-99999999] and padding zero ip number of char < 8, then use fmt like fmt.Sprintf("%08d",rand.Intn(100000000)).

  2. If you dont want padding, which value in range [10000000, 99999999], then give it a base like ranNumber := 10000000 + rand.Intn(90000000)`

secmask
  • 7,649
  • 5
  • 35
  • 52
2

See it on Playground

crypto/rand package is used to generate number.

func generateRandomNumber(numberOfDigits int) (int, error) {
    maxLimit := int64(int(math.Pow10(numberOfDigits)) - 1)
    lowLimit := int(math.Pow10(numberOfDigits - 1))

    randomNumber, err := rand.Int(rand.Reader, big.NewInt(maxLimit))
    if err != nil {
        return 0, err
    }
    randomNumberInt := int(randomNumber.Int64())

    // Handling integers between 0, 10^(n-1) .. for n=4, handling cases between (0, 999)
    if randomNumberInt <= lowLimit {
        randomNumberInt += lowLimit
    }

    // Never likely to occur, kust for safe side.
    if randomNumberInt > int(maxLimit) {
        randomNumberInt = int(maxLimit)
    }
    return randomNumberInt, nil
}
1

I recently needed to do something like this, but with a certain byte length (rather than number of digits) and with numbers larger than max int64 (so using math/big.Int). Here was my general solution:

See on the Playground (with added code comments)

func generateRandomBigInt(numBytes int) (*big.Int, error) {
    value := make([]byte, numBytes)
    _, err := rand.Reader.Read(value)
    if err != nil {
        return nil, err
    }

    for true {
        if value[0] != 0 {
            break
        }
        firstByte := value[:1]
        _, err := rand.Reader.Read(firstByte)
        if err != nil {
            return nil, err
        }
    }

    return (&big.Int{}).SetBytes(value), nil
}
tstraley
  • 41
  • 2