8

Hy,

I have a very Basic Question which is :

How can i create a random number with 20 digits no floats no negatives (basically an Int) in Swift ?

Thanks for all answers XD

rmaddy
  • 314,917
  • 42
  • 532
  • 579
  • Can the leading digit be a 0? – zaph Aug 11 '15 at 21:25
  • Yea shure that wouldnt mater –  Aug 11 '15 at 21:26
  • 2
    Here are some solutions for 64-bit random integers: http://stackoverflow.com/questions/26549830/swift-random-number-for-64-bit-integers, but note that UInt64 is not large enough for an arbitrary integer with 20 decimal digits (10^20 > 2^64). – Martin R Aug 11 '15 at 21:32
  • 1
    The largest number that can be represented in 64 bits is: 18446744073709551616 and that is insufficient. 20 decimal digits would require 67-bits. How do you propose holding the number? – zaph Aug 11 '15 at 21:40
  • @HuRiXD Did you really want a number that was 20 digits? Or was that an example number picked at random that happens to make computer scientists nervous? – Unome Aug 11 '15 at 21:46
  • It was picket sort of random 20 would be nice but if 14 or 18 makes it easier if would also work –  Aug 11 '15 at 21:53

7 Answers7

15

Step 1

First of all we need an extension of Int to generate a random number in a range.

extension Int {
    init(_ range: Range<Int> ) {
        let delta = range.startIndex < 0 ? abs(range.startIndex) : 0
        let min = UInt32(range.startIndex + delta)
        let max = UInt32(range.endIndex   + delta)
        self.init(Int(min + arc4random_uniform(max - min)) - delta)
    }
}

This can be used this way:

Int(0...9) // 4 or 1 or 1...
Int(10...99) // 90 or 33 or 11
Int(100...999) // 200 or 333 or 893

Step 2

Now we need a function that receive the number of digits requested, calculates the range of the random number and finally does invoke the new initializer of Int.

func random(digits:Int) -> Int {
    let min = Int(pow(Double(10), Double(digits-1))) - 1
    let max = Int(pow(Double(10), Double(digits))) - 1
    return Int(min...max)
}

Test

