algorithm - 概率数据结构

标签 algorithm kotlin data-structures

想法是拥有一个数据结构,您只能随机访问其元素,但基于用户为每个元素定义的概率因子。因此,如果包含 100 个元素的结构产生 x 的概率为 0.5,那么理论上,如果我们尝试检索一个随机元素一百次,那么将返回 x大约\~50 次。

我找不到现成的解决方案,所以这是我的看法:

import kotlin.math.absoluteValue

/**
 *@author mhashim6 on 13/10/2019
 */
class ProbabilitySet<T>(private val items: Array<out Pair<T, Float>>) {
    private var probabilityIndices: List<Int>

    private fun calcFutureSize(count: Int, probability: Float) =
            ((count / (1f - probability)) - count).toInt().absoluteValue

    init {
        probabilityIndices = items.withIndex().flatMap { (i, item) ->
             item.act { (_, probability) ->
                calcFutureSize(items.size, probability).minus(items.size).act { delta ->
                    Iterable { ConstIterator(delta, i) }
                }
            }
        }
    }

    fun next(): T = items.random().first
}

class ConstIterator(private var size: Int, private val const: Int) : IntIterator() {

    override fun nextInt(): Int {
        size--
        return const
    }

    override fun hasNext(): Boolean = size > 0

}

fun <E> probabilitySetOf(vararg items: Pair<E, Float>) = ProbabilitySet(items)

inline fun <T, R> T.act(action: (T) -> R) = action(this)

我试图让它可变,但我遇到了很多关于时间和内存的复杂问题。所以它现在是不可变的。

这是一个可行的实现吗? 这个问题已经有实现了吗? 如何使其可变?

最佳答案

我假设如果元素概率之和不等于1,则实际元素概率必须通过将其原始概率除以所有元素概率之和来计算。例如,由 “A”到 0.1F“B”到 0.3F 组成的 ProbabilitySet 返回 “A”25% 的案例中,“B”75% 的案例中。

这是我实现的可变 ProbabilitySetaddO(1) 中运行,在 next 中运行O(logN):

class ProbabilitySet<E>(
    private val random: Random = Random.Default
) {
    private val nodes = mutableListOf<Node>()
    private var sum = 0F

    fun add(element: E, probability: Float) {
        require(probability >= 0) { "[$element]'s probability ($probability) is less than 0" }
        val oldSum = sum
        sum += probability
        nodes += Node(oldSum..sum, element)
    }

    fun isEmpty() = sum == 0F

    fun next(): E {
        if (isEmpty()) throw NoSuchElementException("ProbabilitySet is empty")
        val index = random.nextFloat() * sum
        return nodes[nodes.binarySearch {
            when {
                it.range.start > index -> 1
                it.range.endInclusive < index -> -1
                else -> 0
            }
        }].element
    }

    private inner class Node(
        val range: ClosedRange<Float>,
        val element: E
    )
}

工厂方法:

fun <E> probabilitySetOf(vararg items: Pair<E, Float>, random: Random = Random.Default) =
    ProbabilitySet<E>(random).apply {
        items.forEach { (element, probability) -> add(element, probability) }
    }

用例:

val set = probabilitySetOf("A" to 0.4F, "B" to 0.3F)
println(set.next())
set.add("C", 0.9F)
println(set.next())

关于algorithm - 概率数据结构,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58427353/

相关文章:

将数字分配给字符串的算法?

algorithm - 根据预先存在的主题自动生成摘要?

android - 燃料下载返回空字节?

arrays - 没有两个元素相邻的最大和

algorithm - 范围搜索算法,用于查询给定区域中 2D 平面中的形状

c++ - 存储分组关系和支持外观的最佳数据结构

performance - 时间复杂度和运行时间有什么区别?

algorithm - 关于不同 k-means 算法的质量

android - Recyclerview 适配器加载了数据库中对象数量的平方(例如 5 为 25,4 为 16 等)

kotlin - 基于intellij gradle的项目在bin/dir中不需要的输出