我正在尝试在Apple Swift(iOS)中编写一个函数,该函数将生成给定的包含范围内的任意给定数量的唯一随机数,例如介于0和10之间。因此,如果我说我想要5个唯一的随机数在0到10之间,它将返回一个包含[7,10,2,3,0]或[7,10,2,8,0]等的数组。
我有这个部分:
// Returns an array of unique numbers
func uniqueRandoms(numberOfRandoms: Int, minNum: Int, maxNum: UInt32) -> [Int] {
var uniqueNumbers = [Int]()
while uniqueNumbers.count < numberOfRandoms {
let randomNumber = Int(arc4random_uniform(maxNum + 1)) + minNum
var found = false
for var index = 0; index < uniqueNumbers.count; ++index {
if uniqueNumbers[index] == randomNumber {
found = true
break
}
}
if found == false {
uniqueNumbers.append(randomNumber)
}
}
return uniqueNumbers
}
print(uniqueRandoms(5, minNum: 0, maxNum: 10))
现在我想添加将我不想要的范围内的单个数字列入黑名单的功能。假设我仍然想要0到10之间的5个唯一随机数,但我不希望它包含8个。
那部分导致无限循环(25%以上的时间或更多),我无法弄清楚为什么?这就是我所拥有的:
var blackListNum = 8
// Returns an array of unique numbers
func uniqueRandoms(numberOfRandoms: Int, minNum: Int, maxNum: UInt32, checkBlackList: Bool = false) -> [Int] {
var uniqueNumbers = [Int]()
while uniqueNumbers.count < numberOfRandoms {
let randomNumber = Int(arc4random_uniform(maxNum + 1)) + minNum
var found = false
for var index = 0; index < uniqueNumbers.count; ++index {
if checkBlackList == false {
if uniqueNumbers[index] == randomNumber {
found = true
break
}
} else {
if uniqueNumbers[index] == randomNumber || uniqueNumbers[index] == blackListNum {
found = true
break
}
}
}
if found == false {
uniqueNumbers.append(randomNumber)
}
}
return uniqueNumbers
}
print(uniqueRandoms(5, minNum: 0, maxNum: 10, checkBlackList: true))
我明白我的功能远没有效率,因为我刚刚开始学习Swift,但我想保持它的相似性,因为我想了解它是如何工作的。我不想简单地复制和粘贴别人更有效的解决方案而不理解它。我刚刚学习了变量,常量,if,while,for等语句和其他基础知识,并希望将其保留下去。
答案 0 :(得分:8)
使用Set来存储所有随机数,直到达到预期的random数量,你可以让你的生活更轻松:
func uniqueRandoms(numberOfRandoms: Int, minNum: Int, maxNum: UInt32) -> [Int] {
var uniqueNumbers = Set<Int>()
while uniqueNumbers.count < numberOfRandoms {
uniqueNumbers.insert(Int(arc4random_uniform(maxNum + 1)) + minNum)
}
return Array(uniqueNumbers).shuffle
}
print(uniqueRandoms(5, minNum: 0, maxNum: 10))
func uniqueRandoms(numberOfRandoms: Int, minNum: Int, maxNum: UInt32, blackList: Int?) -> [Int] {
var uniqueNumbers = Set<Int>()
while uniqueNumbers.count < numberOfRandoms {
uniqueNumbers.insert(Int(arc4random_uniform(maxNum + 1)) + minNum)
}
if let blackList = blackList {
if uniqueNumbers.contains(blackList) {
while uniqueNumbers.count < numberOfRandoms+1 {
uniqueNumbers.insert(Int(arc4random_uniform(maxNum + 1)) + minNum)
}
uniqueNumbers.remove(blackList)
}
}
return Array(uniqueNumbers).shuffle
}
uniqueRandoms(3, minNum: 0, maxNum: 10, blackList: 8) // [0, 10, 7]
您需要此扩展程序来重新调整结果:
extension Array {
var shuffle:[Element] {
var elements = self
for index in 0..<elements.count {
let anotherIndex = Int(arc4random_uniform(UInt32(elements.count-index)))+index
if anotherIndex != index {
swap(&elements[index], &elements[anotherIndex])
}
}
return elements
}
}
答案 1 :(得分:7)
直接的方法是创建一个数字数组供您选择,然后在您选择时删除数字:
// create an array of 0 through 10
var nums = Array(0...10)
// remove the blacklist number
nums.removeAtIndex(nums.indexOf(8)!)
var randoms = [Int]()
for _ in 1...5 {
let index = Int(arc4random_uniform(UInt32(nums.count)))
randoms.append(nums[index])
nums.removeAtIndex(index)
}
此方法的优点是您只需要在数组中生成任意数量的随机数。由于您从每次仍然可用的数字中进行选择,因此您无需检查是否已经有随机值。
答案 2 :(得分:3)
我最近发现自己需要一个解决这个问题的方法,但是没有黑名单,我在这个页面上看到了答案,也在this question处看到了答案,但是当我将数字设置为选择范围非常大,而且在选择大量数字时(例如,您选择的总数超过50%的数字)。
所以我尝试了一些解决方案。
第一种是你随机选择一个数字,检查之前是否已经选择了数字,然后选择另一个数字或将其添加到数字列表中。
func randomNumber(between lower: Int, and upper: Int) -> Int {
return Int(arc4random_uniform(UInt32(upper - lower))) + lower
}
func generateRandomUniqueNumbers1(forLowerBound lower: Int, andUpperBound upper:Int, andNumNumbers iterations: Int) -> [Int] {
guard iterations <= (upper - lower) else { return [] }
var numbers: [Int] = []
(0..<iterations).forEach { _ in
var nextNumber: Int
repeat {
nextNumber = randomNumber(between: lower, and: upper)
} while numbers.contains(nextNumber)
numbers.append(nextNumber)
}
return numbers
}
第二个解决方案与vacawama建议的解决方案几乎相同。首先加载一个包含所有可用数字的数组,随机选择一个,然后将其从可用数组中删除,然后将其添加到数字数组中。根据需要重复多个数字。
func generateRandomUniqueNumbers2(forLowerBound lower: Int, andUpperBound upper:Int, andNumNumbers iterations: Int) -> [Int] {
guard iterations <= (upper - lower) else { return [] }
var indices: [Int] = (lower..<upper).sorted()
var numbers: [Int] = []
(0..<iterations).forEach { _ in
let nextNumberIndex = randomNumber(between: 0, and: indices.count)
let nextNumber: Int = indices[nextNumberIndex]
indices.remove(at: nextNumberIndex)
numbers.append(nextNumber)
}
return numbers
}
第三种解决方案是对第一种解决方案的改编,以解决阵列在寻找其中的元素方面很慢的事实。通过将存储的数字数组更改为Set,该操作应该更快。
func generateRandomUniqueNumbers3(forLowerBound lower: Int, andUpperBound upper:Int, andNumNumbers iterations: Int) -> [Int] {
guard iterations <= (upper - lower) else { return [] }
var numbers: Set<Int> = Set<Int>()
(0..<iterations).forEach { _ in
let beforeCount = numbers.count
repeat {
numbers.insert(randomNumber(between: lower, and: upper))
} while numbers.count == beforeCount
}
return numbers.map{ $0 }
}
我非常确定解决方案1会在您有100个数字可供选择的情况下陷入困境,而您想要90个独特的数字。当你选择第80个数字时,你只有20%的机会选择一个尚未被选中的数字。如果你有5000个数字并且你仍然想要它们的90%,那它就会非常糟糕。
我认为解决方案2可以更好地处理它,但我不确定什么样的性能会从大型数组中删除这么多的值。
我不知道该怎么做解决方案3.可能在中间的某个地方是我的猜测。
我设置XCTest来测量两种算法在不同负载条件下的某些性能。有2个参数:人口和密度。人口是可供选择的总人数,密度是我们想要提取的人口的百分比(即:80人口意味着80个数字可供选择,50%的密度意味着我们想要选择80个人群随机抽取40个唯一数字。
我对3种不同种群(5,250和12,500)和密度值(10%,50%和90%)的每种组合进行了9次测试。根据测试能够完成的速度或速度,我调整了我执行的测试迭代次数(从一次传递到多达2,500次传递)。
结果如下:
(Population: 5; Density: 10%; Iterations: 2,500): 0.0056s
(Population: 250; Density: 10%; Iterations: 50) : 0.0046s
(Population: 12,500; Density: 10%; Iterations: 10) : 1.33s
(Population: 5; Density: 50%; Iterations: 2,500): 0.0131s
(Population: 250; Density: 50%; Iterations: 50) : 0.0912s
(Population: 12,500; Density: 50%; Iterations: 1) : 4.09s
(Population: 5; Density: 90%; Iterations: 2,500): 0.0309s
(Population: 250; Density: 90%; Iterations: 10) : 0.0993s
(Population: 12,500; Density: 90%; Iterations: 1) : 23s
(Population: 5; Density: 10%; Iterations: 2,500): 0.0184s
(Population: 250; Density: 10%; Iterations: 50) : 0.0086s
(Population: 12,500; Density: 10%; Iterations: 10) : 0.103s
(Population: 5; Density: 50%; Iterations: 2,500): 0.0233s
(Population: 250; Density: 50%; Iterations: 50) : 0.0125s
(Population: 12,500; Density: 50%; Iterations: 1) : 0.0209s
(Population: 5; Density: 90%; Iterations: 2,500): 0.0242s
(Population: 250; Density: 90%; Iterations: 10) : 0.0046s
(Population: 12,500; Density: 90%; Iterations: 1) : 0.0278s
(Population: 5; Density: 10%; Iterations: 2,500): 0.00672s
(Population: 250; Density: 10%; Iterations: 50) : 0.0024s
(Population: 12,500; Density: 10%; Iterations: 10) : 0.0148s
(Population: 5; Density: 50%; Iterations: 2,500): 0.0134s
(Population: 250; Density: 50%; Iterations: 50) : 0.00769s
(Population: 12,500; Density: 50%; Iterations: 1) : 0.00789s
(Population: 5; Density: 90%; Iterations: 2,500): 0.0209s
(Population: 250; Density: 90%; Iterations: 10) : 0.00397s
(Population: 12,500; Density: 90%; Iterations: 1) : 0.0163s
(Case 1): Solution 1 is fastest; then 3; then 2
(Case 2): Solution 3 is fastest; then 1; then 2
(Case 3): Solution 3 is fastest; then 2; then 3
(Case 4): Solution 1 is fastest; then 3; then 2
(Case 5): Solution 3 is fastest; then 2; then 1
(Case 6): Solution 3 is fastest; then 2; then 1
(Case 7): Solution 3 is fastest; then 2; then 1
(Case 8): Solution 3 is fastest; then 2; then 1
(Case 9): Solution 3 is fastest; then 2; then 1
正如我在第一个解决方案中所怀疑的那样,它确实陷入了大量人口和高密度的困境。当你没有那么多人口,或者当你只选择2个数字时,它仍然很快,但在这些条件下所有解决方案都非常快。即使情况解决方案1可以从比解决方案2或3更快的250个群体中选择25个随机数,实时差异也非常小。
但是,指出如果你想从一个非常庞大的人口中获得很少的唯一数字(即:来自12,500个池中的2个唯一数字),有用的是,解决方案1是最快的,比约快77%解决方案3,两者都比解决方案2快几个数量级。对于我的具体情况,几乎所有时间都接近我将要做的事情,所以我可能会做一个特定的功能,特别是使用解决方案1从大量数据池中选择2个唯一数字。
总的来说,解决方案3非常接近全能最佳算法。但是对于大型数据集,请根据您计划使用它们的方法来考虑这些解决方案。
答案 3 :(得分:2)
这是我的解决方案。 SwiftStub:
extension Array {
func shuffle() -> Array<Element> {
var newArray = self
for i in 0..<newArray.count {
let j = Int(arc4random_uniform(UInt32(newArray.count)))
guard i != j else { continue }
swap(&newArray[i], &newArray[j])
}
return newArray
}
}
func uniqueRandoms(count: Int, inRange range: Range<Int>, blacklist: [Int] = []) -> [Int] {
var r = [Int](range)
.filter{ !blacklist.contains($0) }
.shuffle()
return Array(r[0..<count])
}
let x = uniqueRandoms(5, inRange: 1...10000)
let y = uniqueRandoms(5, inRange: 1...10, blacklist: [2,4,6,8,10])
print(x)
print(y)
filter
过滤掉黑名单上的数字。
shuffle
是添加到Array
课程的扩展程序。如果需要,可以将其作为单独的函数实现。
return Array(r[0..<count])
从现有数组的Slice
创建一个新数组。
当列表小于要求的count
时,这有一个潜在的索引超出绑定错误。例如,这些会崩溃:
let a = uniqueRandoms(10, inRange: 1...5)
let b = uniqueRandoms(3, inRange: 1...5, blacklist: [1,2,3,4])
将处理作为OP的练习。
答案 4 :(得分:0)
<div class="bg">
<h1>text</h1>
</div>
我创建了2个空数组&#34; randomNumbers&#34;和&#34; blackList&#34;然后设置一个for循环语句。 在循环语句中,我初始化变量&#34; number。&#34;我指定&#34;数字&#34;对于一个随机数,然后我使用while语句和.contians来检查&#34; number&#34;已经在数组&#34; randomNumbers&#34;或者如果&#34;数字&#34;在数组&#34; blackList&#34;如果任一数组包含&#34;数字,&#34; &#34;数字被重新分配,直到两个数组都不包含&#34; number&#34;。然后我randomNumbers.append(number)追加&#34; number&#34;进入&#34; randomNumbers&#34;然后循环重新开始。
答案 5 :(得分:-1)
这么简单的方法是:
let UniqueIdNumber = CFUUIDCreateString(nil, CFUUIDCreate(nil))