go实战之3小时极简春节抢红包

最近开始学go, 在慕课网上趁热打铁学了下抢红包的教程:

https://www.imooc.com/learn/345 go语言第一课

https://www.imooc.com/learn/1101  3小时极简春节抢红包之Go的实战

抢红包金额设计(2个公式):
最大可调度金额 = 总金额 - 最小金额*红包数量
平均可调度金额 = 最大可调度金额/红包数量

抢红包可以如下分为4个算法。

简单随机算法(1)算法:红包序列元素 = 随机数(0~最大可用金额) + 最小金额
(2)比较:缺点是金额先大后小
后洗牌算法(1)算法:2次随机,先随机再洗牌
红包序列元素 = 随机数(0~最大可用金额) + 最小金额
对生成的序列再洗牌打乱
(2)比较
缺点:需事先生成整个序列;只适用于发红包时生成
与简单随机比较:差不多,总体慢不到万分之一
先洗牌算法(1)算法:2次随机,先洗牌后随机
生成以一个一定长度的红包种子金额序列:
a.种子金额序列长度控制在3~1/3
b.红包序列元素 = 最大可用剩余金额 / (n+1)
c.最大可用剩余金额 = 剩余金额 - 最小金额*剩余数量
再随机一个数字,取模计算索引
按索引从种子金额序列拿出一个金额作为基数
红包序列元素 = 基数作为最大数随机 + 最小金额
(2)比较:随红包数量的变大,计算性能变差,红包分布出现先大后小

2次随机算法

(改进先洗牌算法)

(1)算法:
优化上述2个缺点,改为双随机:
a.把种子金额序列的生成改成随机生成
b.随机种子的数量,范围是0~2倍剩余数量
c.剩余金额除以随机的种子数量
(2)比较:缺点是当红包金额较大且数量较小时,最后一个大金额的概率增加

2倍均值算法(1)算法:
a.每次随机金额的平均值基本相等
b.剩余金额平均数的2倍作为随机最大数
问:为何是2倍,而不是3倍?
答:每次随机金额的平均值作为中位数,随机上下范围一致,(最大值+最小值)/2 = 平均值

简单随机

 

max = amount-count*min

rand(max)+min

后洗牌

 

remain = amount

inds = make([]int64, 0)

for(i=0 ~ <count) {

    x = SimpleRand(count-i, remain)

    remain -= x

    inds = append(inds, x)

}

先洗牌算法

 

seeds[ size = 3 ~ 1/3*count ]

for(i=0 ~ <size) 

   seeds = append(seeds, max/(i+1))

idx = rand(len(seeds))

x = rand(seeds[idx]) + min

二次随机算法(改进先洗牌算法)

 

n = max/(rand(2*count)+1) + min

x = rand(n) + min

2倍均值算法

 

avg2 = 2*(max/count) + min

x = avg2 + min

 

1、简单随机算法
(1)算法:红包序列元素 = 随机数(0~最大可用金额) + 最小金额
(2)比较:缺点是金额先大后小

-----------------------------------------
package algo
import (
    "math/rand"
)
const min = int64(1)
//简单随机算法;红包的数量,红包金额;金额单位为分,1元钱=100分
func SimpleRand(count, amount int64) int64 {
    if count == 1 { return amount}
    max := amount - min*count
    x := rand.Int63n(max) + min
    return x
}
-------------------------
package main
import "xxx/infra/algo"
func main() {
    count,amount := int64(10),int64(100)
    for i := int64(0); i < count; i++ {
        x := algo.SimpleRand(count, amount*100)//单位分
        fmt.Print(float64(x)/float64(100), ",")
    }
    fmt.Println()
}

-----------------------------------------

2、后洗牌算法
(1)算法:2次随机,先随机再洗牌
红包序列元素 = 随机数(0~最大可用金额) + 最小金额
对生成的序列再洗牌打乱
(2)比较
缺点:需事先生成整个序列;只适用于发红包时生成
与简单随机比较:差不多,总体慢不到万分之一
-----------------------------------------
package algo
import "math/rand"
func AfterShuffle(count, amount int64) []int64 {
    inds := make([]int64, 0)
    //计算最大可调度金额
    max := amount - min*count
    remain := max
    //随机生成初级红包序列
    for i := int64(0); i < count; i++ {
        x := SimpleRand(count-i, remain)
        remain -= x
        inds = append(inds, x)
    }
    //洗牌,洗初级红包序列
    rand.Shuffle(len(inds), func(i, j int) {
        inds[i], inds[j] = inds[j], inds[i]
    })
    return inds
}
-------------------------
package main
import (
    "fmt"
    "imooc.com/resk/infra/algo"
)
func main() {
    fmt.Println(algo.AfterShuffle(int64(10), int64(10000)))
}
-----------------------------------------


