怪物掉落金币随想

越雨泽
2023-12-01

想必大家在过年的时候玩微信抢红包非常高兴,今天项目中碰到一个问题:

总共有2000金币,需要随机掉落到N个怪物身上,每只怪掉率的金币不的少于 2000/(2 * N), N 可以被2000 整除。

遇到这个问题后,我想到了过年时大家都玩的很高兴的微信红包,当时我想到了微信红包这种随机算法是如何实现的,其中有没有顺序漏洞存在,这样通过领取的时机让自己利益最大化。因为很多微信红包在群里发的时候,都是整个群所有成员全体发,这样的话每个人都会都到一个红包,是不是我最后一个领,得到的最多?后来这个问题就搁浅了,没在去想,这两天项目中遇到了同样的问题,大概思考了一下,写出了游戏中怪随机掉落的算法。

def get_reward_arr(coin_sum, n):
    det_coin = coin_sum / n
    coin_arr = [det_coin for i in range(n)]
    max_lose_coin = det_coin / 2 - 1

    for i in range(n):
        lose_coin = random.randint(0, max_lose_coin)
        win_index = random.randint(0, n - 1)
        coin_arr[i] -= lose_coin
        coin_arr[win_index] += lose_coin

    return coin_arr

程序首先将金币分成N等份,然后从每份中拿出最多一半的金币,在随机出一个获得金币的index,将此金币加到获得金币的index。

上面是我想到的一种方式,还有一种常见的方式:

def get_reward_arr(coin_sum, n):
    rand_arr = [random.randint(n / 2, n) for i in range(n)]
    rand_sum = reduce(lambda x, y: x + y, rand_arr)

    coin_arr = [(rand_arr[i] * coin_sum) / rand_sum for i in range(n)]

    return coin_arr

这种方式是先随机出一组数组,然后每次通过随机出占用比来得到获得的金币数。

结果有一天看到一篇博客里有谈到微信红包的实现方式,为了节省空间,他们利用了随机种子一定,则随机数就一定的原理,每次只记录总钱数,第几次获取,随机种子,总共分多少人。数据结构大概是:money, n, times, seed。这里不讨论具体微信如何处理浮点数,如何处理锁的问题,只探讨随机实现方式:

def get_reward_coin(coin_sum, n, seed, times):
    random.seed(seed)
    rand_num = 0
    rand_sum = 0

    for i in range(n):
        ret = random.randint(n / 2, n)
        rand_sum += ret
        if i == times:
            rand_num = ret

    return (rand_num * coin_sum) / rand_sum

通过cpu的时间来节省内存空间,非常聪明的做法。

上面的算法并没有考虑浮点数的问题,如果项目中涉及到浮点数问题,对于不同的项目有不同的解决方案。

所有的测试代码可以通过访问这里。参考到的文章可以访问后端技术 by Tim Yang

 类似资料: