如何增加或减少数组中某项被选中的概率
How to increase or decrease the probability of an item of an array being picked
假设我有这样一组数字:
// Create Array Of Numbers
let numbers = ["1","2","3","4","5"]
如果我想从数组中打印一个随机数,我可以这样做:
pickedNumber = Int.random(in: 0...numbers.count - 1)
以上行将从我的数组中return一个随机值。
我想做的是,为数组中的每个值设置一个概率。例如:
- Chance of 1 being picked at 10%
- Chance of 2 being picked at 20%
- Chance of 3 being picked at 30%
- Chance of 4 being picked at 35%
- Chance of 5 being picked at 5%
最好的方法是什么?任何指导或建议将不胜感激。我面临的这个问题是在 swiftUI 中。
将 1
添加到数组中 10 次,添加 2
20 次,依此类推。您的 Array
将有 100 个元素,因此您将控制每个元素的概率。
如果您的值总是 5% 的倍数,您可以使用包含 20 个元素的数组并按比例添加值。
比 UI 问题更多的是数学问题,但是:
let probs = [
1 : 10,
2 : 20,
3 : 30,
4 : 35,
5 : 5
]
func randomWithProbability(distribution: [Int : Int]) -> Int {
var distributionArray: [Int] = []
distribution.forEach { (key: Int, value: Int) in
let new = Array(repeating: key, count: value)
distributionArray.append(contentsOf: new)
}
let r = Int.random(in: 0..<distributionArray.count)
return distributionArray[r]
}
并证明它:
struct ContentView: View {
private var results: [Int]
init() {
results = [0,0,0,0,0]
for _ in 0..<1000 {
let i = randomWithProbability(distribution: probs)
results[i-1] += 1
}
}
var body: some View {
VStack(alignment: .leading) {
ForEach(results.indices) { i in
HStack {
Text("\(i)")
Color.blue
.frame(width: CGFloat(results[i]), height: 40)
}
}
}
}
}
- 创建标准化加权范围。
[ 0.0..<0.1,
0.1..<0.3,
0.3..<0.6,
0.6..<0.95,
0.95..<1.0
]
let normalizedWeightedRanges = [10, 20, 30, 35, 5].normalizedWeightedRanges
import Algorithms
public extension Sequence where Element: FloatingPoint {
/// Normalized (`0..<1`) representations of the elements' weights within their sum.
var normalizedWeightedRanges: [Range<Element>] {
guard let sum = sum else {
return []
}
return .init(
reductions(0..<0) {
[=12=].upperBound..< / sum + [=12=].upperBound
}.dropFirst()
)
}
}
public extension Sequence where Element: AdditiveArithmetic {
var sum: Element? { reduce(+) }
}
public extension Sequence {
/// - Returns: `nil` If the sequence has no elements, instead of an "initial result".
func reduce(
_ nextPartialResult: (Element, Element) throws -> Element
) rethrows -> Element? {
var iterator = makeIterator()
return try iterator.next().map { first in
try IteratorSequence(iterator).reduce(first, nextPartialResult)
}
}
}
- 样本。 (二进制搜索会比
firstIndex
更好。)
/// - precondition: `normalizedWeightedRanges` is the result of `Sequence.normalizedWeightedRanges`
func randomIndex<Float: BinaryFloatingPoint>(
inNormalizedWeightedRanges normalizedWeightedRanges: [Range<Float>]
) -> Int
where Float.RawSignificand: FixedWidthInteger {
normalizedWeightedRanges.firstIndex { [random = Float.random(in: 0..<1)] in
[=15=].contains(random)
}!
}
效率更高:
let results = getResults(input:[0.1, 0.3, 0.6, 0.95,1])
func getResults(input: [Float]) -> [Int] {
var results: [Int] = [Int](0..<input.count)
for _ in 0..<iteration {
let random = Float.random(in: 0...1)
for i in input.enumerated() {
if random <= i.element {
results[i.offset] += 1
break
}
}
}
return results
}
假设我有这样一组数字:
// Create Array Of Numbers
let numbers = ["1","2","3","4","5"]
如果我想从数组中打印一个随机数,我可以这样做:
pickedNumber = Int.random(in: 0...numbers.count - 1)
以上行将从我的数组中return一个随机值。
我想做的是,为数组中的每个值设置一个概率。例如:
- Chance of 1 being picked at 10%
- Chance of 2 being picked at 20%
- Chance of 3 being picked at 30%
- Chance of 4 being picked at 35%
- Chance of 5 being picked at 5%
最好的方法是什么?任何指导或建议将不胜感激。我面临的这个问题是在 swiftUI 中。
将 1
添加到数组中 10 次,添加 2
20 次,依此类推。您的 Array
将有 100 个元素,因此您将控制每个元素的概率。
如果您的值总是 5% 的倍数,您可以使用包含 20 个元素的数组并按比例添加值。
比 UI 问题更多的是数学问题,但是:
let probs = [
1 : 10,
2 : 20,
3 : 30,
4 : 35,
5 : 5
]
func randomWithProbability(distribution: [Int : Int]) -> Int {
var distributionArray: [Int] = []
distribution.forEach { (key: Int, value: Int) in
let new = Array(repeating: key, count: value)
distributionArray.append(contentsOf: new)
}
let r = Int.random(in: 0..<distributionArray.count)
return distributionArray[r]
}
并证明它:
struct ContentView: View {
private var results: [Int]
init() {
results = [0,0,0,0,0]
for _ in 0..<1000 {
let i = randomWithProbability(distribution: probs)
results[i-1] += 1
}
}
var body: some View {
VStack(alignment: .leading) {
ForEach(results.indices) { i in
HStack {
Text("\(i)")
Color.blue
.frame(width: CGFloat(results[i]), height: 40)
}
}
}
}
}
- 创建标准化加权范围。
[ 0.0..<0.1,
0.1..<0.3,
0.3..<0.6,
0.6..<0.95,
0.95..<1.0
]
let normalizedWeightedRanges = [10, 20, 30, 35, 5].normalizedWeightedRanges
import Algorithms
public extension Sequence where Element: FloatingPoint {
/// Normalized (`0..<1`) representations of the elements' weights within their sum.
var normalizedWeightedRanges: [Range<Element>] {
guard let sum = sum else {
return []
}
return .init(
reductions(0..<0) {
[=12=].upperBound..< / sum + [=12=].upperBound
}.dropFirst()
)
}
}
public extension Sequence where Element: AdditiveArithmetic {
var sum: Element? { reduce(+) }
}
public extension Sequence {
/// - Returns: `nil` If the sequence has no elements, instead of an "initial result".
func reduce(
_ nextPartialResult: (Element, Element) throws -> Element
) rethrows -> Element? {
var iterator = makeIterator()
return try iterator.next().map { first in
try IteratorSequence(iterator).reduce(first, nextPartialResult)
}
}
}
- 样本。 (二进制搜索会比
firstIndex
更好。)
/// - precondition: `normalizedWeightedRanges` is the result of `Sequence.normalizedWeightedRanges`
func randomIndex<Float: BinaryFloatingPoint>(
inNormalizedWeightedRanges normalizedWeightedRanges: [Range<Float>]
) -> Int
where Float.RawSignificand: FixedWidthInteger {
normalizedWeightedRanges.firstIndex { [random = Float.random(in: 0..<1)] in
[=15=].contains(random)
}!
}
效率更高:
let results = getResults(input:[0.1, 0.3, 0.6, 0.95,1])
func getResults(input: [Float]) -> [Int] {
var results: [Int] = [Int](0..<input.count)
for _ in 0..<iteration {
let random = Float.random(in: 0...1)
for i in input.enumerated() {
if random <= i.element {
results[i.offset] += 1
break
}
}
}
return results
}