【问题标题】:Is map iteration sufficiently random for randomly selecting keys?地图迭代是否足够随机以随机选择键?
【发布时间】:2016-12-07 14:15:23
【问题描述】:

我能否依靠地图的随机迭代顺序在 Web 应用程序中实现客户端的随机“配对”?我试着环顾四周,但似乎无法找到这种随机性有多随机的细分。

算法看起来像:

var clients map[Client]struct{}

func PairClient(c Client) (Client, error) {
    for m := range clients {
        if m != c {
            return m, nil
        }
    }
    return nil, fmt.Errorf("lobby: insufficient number of clients")
}

当有 >1000 个连接的客户端时这是否足够,或者我应该维护一个单独的客户端切片并从中随机选择?

【问题讨论】:

  • 字典迭代顺序通常是插入和哈希冲突顺序的函数,所以如果你需要随机,你可能需要自己引入一些随机性。一种方法是拉出地图的键,随机打乱它们,然后使用打乱的键迭代地图。
  • 不保证任何特定的顺序,包括随机。虽然实现目前对输出进行了混洗,但它绝对不是正态分布。
  • 谢谢@JimB,我会保留一部分客户并使用它,或者找出比这更有效的解决方案。
  • @matiaselgart 当前的实现实际上明确地随机化了迭代。但这不是标准所保证的。
  • @BrettLempereur:如果比插入和删除更重要,是随机访问,也许使用不同的数据结构。一个简单的排序切片是O(1) 随机访问、O(log n) 查找和O(n) 插入,这通常很好。

标签: dictionary random go


【解决方案1】:

尽管它说是随机的(随机的)(specbloghashmap sourceanother blogSO),但分布远非完美。

为什么?因为我们喜欢地图快速,更好的随机分布往往需要更多的计算和/或更大的延迟。必须做出妥协。而且因为for range 的目的不是提供高质量的“随机播放”功能,而只是为了防止开发人员依赖稳定的迭代顺序(因为即使没有明确的随机化,它也可能发生变化)。

但是这种分布“有多好”?很容易获得“味道”。让我们创建一个包含 10 对的地图,并开始对其进行多次迭代。让我们计算第一个索引(键)的分布:

m := map[int]int{}
for i := 0; i < 10; i++ {
    m[i] = i
}

dist := make([]int, 10)
for i := 0; i < 100000; i++ {
    for idx := range m {
        dist[idx]++
        break
    }
}

fmt.Println("Distribution:", dist)

输出(在Go Playground 上试试):

Distribution: [25194 24904 6196 6134 6313 6274 6297 6189 6189 6310]

前 2 个键(01)的遇到概率大约是其余键的 4 倍,概率大致相同。

你可以说它是真实的(甚至是好的)随机性非常糟糕,但这不是重点。提供不同的迭代顺序就足够了(重要的是:它很快)。

【讨论】:

  • 是的,根据密钥的分布情况,分布会变得更糟:play.golang.org/p/2MMS3pPCMO[15597 9339 3211 3076 3153 3170 21979 3201 3119 3081 3082 3137 3114 2943 3099 3101 3151 0 0 3101 3150 3196 0]
  • 非常感谢,这真的很清楚!
【解决方案2】:

来自spec

  1. 未指定映射的迭代顺序,也不保证从一次迭代到下一次迭代顺序相同。 (...)

这实质上意味着映射的迭代顺序是未定义的。即使现在使用默认 Go 编译器是随机的,其他编译器或其他版本也可能会有所不同。

【讨论】:

    猜你喜欢
    • 2022-10-19
    • 2011-02-08
    • 2016-02-20
    • 1970-01-01
    • 2019-07-30
    • 2018-05-15
    • 2023-03-10
    • 2012-09-30
    • 1970-01-01
    相关资源
    最近更新 更多