如何在 Kotlin 中获取随机数?

304

有一种通用的方法可以返回两个参数之间的随机整数,就像 Ruby 中使用 rand(0..n) 一样。

有什么建议吗?

25个回答

679

我建议在IntRange上创建一个扩展函数,以此方式生成随机数:(0..10).random()

简而言之,Kotlin >= 1.3版本可在所有平台使用同一Random

从1.3版本开始,Kotlin自带一个多平台的Random生成器。它在这个KEEP中有描述。下面描述的扩展现在已经成为Kotlin标准库的一部分,可以像这样使用:

val rnds = (0..10).random() // generated random from 0 to 10 included

Kotlin < 1.3

1.3版本之前,在JVM上我们使用Random甚至在JDK>1.6时使用ThreadLocalRandom

fun IntRange.random() = 
       Random().nextInt((endInclusive + 1) - start) + start

使用方式如下:

// will return an `Int` between 0 and 10 (incl.)
(0..10).random()

如果您希望该函数仅返回1,2,...,9(不包括10),请使用使用until构造的范围:

(0 until 10).random()

如果你正在使用JDK > 1.6,那么请使用ThreadLocalRandom.current()而不是Random()

KotlinJs和其他变体

对于kotlinjs和其他不允许使用java.util.Random的用例,请参见这个替代方案

此外,还可以查看此答案以获取我的建议的其他变体。它还包括一个用于随机生成Char的扩展函数。


我假设这也使用了Java的java.util.Random - Simon Forsberg
2
val rnds = (0..10).random() 实际生成从 0 到 10(包括 10 和 0)的随机数。 - xst
6
虽然每次调用时返回相同的数字,但它是有效的。 - taha
假设你想要一个10位数字。那么它会在内存中创建一个巨大的列表,然后随机取出一个数字。或者我理解错了吗? - Rob Anderson
@RobAnderson 不会创建一个列表,只会创建一个包装类型IntRange,其中有两个整型字段表示范围的起点和终点。然后 IntRange.random 方法会调用 nextInt(IntRange) 方法,它只是从startendInclusive 包装了一下 nextInt. - Salem
显示剩余2条评论

51

生成一个介于from(包含)和to(不包含)之间的随机整数

import java.util.Random

val random = Random()

fun rand(from: Int, to: Int) : Int {
    return random.nextInt(to - from) + from
}

1
如果你想要获取一对范围内的随机整数,可以在 [tag:java-8] 中使用 Random#int(size,from,to) - holi-java
4
这个解决方案在JavaScript / 本地Kotlin上不起作用。也许制作一个更通用的方法会更好。 :) - Valentin Michalak
@ValentinMichalak 你可以试试这个:https://dev59.com/hFcO5IYBdhLWcg3whR7M#49507413 - s1m0nw1

38

从 Kotlin 1.2 开始,你可以这样写:

(1..3).shuffled().last()

需要注意的是它的时间复杂度为 O(n),但对于小型列表(尤其是唯一值的列表)来说还是可以接受的 :D


这个对于 Kotlin Native 很有用。 - user3471194

15

在 Kotlin SDK >=1.3 中,您可以这样做:

import kotlin.random.Random

val number = Random.nextInt(limit)

简单。不需要编写扩展功能。 - Milad Faridnia

14
你可以创建一个类似于 `java.util.Random.nextInt(int)` 的扩展函数,但是它接受一个 `IntRange` 而不是一个 `Int` 作为其边界。
fun Random.nextInt(range: IntRange): Int {
    return range.start + nextInt(range.last - range.start + 1)
}

现在你可以将这个用于任何 Random 实例:
val random = Random()
println(random.nextInt(5..9)) // prints 5, 6, 7, 8, or 9

如果你不想自己管理Random实例,那么你可以定义一个方便的方法,例如使用ThreadLocalRandom.current()
fun rand(range: IntRange): Int {
    return ThreadLocalRandom.current().nextInt(range)
}

现在你可以像在Ruby中一样获得一个随机整数,而无需先声明一个Random实例:
rand(5..9) // returns 5, 6, 7, 8, or 9

12

关于生成随机字符的另一篇回答的可能变化

为了获取随机的Char,你可以定义一个扩展函数,如下所示:

fun ClosedRange<Char>.random(): Char = 
       (Random().nextInt(endInclusive.toInt() + 1 - start.toInt()) + start.toInt()).toChar()

// will return a `Char` between A and Z (incl.)
('A'..'Z').random()

如果您使用的是 JDK >1.6,请使用 ThreadLocalRandom.current() 代替 Random()

对于 kotlinjs 和其他不允许使用 java.util.Random 的用例,这个答案 会有所帮助。

Kotlin >=1.3 多平台支持随机数生成器

从 1.3 版本开始,Kotlin 自带了自己的多平台随机数生成器。其在这篇 KEEP 中有描述。现在,您可以直接使用 Kotlin 标准库中的扩展而无需定义它:

('a'..'b').random()

7

@s1m0nw1的出色回答基础上,我做了以下更改。

  1. (0..n)在Kotlin中表示包含
  2. (0 until n)在Kotlin中表示不包含
  3. 使用单例对象作为Random实例(可选)

代码:

private object RandomRangeSingleton : Random()

fun ClosedRange<Int>.random() = RandomRangeSingleton.nextInt((endInclusive + 1) - start) + start

测试用例:
fun testRandom() {
        Assert.assertTrue(
                (0..1000).all {
                    (0..5).contains((0..5).random())
                }
        )
        Assert.assertTrue(
                (0..1000).all {
                    (0..4).contains((0 until 5).random())
                }
        )
        Assert.assertFalse(
                (0..1000).all {
                    (0..4).contains((0..5).random())
                }
        )
    }

6

在范围[1, 10]内随机生成数字的示例

val random1 = (0..10).shuffled().last()

使用Java Random或利用Java Random
val random2 = Random().nextInt(10) + 1

1
我最喜欢你的第一个答案。这是一个完美的一行代码,不需要编写任何扩展函数。 - Alex Semeniuk
@AlexSemeniuk请不要这样做。shuffled()将返回一个列表。想象一下,如果您传递(0..Integer.MAX_VALUE)会发生什么。 - deviant
@deviant 很好的观点。只是我有一个使用案例,需要从集合中获取随机元素。 - Alex Semeniuk

5

实现s1m0nw1答案的另一种方法是通过变量访问它。虽然这并没有更高效,但它可以节省您不必键入()的麻烦。

val ClosedRange<Int>.random: Int
    get() = Random().nextInt((endInclusive + 1) - start) +  start 

现在可以像这样访问它

(1..10).random

5

现在不再需要使用自定义扩展函数了。 IntRange 现在已经内置了一个random() 扩展函数。

val randomNumber = (0..10).random()

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