3、先洗牌算法
(1)2次随机,先洗牌后随机
生成以一个一定长度的红包种子金额序列:
a.种子金额序列长度控制在3~1/3
b.序列元素为最大可用剩余金额/(n+1)
c.最大可用剩余金额 = 剩余金额 - 最小金额*剩余数量
再随机一个数字,取模计算索引
按索引从种子金额序列拿出一个金额作为基数
基数作为最大数随机 + 最小金额作为红包序列元素

(2)比较:随红包数量的变大,计算性能变差,红包分布出现先大后小
-----------------------------------------
package algo
import (
    "math/rand"
    "time"
)
func BeforeShuffle(count, amount int64) int64 {
    if count == 1 {return amount}
    //计算出最大可调度金额
    max := amount - min*count
    //生成红包种子金额序列
    seeds := make([]int64, 0)
    //红包种子金额序列长度=3~1/2*count
    size := count / 2
    if size < 3 {
        size = 3
    }
    for i := int64(0); i < size; i++ {
        x := max / (i + 1)
        seeds = append(seeds, x)
    }
    rand.Seed(time.Now().UnixNano())
    //从红包种子金额序列中随机选择一个作为随机基数
    idx := rand.Int63n(int64(len(seeds)))
    //使用随机基数作为最大数,随机出一个数字作为红包金额序列元素
    x := rand.Int63n(seeds[idx])
    return x + min
}

-------------------------
package main
import (
    "fmt"
    "imooc.com/resk/infra/algo"
)
func main() {
    count, amount := int64(10), int64(100)
    for i := int64(0); i < count; i++ {
        x := algo.BeforeShuffle(count, amount*100)
        fmt.Print(x, ",")
    }
    fmt.Println()
}
-----------------------------------------


4、二次随机算法(改进先洗牌算法)
(1)算法
优化上述2个缺点,改为双随机:
a.把种子金额序列的生成改成随机生成
b.随机种子的数量,范围是0~2倍剩余数量
c.剩余金额除以随机的种子数量
(2)比较:缺点是当红包金额较大且数量较小时,最后一个大金额的概率增加
-----------------------------------------
package algo
import (
    "math/rand"
    "time"
)
func DoubleRandom(count, amount int64) int64 {
    if count == 1 {return amount}
    //计算最大可调度金额
    max := amount - min*count
    rand.Seed(time.Now().UnixNano())
    //一次随机,计算出一个种子金额作为基数
    seed := rand.Int63n(count*2) + 1
    n := max/seed + min
    //二次随机,计算出红包金额序列元素
    x := rand.Int63n(n)
    return x + min
}
-------------------------
package main

import (
    "fmt"
    "imooc.com/resk/infra/algo"
)

func main() {
    count, amount := int64(10), int64(100)
    for i := int64(0); i < count; i++ {
        x := algo.DoubleRandom(count, amount*100)
        fmt.Print(x, ",")

    }
    fmt.Println()
}
-----------------------------------------

4、2倍均值算法
(1)算法:
a.每次随机金额的平均值基本相等
b.剩余金额平均数的2倍作为随机最大数
问:为何是2倍,而不是3倍?
答:每次随机金额的平均值作为中位数,随机上下范围一致,(最大值+最小值)/2 = 平均值

(2)

-----------------------------------------
package algo
import (
    "math/rand"
    "time"
)
func DoubleAverage(count, amount int64) int64 {
    if count == 1 {
        return amount
    }
    //计算出最大可用金额
    max := amount - min*count
    //计算最大可用平均值
    avg := max / count
    //二倍均值基础在加上最小金额,防止出现0值
    avg2 := 2*avg + min
    //随机红包金额序列元素,把二倍均值作为随机的最大数
    rand.Seed(time.Now().UnixNano())
    x := rand.Int63n(avg2) + min
    return x
}

-------------------------
package main
import (
    "fmt"
    "imooc.com/resk/infra/algo"
)
func main() {
    count, amount := int64(10), int64(100)
    for i := int64(0); i < count; i++ {
        x := algo.DoubleAverage(count, amount*100)
        fmt.Print(x, ",")
    }
    fmt.Println()
}
-----------------------------------------
 

 

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
提供的源码资源涵盖了Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值