生成特定位数的随机数

8

你好,

我有一个非常基础的问题:

如何在Swift中创建一个20位数字的随机数,不包含小数和负数(即整数)?

感谢所有的回答XD。


首位数字可以是0吗? - zaph
是的,当然,那没关系。 - user5161989
2
以下是一些64位随机整数的解决方案:https://dev59.com/YV8d5IYBdhLWcg3wsD1W,但请注意,UInt64对于具有20个十进制数字的任意整数来说不够大(10^20 > 2^64)。 - Martin R
1
64位可以表示的最大数字是:18446744073709551616,但这是不够的。20个十进制数字需要67位。你打算如何保存这个数字? - zaph
@HuRiXD 你真的想要一个20位数的数字吗?还是那只是随机挑选的一个例子,让计算机科学家感到紧张? - Unome
最好是随机的20个,但如果14或18更容易的话也可以。 - user5161989
7个回答

15

步骤 1

首先,我们需要扩展Int以生成一个指定范围内的随机数。

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)
    }
}

这可以这样使用:

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

第二步

现在我们需要一个函数来接收所需的位数,计算随机数的范围,并最终调用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)
}

测试

random(1) // 8
random(2) // 12
random(3) // 829
random(4) // 2374

喜欢用扩展程序的方式实现这个想法!+1 - Unome
实际上,我只是使用扩展来生成一个范围内的随机数。但你说得对,我们可以将random函数转换为UInt的扩展。 - Luca Angeletti
你将如何表示数字36893488147419103232?(它需要超过64位,但仍然只有20个十进制数字。) - zaph
1
再次强调,这无法处理20位数字。还要注意,如果UInt32(..)计算的范围包括负数(这不是问题的情况),则在64位平台上可能会溢出。尝试let rnd = Int(Int.min + 10 ..< Int.max - 10)(它会崩溃)。以下是一种安全的方法来生成任意整数范围内的随机数:http://stackoverflow.com/questions/31391577/how-can-i-generate-large-ranged-random-numbers-in-swift。 - Martin R
3
在 Swift 4 中,出现了错误“无法使用类型为'Int'的初始化程序调用参数类型为'(CountableClosedRange<Int>)'的参数列表”。 - vikzilla
@LucaAngeletti 我通过打印1000次来检查你的Step2解决方案,结果给了我9个错误的数字。 for _ in 1...1000 { print(random(digits: 2)) }。 - ZAFAR007

13

Swift 5:简单解决方案

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

注意:该函数将返回一个 String 类型的值。如果您需要得到一个 Int 类型的值,可以像这样操作:

let number = Int(random(digits: 1)) ?? 0

4
这里有一些伪代码,应该可以满足你的需求。
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
}

很简单。你需要生成20个随机数,并将它们分别乘以十位、百位、千位等应该在的位置上的值。这样可以保证生成正确大小的数字,但每个位置上使用的数字都是随机的。
更新:正如评论中所说,使用这么长的数字很可能会引起溢出异常,因此你需要有创意地决定如何存储该数字(例如字符串等)。但我只是想向你展示一种简单的方法来生成具有保证位数的数字。另外,根据当前代码,你的前导数字有很小的几率是0,因此你也应该对此进行保护。

1
这可能会在Swift中引发溢出异常。 - Martin R
很可能是真的...在这种情况下,将其存储在一个字符串中,例如UUID,并在需要时将其解析回数字。 - Unome
2
解析它返回一个数字到哪里?转换成uint128_t类型? - zaph
2
@zaph 说得好。我不确定他为什么需要一个20位数。但也许问题更偏向于生成一定数量的数字,而20只是一个不寻常的随机数字。标题问如何生成数字,而帖子中出现了神秘的数字20。 - Unome

2

Swift 3 appzyourlifz的答案已更新为Swift 3

步骤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)
    }
}

步骤二:

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)))
}

使用方法:

randomNumberWith(digits:4) // 2271
randomNumberWith(digits:8) // 65273410 

2

你可以创建一个字符串数字,然后将该数字转换为所需的数字。

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)))

如果要使用20位数字,可以使用Double而不是Int。


1

这里是一个UInt64中的18个十进制数字:

(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.

一个以1..9为首位的16位十进制数字在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))

感谢您的解决方案。如何获得16个十进制位? - Anton Kashpor
1
更新了答案,使其支持16位数字,其中首位数字必须为1-9。 - zaph

1

Unome的验证响应加强版的Swift 4版本:

  • 防止溢出和0位数字

  • 添加对Linux设备的支持,因为“arc4random *”函数不存在

在使用Linux设备时,请不要忘记执行以下操作

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

只有在调用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
} 

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接