Scala:模拟 1 到 45 范围内的乐透号码生成器
Scala : simulate Lotto number generator in Range 1 to 45
以下是模拟范围为 1 到 45 的彩票的命令式解决方案,每次我们生成一个号码 n1 时,该号码都会从可能的号码集中删除。
是否有可能以更实用的方式实现同样的功能?即使用地图、过滤器等
def getNumbers :Array[Int] = {
val range = 1 to 45
var set = range.toSet
var resultSet:Set[Int] = Set()
var current: Int = 0
while(resultSet.size < 5 ){
current = Random.shuffle(set).head // pick the head of the shuffled set
set -= current
resultSet += current
}
resultSet.toArray
}
"edit"
示例从 1 到 5 范围内选择 3 个号码
Original Set is {1,2,3,4,5}
{1,2,3,4,5} shuffle(1) picked at random 3
{1,2,4,5} shuffle(2) picked at random 2
{1,4,5} shuffle(3) picked at random 4
original Set becomes {1,5}
numbers picked {3,2,4}
每次随机播放不同的 SET! =>不同的概率
我希望看到具有 5 次随机播放而不是 1 次随机播放的功能性 "method"!
当然,这是可能的。集合 API 拥有您需要的一切。您正在寻找的是 take
,它将采用集合的前 n
个元素,或者如果元素少于 n
.[=14,则与集合中的元素一样多=]
Random.shuffle(1 to 45).take(5).toArray
我同意 m-z 的观点,但是如果您真的想要不断重新洗牌的函数形式,那么您需要这样的东西:
import scala.util.Random
import scala.annotation.tailrec
val initialSet = 1 to 45
def lottery(initialSet: Seq[Int], numbersPicked: Int): Set[Int] = {
@tailrec
def internalTailRec(setToUse: Seq[Int], picksLeft: Int, selection: Set[Int]):Set[Int]= {
if(picksLeft == 0) selection
else {
val selected = Random.shuffle(setToUse).head
internalTailRec(setToUse.filter(_ != selected), picksLeft - 1, selection ++ Set(selected))
}
}
internalTailRec(initialSet, numbersPicked, Set())
}
lottery(initialSet, 5)
我也更喜欢@m-z 的解决方案,并且同意他关于概率的推理。阅读 Random.shuffle
的源代码可能是一项值得练习的工作。
每次迭代都会从 range
中删除一个元素并将其添加到累加器 acc
,这与您的命令式方法类似,只是我们没有改变集合和计数器。
import scala.util.Random._
import scala.annotation.tailrec
def getNumbers(): Array[Int] = {
val range = (1 to 45).toSeq
@tailrec
def getNumbersR(range: Seq[Int], acc: Array[Int], i: Int): Array[Int] = (i, range(nextInt(range.size))) match{
case (i, x) if i < 5 => getNumbersR(range.filter(_ != x), x +: acc, i + 1)
case (i, x) => acc
}
getNumbersR(range, Array[Int](), 0)
}
scala> getNumbers
res78: Array[Int] = Array(4, 36, 41, 20, 14)
这将模拟您想要的行为:
画一张,向左重新洗牌数据,画一张等等。
import scala.util.Random
import scala.annotation.tailrec
def draw(count: Int, data: Set[Int]): Set[Int] = {
@tailrec
def drawRec( accum: Set[Int] ) : Set[Int] =
if (accum.size == count )
accum
else
drawRec( accum + Random.shuffle( (data -- accum).toList ).head )
drawRec( Set() )
}
例子
scala> draw(5, (1 to 45).toSet)
res15: Set[Int] = Set(1, 41, 45, 17, 22)
scala> draw(5, (1 to 45).toSet)
res16: Set[Int] = Set(5, 24, 1, 6, 28)
以下是模拟范围为 1 到 45 的彩票的命令式解决方案,每次我们生成一个号码 n1 时,该号码都会从可能的号码集中删除。
是否有可能以更实用的方式实现同样的功能?即使用地图、过滤器等
def getNumbers :Array[Int] = {
val range = 1 to 45
var set = range.toSet
var resultSet:Set[Int] = Set()
var current: Int = 0
while(resultSet.size < 5 ){
current = Random.shuffle(set).head // pick the head of the shuffled set
set -= current
resultSet += current
}
resultSet.toArray
}
"edit"
示例从 1 到 5 范围内选择 3 个号码
Original Set is {1,2,3,4,5}
{1,2,3,4,5} shuffle(1) picked at random 3
{1,2,4,5} shuffle(2) picked at random 2
{1,4,5} shuffle(3) picked at random 4
original Set becomes {1,5}
numbers picked {3,2,4}
每次随机播放不同的 SET! =>不同的概率
我希望看到具有 5 次随机播放而不是 1 次随机播放的功能性 "method"!
当然,这是可能的。集合 API 拥有您需要的一切。您正在寻找的是 take
,它将采用集合的前 n
个元素,或者如果元素少于 n
.[=14,则与集合中的元素一样多=]
Random.shuffle(1 to 45).take(5).toArray
我同意 m-z 的观点,但是如果您真的想要不断重新洗牌的函数形式,那么您需要这样的东西:
import scala.util.Random
import scala.annotation.tailrec
val initialSet = 1 to 45
def lottery(initialSet: Seq[Int], numbersPicked: Int): Set[Int] = {
@tailrec
def internalTailRec(setToUse: Seq[Int], picksLeft: Int, selection: Set[Int]):Set[Int]= {
if(picksLeft == 0) selection
else {
val selected = Random.shuffle(setToUse).head
internalTailRec(setToUse.filter(_ != selected), picksLeft - 1, selection ++ Set(selected))
}
}
internalTailRec(initialSet, numbersPicked, Set())
}
lottery(initialSet, 5)
我也更喜欢@m-z 的解决方案,并且同意他关于概率的推理。阅读 Random.shuffle
的源代码可能是一项值得练习的工作。
每次迭代都会从 range
中删除一个元素并将其添加到累加器 acc
,这与您的命令式方法类似,只是我们没有改变集合和计数器。
import scala.util.Random._
import scala.annotation.tailrec
def getNumbers(): Array[Int] = {
val range = (1 to 45).toSeq
@tailrec
def getNumbersR(range: Seq[Int], acc: Array[Int], i: Int): Array[Int] = (i, range(nextInt(range.size))) match{
case (i, x) if i < 5 => getNumbersR(range.filter(_ != x), x +: acc, i + 1)
case (i, x) => acc
}
getNumbersR(range, Array[Int](), 0)
}
scala> getNumbers
res78: Array[Int] = Array(4, 36, 41, 20, 14)
这将模拟您想要的行为:
画一张,向左重新洗牌数据,画一张等等。
import scala.util.Random
import scala.annotation.tailrec
def draw(count: Int, data: Set[Int]): Set[Int] = {
@tailrec
def drawRec( accum: Set[Int] ) : Set[Int] =
if (accum.size == count )
accum
else
drawRec( accum + Random.shuffle( (data -- accum).toList ).head )
drawRec( Set() )
}
例子
scala> draw(5, (1 to 45).toSet)
res15: Set[Int] = Set(1, 41, 45, 17, 22)
scala> draw(5, (1 to 45).toSet)
res16: Set[Int] = Set(5, 24, 1, 6, 28)