如何确保RNG结果的最小发生?

时间:2018-06-03 15:53:02

标签: algorithm random kotlin

我正在为This Reddit Programming Exercise开发基本密码生成器。我在Kotlin工作的密码生成非常简单,但是我很难想出一个很好的方法来保证每个字符类型的特定最小数量(即上,下,数字和特殊字符中至少有2个)。

我可以很容易地只做两个来启动密码,但我希望它们是随机分布的,而不是所有前载。我还想过将字符随机插入到字符数组中,而不是附加到字符串中,但我想知道是否有更好的方法。

以下是我目前的情况:

private fun generatePassword() {

    var password = ""

    val passwordLength = (8..25).random()

    for (i in 0..passwordLength) {
        val charType = (1..5).random()
        when (charType) {
            1 -> password += lettersLower[(0..lettersLower.size).random()]
            2 -> password += lettersUpper[(0..lettersUpper.size).random()]
            3 -> password += numbers[(0..numbers.size).random()]
            4 -> password += symbols[(0..symbols.size).random()]
        }
    }
    println(password)
}

这适用于生成随机密码为8-24个字符的随机密码,并包含大写字母,小写字母,数字和特殊字符的随机组合,但不保证是否存在任何特定数字每种类型的,如今大多数网站都需要。

我想做这样的事情:

private fun generatePassword() {

    var password: ArrayList<Char> = arrayListOf()

    var newChar:Char

    var numLow = 0
    var numUpp = 0
    var numDig = 0
    var numSpe = 0

    while (numLow < 2){
        newChar = lettersLower[(0..lettersLower.size).random()]
        password.add((0..password.size).random(), newChar)
        numLow++
    }
    while (numUpp < 2){
        newChar = lettersUpper[(0..lettersUpper.size).random()]
        password.add((0..password.size).random(), newChar)
        numUpp++
    }
    while (numDig < 2){
        newChar = numbers[(0..numbers.size).random()]
        password.add((0..password.size).random(), newChar)
        numDig++
    }
    while (numSpe < 2){
        newChar = symbols[(0..symbols.size).random()]
        password.add((0..password.size).random(), newChar)
        numSpe++
    }

    val passwordLength = (8..25).random() - password.size

    for (i in 0..passwordLength) {

        val charType = (1..5).random()
        when (charType) {
            1 -> {
                newChar = lettersLower[(0..lettersLower.size).random()]
                password.add(newChar)
            }
            2 -> {
                newChar = lettersUpper[(0..lettersUpper.size).random()]
                password.add(newChar)
            }
            3 -> {
                newChar = numbers[(0..numbers.size).random()]
                password.add(newChar)
            }
            4 -> {
                newChar = symbols[(0..symbols.size).random()]
                password.add(newChar)
            }
        }
    }
    for (i in password.indices){
        print(password[i])
    }
    print("\n")
}

但这似乎并不特别简洁。我想到的另一个选项是检查密码是否包含每种类型的字符中的两种,如果没有,则生成一个新的字符,但这看起来非常低效。有没有更好的方法来确保如果我运行我的RNG 8次以上它将至少返回,但不完全是两个,两个,两个三个,两个四肢?

这是我的随机函数,如果有帮助:

// Random function via SO user @s1m0nw1 (https://stackoverflow.com/a/49507413)
private fun ClosedRange<Int>.random() = (Math.random() * (endInclusive - start) + start).toInt()

我还发现this SO post in Java,但它也不是很简洁,我想知道是否有一个更通用,更简洁的解决方案,以确保RNG的每个可能结果的最小出现次数试验次数大于最低次数的总和。

很抱歉这个漫无边际的帖子。提前感谢您的见解。

1 个答案:

答案 0 :(得分:0)

我认为你的第二个版本是正确的。它可以简化为:

import java.util.Random

fun generatePassword() : String {
    // this defines the classes of characters
    // and their corresponding minimum occurrences
    val charClasses = arrayOf(
        ('a'..'z').asIterable().toList() to 2,
        ('A'..'Z').asIterable().toList() to 2,
        ('0'..'9').asIterable().toList() to 2,
        "!@#$%^&*()_+".asIterable().toList() to 2
    )

    val maxLen = 24;
    val minLen = 8;

    var password = StringBuilder()

    val random = Random()

    charClasses.forEach {(chars, minOccur) ->
        (0..minOccur).forEach {
            password.insert(
                if (password.isEmpty()) 0 else random.nextInt(password.length),
                chars[random.nextInt(chars.size)])
        }
    }

    ((password.length + 1)..(random.nextInt(maxLen - minLen + 1) + minLen)).forEach {
        val selected = charClasses[random.nextInt(charClasses.size)]
        password.append(selected.first[random.nextInt(selected.first.size)])
    }
    return password.toString()
}