random(1) // 8
random(2) // 12
random(3) // 829
random(4) // 2374
Luca Angeletti
  • 58,465
  • 13
  • 121
  • 148
  • Love the idea to do it in an extension! +1 – Unome Aug 11 '15 at 21:41
  • Actually here I used the extension only to generate a random number in a range. But you are right, we could transform the `random` function as an extension of `UInt`. – Luca Angeletti Aug 11 '15 at 21:42
  • How will you represent the number 36893488147419103232? (that requires more than 64-bits and still only has 20 decimal digits. – zaph Aug 11 '15 at 21:45
  • 1
    Again, this cannot work with 20 digits. Also note that your `UInt32(..)` calculations can *overflow* on a 64-bit platform if the range includes negative numbers (which is not the case for *this* question). Try `let rnd = Int(Int.min + 10 ..< Int.max - 10)` (it crashes). Here is a safe way to produce random numbers in an arbitrary integer range: http://stackoverflow.com/questions/31391577/how-can-i-generate-large-ranged-random-numbers-in-swift. – Martin R Aug 11 '15 at 21:48
  • 3
    In Swift 4, getting error "cannot invoke initializer for type 'Int' with an argument list of type '(CountableClosedRange)'" – vikzilla Sep 14 '17 at 22:16
  • @LucaAngeletti I checked your Step2 solution by printing 1000 times and it gives me 9 wrong numbers. for _ in 1...1000 { print(random(digits: 2)) }. – ZAFAR007 Apr 01 '20 at 10:38
13

Swift 5: Simple Solution

func random(digits:Int) -> String {
    var number = String()
    for _ in 1...digits {
       number += "\(Int.random(in: 1...9))"
    }
    return number
}

print(random(digits: 1)) //3
print(random(digits: 2)) //59
print(random(digits: 3)) //926

Note It will return value in String, if you need Int value then you can do like this

let number = Int(random(digits: 1)) ?? 0
ZAFAR007
  • 3,049
  • 1
  • 34
  • 45
4

Here is some pseudocode that should do what you want.

generateRandomNumber(20)
func generateRandomNumber(int numDigits){
   var place = 1
   var finalNumber = 0;
   for(int i = 0; i < numDigits; i++){
      place *= 10
      var randomNumber = arc4random_uniform(10)
      finalNumber += randomNumber * place
  }
  return finalNumber
}

Its pretty simple. You generate 20 random numbers, and multiply them by the respective tens, hundredths, thousands... place that they should be on. This way you will guarantee a number of the correct size, but will randomly generate the number that will be used in each place.

Update

As said in the comments you will most likely get an overflow exception with a number this long, so you'll have to be creative in how you'd like to store the number (String, ect...) but I merely wanted to show you a simple way to generate a number with a guaranteed digit length. Also, given the current code there is a small chance your leading number could be 0 so you should protect against that as well.

Unome
  • 6,750
  • 7
  • 45
  • 87
  • 1
    This will probably abort with an overflow exception in Swift. – Martin R Aug 11 '15 at 21:33
  • Probably true... in that case store it in a String like a UUID and parse it back into a number wherever you need it. – Unome Aug 11 '15 at 21:34
  • 2
    Parse it back to a number where? Into a uint128_t? – zaph Aug 11 '15 at 21:41
  • 2
    Good point @zaph. I'm not sure why he would need a number that's 20 digits. But perhaps the question was more geared towards generating a number with a certain amount of digits and 20 was a random number that is unusual. The title asked how to generate a number of digits, and the post is where the mystical number 20 appeared. – Unome Aug 11 '15 at 21:44
2

you can create a string number then convert the number to your required number.

func generateRandomDigits(_ digitNumber: Int) -> String {
    var number = ""
    for i in 0..<digitNumber {
        var randomNumber = arc4random_uniform(10)
        while randomNumber == 0 && i == 0 {
            randomNumber = arc4random_uniform(10)
        }
        number += "\(randomNumber)"
    }
    return number
}

print(Int(generateRandomDigits(3)))

for 20 digit you can use Double instead of Int

Mohammadalijf
  • 1,387
  • 9
  • 19
2

Swift 3 appzyourlifz's answer updated to Swift 3

Step 1:

extension Int {
init(_ range: Range<Int> ) {
    let delta = range.lowerBound < 0 ? abs(range.lowerBound) : 0
    let min = UInt32(range.lowerBound + delta)
    let max = UInt32(range.upperBound   + delta)
    self.init(Int(min + arc4random_uniform(max - min)) - delta)
    }
}

Step 2:

func randomNumberWith(digits:Int) -> Int {
    let min = Int(pow(Double(10), Double(digits-1))) - 1
    let max = Int(pow(Double(10), Double(digits))) - 1
    return Int(Range(uncheckedBounds: (min, max)))
}

Usage:

randomNumberWith(digits:4) // 2271
randomNumberWith(digits:8) // 65273410 
FredFlinstone
  • 896
  • 11
  • 16
1

Here is 18 decimal digits in a UInt64:

(Swift 3)

let sz: UInt32 = 1000000000
let ms: UInt64   = UInt64(arc4random_uniform(sz))
let ls: UInt64   = UInt64(arc4random_uniform(sz))
let digits: UInt64 = ms * UInt64(sz) + ls

print(String(format:"18 digits: %018llu", digits)) // Print with leading 0s.

16 decimal digits with leading digit 1..9 in a UInt64:

let sz: UInt64 = 100000000
let ld: UInt64 = UInt64(arc4random_uniform(9)+1)
let ms: UInt64 = UInt64(arc4random_uniform(UInt32(sz/10)))
let ls: UInt64 = UInt64(arc4random_uniform(UInt32(sz)))
let digits: UInt64 = ld * (sz*sz/10) + (ms * sz) + ls

print(String(format:"16 digits: %llu", digits))
zaph
  • 111,848
  • 21
  • 189
  • 228
1

Swift 4 version of Unome's validate response plus :

  • Guard it against overflow and 0 digit number

  • Adding support for Linux's device because "arc4random*" functions don't exit

With linux device don't forgot to do

#if os(Linux)
    srandom(UInt32(time(nil)))
#endif

only once before calling random.

/// This function generate a random number of type Int with the given digits number
///
/// - Parameter digit: the number of digit
/// - Returns: the ramdom generate number or nil if wrong parameter
func randomNumber(with digit: Int) -> Int? {

    guard 0 < digit, digit < 20 else { // 0 digit number don't exist and 20 digit Int are to big
        return nil
    }

    /// The final ramdom generate Int
    var finalNumber : Int = 0;

    for i in 1...digit {

        /// The new generated number which will be add to the final number
        var randomOperator : Int = 0

        repeat {
            #if os(Linux)
                randomOperator = Int(random() % 9) * Int(powf(10, Float(i - 1)))
            #else
                randomOperator = Int(arc4random_uniform(9)) * Int(powf(10, Float(i - 1)))
            #endif

        } while Double(randomOperator + finalNumber) > Double(Int.max) // Verification to be sure to don't overflow Int max size

        finalNumber += randomOperator
    }

    return finalNumber
} 
Sombre Osmo'z
  • 165
  • 1
  • 8