快捷搜索: 王者荣耀 脱发

随机、优先与权重(续)

随机、优先与权重(续)

写完上文《》后,我顺便写了一个 Python 版本的 ——毕竟我们这个人工智能组的主要编程语言是 Python。在测试的时候,我发现一个很糟糕的问题。 Damping 和 Invert 这两个主要算法,居然有缺陷,因为之前的测试逻辑不严谨,边界处理的问题没有暴露出来。 首先是以前的 damping 并不会选中第一个元素。这是一个很简单的计算问题,做一下平移就能解决

class Damping[T](val random: Random) extends Poker[T] {
          
   
  override def select(cards: Seq[T]): Option[Int] = {
          
   
    if (cards == null || cards.isEmpty) {
          
   
      return None
    }
    if (cards.size == 1) {
          
   
      return Some(0)
    }

    val range = Math.log(cards.size + 1)
    val value = Math.exp(random.nextDouble() * range)
    Some(Math.floor(value).intValue() - 1)
  }
}

不过没有第一时间发现这个问题,确实是因为测试逻辑写的不严谨。过去的测试逻辑是执行完算法逻辑后,遍历计数器字典,打印结果。但是如果某个元素一次也没有被选中过,就不会出现在字典中,正确的逻辑应该是:

it should "select more elements while more nearer front user damping" in {
          
   
    val counter: mutable.Map[Int, Int] = new mutable.TreeMap[Int, Int]()
    val croupier = Croupier.damping[Int]
    for (_ <- 0 until 100000) {
          
   
      val element = croupier.randSelect(elements)
      element should not be None
      element.foreach {
          
    value =>
        counter.put(value, counter.getOrElse(value, 0) + 1)
      }
    }
    for (num <- elements) {
          
   
      info(s"damping select $num times ${
            
     counter.getOrElse(num, 0)}")
    }
  }

当然,在scaled/rank算法中,如果某一个元素的权重是0,那么它也不会被选中,这是符合设计的。 最后,我观察了 invert 的计算结果,感觉这个曲线还是过于陡峭了,对于(我自己)常见的业务来说,是一个太过激进的做法。于是我把 invert 改成了反向的damping。 相关的内容我已经在前文中做了修改,jaskell core、jaskell dotty 和 jaskell java8的相关修改也已经发布。不过测试代码不严谨导致问题没有及时发现,这倒是值得记一笔。 顺便说一下 Python 版本 pycroupier,这个版本的实现比较简单,因为 python 是动态类型,很多在静态类型中的设计都没有意义。rank/scale合并为rank,并且rank只要求是一个函数(或者实现了 __call__(self, item)方法的对象)。同样在Scala和Java版中用来构造 croupier 的静态(object)方法,也写成了函数。 再就是draw_one 抽取一个元素,draw 则提取一组,不提供分组为(selected, rest)的操作,因为 Python 的默认 list 并不提供只读保证,Python 的使用者也习惯了利用高度的动态特性。如果确实需要,可以将原有的 list 复制之后使用,这也是我实现 select 的方法。 也因为这个原因,使用 ZipRanked 类型时要注意,此时使用的 rank 函数需要返回整数,因为 ZipRank 要构造一个键为权重的字典,如果使用浮点数 rank,那么意喻着rank值来自一个稠密的实数区间,恐怕 zip 就失去了意义。 最后,pycroupier 的安装和使用非常简单,从 pypi 就可以安装:

pip install pycroupier

导入 pycroupier 包就可以使用

In [1]: import pycroupier

In [2]: data = ["b", "e", "h", "d", "f"]

In [3]: croupier = pycroupier.damping()

In [4]: croupier.draw(data, 2)
Out[4]: [d, b]

In [5]:

上面这段代码演示了在 IPython 里执行 damping 选择的效果。 最后,如果大家有深入了解过 Python 的标准库,它提供了 shuffle、choice这样的基本操作,可以用于构造对列表的随机操作,但是这些操作都是基于平均概率的。

经验分享 程序员 微信小程序 职场和发展