crist
V2EX  ›  问与答

如何把 100 块钱随机地分成 30 份呢?

  •  
  •   crist · Apr 26, 2017 · 11457 views
    This topic created in 3477 days ago, the information mentioned may be changed or developed.
    如何把 100 块钱随机地分成 30 份,也就是每一份的钱是不固定的,但是总数加起来必须等于 100 。
    68 replies  •  2017-04-28 18:02:45 +08:00
    starvedcat
    starvedcat
    Apr 26, 2017❤️13
    1
    取 29 个(0, 100)的坐标
    xialdj
    xialdj
    Apr 26, 2017 via iPhone❤️5
    3
    随便生成 30 个随机数 按照总和与 100 的比例 所有数字等比例缩放
    crist
    crist
    OP
    Apr 26, 2017
    4
    @hinate 知乎上的回答好复杂啊;
    crist
    crist
    OP
    Apr 26, 2017
    5
    其实我是这样想的:平均分成 30 份每份 10 元,然后再从这 10 元里面随机抽取几元钱,然后再随机分配给每一份,就 OK 啦
    coderluan
    coderluan
    Apr 26, 2017
    6
    没特殊要求的话,基本怎么算都能实现吧。
    blankme
    blankme
    Apr 26, 2017 via Android
    7
    @crist 你要是只需要均匀分布,按一楼说的做就行了
    Vizogood
    Vizogood
    Apr 26, 2017 via iPhone
    8
    楼主是不是要写一个红包算法 (滑稽
    crist
    crist
    OP
    Apr 26, 2017❤️1
    9
    @Vizogood 那请问你有什么妙计没?说出来听听
    crist
    crist
    OP
    Apr 26, 2017
    10
    @crist 说错了,好像 100 分成 30 份并不是每份 10 哈应该是 3.33333333333333333333333333333 元
    tankb52
    tankb52
    Apr 26, 2017❤️1
    11
    建 30 个人的群,发 100 块钱红包啊
    ioriwong
    ioriwong
    Apr 26, 2017 via Android
    12
    用一楼 挡板法 就行了
    rogerchen
    rogerchen
    Apr 26, 2017 via Android
    13
    @crist 一楼已经给出真相了
    johnny23
    johnny23
    Apr 26, 2017 via iPhone
    14
    random100 次 每次都取模 3 结果为 0 给第一份红包加一 为 1 加二个红包 为 2 加第三个红包 不知道这样如何?
    zhangbohun
    zhangbohun
    Apr 26, 2017 via Android❤️1
    15
    随机生成三十个数,然后每个除以总和乘以 100 生成前九十九个,最后一个 100 减前面的九十九个。
    johnny23
    johnny23
    Apr 26, 2017 via iPhone
    16
    看错题目了 楼主可以无视我 哭...
    zhaojjxvi
    zhaojjxvi
    Apr 26, 2017 via iPhone
    17
    @starvedcat 说实话没太懂怎么保证各项和等于 100
    crist
    crist
    OP
    Apr 26, 2017
    18
    29 个(0, 100)的坐标怎么取,用什么语言?能否 30 份每一份都是随机数量的?
    johnny23
    johnny23
    Apr 26, 2017 via iPhone
    19
    改良哈 random ( 30 ) 70 次
    blankme
    blankme
    Apr 26, 2017 via Android❤️3
    20
    @crist 29 个(0, 100)随机数,红包金额就是相邻两点随机数之差
    johnny23
    johnny23
    Apr 26, 2017 via iPhone
    21
    重新来 先从 100 元扣出 30 分分别放在 30 个红包里面一个红包一分钱 然后做 9970 次 random(30 的运算) 随机结果是多少就给对应的红包加一分钱 比如 25 就给第 25 个红包加一分钱 直到 9970 次结束
    SingeeKing
    SingeeKing
    Apr 26, 2017
    22
    找来 29 个人建个群,发 100 元分 30 个包,大家领完记录就好了
    RqPS6rhmP3Nyn3Tm
    RqPS6rhmP3Nyn3Tm
    Apr 26, 2017 via iPhone
    23
    离散数学基础啊哥
    crist
    crist
    OP
    Apr 26, 2017
    24
    @johnny23 根据你的描述,应该随机 100 次就已经结束了,为什么还要随机 9970 次呢?除非你是想把 100 块分成 9970 份啊,这样也是 OK 的,不过太耗费资源了,分成 500 个小份再随机加到 30 个红包里面就可以了。点子不错,谢谢你~~
    libook
    libook
    Apr 26, 2017
    25
    循环随机二分?
    循环 29 次,每次把剩下的钱随机分成两份,一份分出去,另一份进入下一次循环。

    不知道在概率学上和 1 楼的区别,有大牛来讲一下吗?
    blankme
    blankme
    Apr 26, 2017❤️1
    26
    @libook 1 楼是最标准的做法,每份的期望值都是 100/30.
    你的做法得到的每份红包期望值都不一样,比如第一份是 50.
    acros
    acros
    Apr 26, 2017
    27
    困扰的主要是红包带来的潜在限制条件吧··· 分的平均了没意思,大小太极端不行。

    所以这个做好后还需要测试下,最好建个群分真钱测试,这样可以如实调研用户心理。
    --- 楼主建好这个群后请务必加我。我有时间帮你测试!
    jiangzhuo
    jiangzhuo
    Apr 26, 2017
    28
    楼上说发红包的方法,如果有人不领红包怎么能保证我最后得到 30 个数字。
    crist
    crist
    OP
    Apr 26, 2017
    29
    $total = 100;

    $pr = 1000; // 分成小份数

    $bit = $total / $pr;

    $array = [];

    for ($i=0; $i < $pr; $i++) {

    $array[rand(0,29)] += $bit;
    }

    echo "<pre>";print_r($array);
    echo "total = " . array_sum($array);
    vicalloy
    vicalloy
    Apr 26, 2017
    30
    最主要的问题是每次分完后,剩下的前足够余下的人分。
    简单的写了一下,可能会有 bug

    https://gist.github.com/vicalloy/7225cf8749e427882e0e70138353739a
    johnny23
    johnny23
    Apr 26, 2017 via iPhone
    31
    @crist 分细的原因只是钱最小单位是分....至于这个单位如何取舍 可以自己控制
    johnny23
    johnny23
    Apr 26, 2017 via iPhone
    32
    @crist 因为以前抢红包抢过 1 分钱...所以
    lygmqkl
    lygmqkl
    Apr 26, 2017
    33
    我贡献一个思路,首先要看细分的程度,如果每个人都要有,而且粒度到 1 元,那么就
    第一轮先每人给 1 块,剩下 70 元,再进入第二轮
    第二轮拿出 30 元,每人 mt_rand(0,1), 如果 0 则不分配,如果 1 则分配 1 元,余下未分配的放回奖池
    第三,第四,第五轮, repeat 第二轮

    理论上 第三轮结束,应该已经分配出去 90 元, 所以还是很快的。

    如果是要到 0.01 ,那么就以 10000 为基数,第一轮每人 1 , 剩下每轮 0-3000 也可以 0-1000 ,多跑几个循环即可。

    理论上结果应该和一楼隔板法很接近,其实我是赞同一楼的,但是这种以轮为单位的方法感觉也 ok ,并没有多跑几次,而且看起来更均匀和公平。
    ifishman
    ifishman
    Apr 26, 2017 via Android
    34
    @blankme 确定一楼的总数是 100 ?
    viator42
    viator42
    Apr 26, 2017
    35
    取一个 0 到 100 的随机数,这个数作为第一份,然后 100 减去这个数,剩下的钱再随机,29 次之后剩下的钱作为第三十份
    ifishman
    ifishman
    Apr 26, 2017 via Android
    36
    @blankme 既然楼主需要的是红包算法,那就不能采用相同期望值的算法,红包就应该是第一个人抢的时候最容易抢最多的,第二个次之……
    Domains
    Domains
    Apr 26, 2017
    37
    这不就是红包?
    理论要看实践
    到我这都 36 楼了,要不 LZ 开个群, 100 发个 30 人红包,每次统计好,发一百次
    Domains
    Domains
    Apr 26, 2017
    38
    @ifishman 你要是真抢过红包,你就知道错得过分,抢红包出现在中后段出现的金额普遍较高。另,这真有实践题,有人就专门测试过的,金额高于平均值的普遍出现于中段后
    Monad
    Monad
    Apr 26, 2017
    39
    直接插板法就好了 高中数学
    srlp
    srlp
    Apr 26, 2017
    40
    太复习了。均匀的话,一楼的足够。


    @ifishman 一楼的意思是,标记了 29 个挡板之后,每个红包的钱数是两个挡板之间的距离。加起来显然是 100 。
    Vizogood
    Vizogood
    Apr 26, 2017 via iPhone
    41
    @crist https://www.zhihu.com/question/22625187
    我没有什么好的算法 原来在知乎上看过一个类似问题
    crist
    crist
    OP
    Apr 26, 2017
    42
    29 楼我那个方法有点缺陷哈:就是结果起伏变化不是很大, 100 块钱 30 份的每一份都很少有低于 2.0 或者高于 4.0 的
    blankme
    blankme
    Apr 26, 2017
    43
    @ifishman 确定,区间多大,总数就多大,这不是显而易见的吗。。
    wbt
    wbt
    Apr 26, 2017
    44
    随机生成 30 个( 0-1 之间的)数字,然后求和计算每个数字的百分比,最后乘以 100 。
    rogerchen
    rogerchen
    Apr 26, 2017 via Android
    45
    #1 隔板法
    #14 正则化
    两个都是好方法,正则化可以完全控制分布
    slixurd
    slixurd
    Apr 26, 2017
    46
    "首先,我来讨论一下为什么要采用截尾正态分布。首先介绍一种更加直接的方法(我有一些朋友也这样猜测):如果我有 50 元,要发给 25 人。那么我用连续均匀分布随机产生 24 个位于 0 到 50 之间的数字。这 24 个数字将整个 0-50 的区间划分为 25 份,分别分给这 25 个人。但事实并不是这样的。学过序列统计的人应该知道,由于这 24 个点是连续均匀分布产生的,因此他们的序列统计量也是连续均匀分布产生的,因此他们之间的间隔的分布是指数分布的。具体证明从略,可参照 John Rice 2007 。

    作者: Mr.L
    链接: https://www.zhihu.com/question/22625187/answer/85431684
    来源:知乎
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。"
    nszm
    nszm
    Apr 26, 2017
    47
    @crist #42 有可能分不到吗....
    qxd123
    qxd123
    Apr 26, 2017
    48
    关于这个问题,如果单纯为了每个人获得的金额都相差不大的话,用一楼的确实就够用了,但如果要想跟微信红包差不多的结果,用正态分布的算法来更为合理,楼上也都给出了些方法,在保证大部分人的金额处于平均数附近,但是又有极少人的金额是相对大一点或者小一点,另外之前在网上看到过类似这种的,可以参考看看 https://segmentfault.com/q/1010000006002081
    geelaw
    geelaw
    Apr 26, 2017
    49
    你这个问题根本不是 well-asked ,都不知道你想要什么分布,怎么随机?
    di94sh
    di94sh
    Apr 26, 2017 via Android
    50
    就一楼的方法, 10000 里面生成 29 个不想等的随机数,大小排序,从零开始判断区间。/100 可以精确到分。
    fitbit
    fitbit
    Apr 26, 2017
    51
    你要的是啥分布的随机,这很重要
    wangleineo
    wangleineo
    Apr 26, 2017
    52
    实践出真知,楼主建个群发红包,看看微信红包到底什么分布。
    zjp
    zjp
    Apr 26, 2017 via Android
    53
    是我想的太简单吗😂 #15 的方法不就可以了吗
    yellowV2ex
    yellowV2ex
    Apr 26, 2017
    54
    随机 100 个数记下来,再计算他们的和,然后每一份的钱就是

    每一个随机数
    ------------------- x 100.00
    所有随机数总和
    qqjt
    qqjt
    Apr 26, 2017
    55
    钱应该最小是 0.01 , 0-100 间有 999 个取值,问题变成了 [1-999]里面随机取 30 个整数。
    qqjt
    qqjt
    Apr 26, 2017
    56
    @qqjt 不对是取 29 个。
    Actrace
    Actrace
    Apr 26, 2017❤️1
    57
    既然入坑了,我就贴一下代码把。。
    ```````````
    <?php
    $total = 10000;//总额,按分
    $max = 500;//最大可分额度
    $min = 100;//最小可分额度
    $times = 30;//分几个人
    for($i=1;$i<=$times;$i++){
    $exp = ($total-($times-$i))>$max?$max:($total-($times-$i));
    $now = rand($min, $exp);
    $total = $total - $now;
    $f1 = $now/100;
    $f2 = $total/100;
    echo "//第{$i}个人:{$f1}元,剩余{$f2}元。\n";
    }
    echo "//第 30 个人:{$f2}元。\n";
    ?>
    ````````````
    //第 1 个人:3.69 元,剩余 96.31 元。
    //第 2 个人:2.82 元,剩余 93.49 元。
    //第 3 个人:4.79 元,剩余 88.7 元。
    //第 4 个人:3.57 元,剩余 85.13 元。
    //第 5 个人:4.39 元,剩余 80.74 元。
    //第 6 个人:2.45 元,剩余 78.29 元。
    //第 7 个人:2.1 元,剩余 76.19 元。
    //第 8 个人:4.89 元,剩余 71.3 元。
    //第 9 个人:4.93 元,剩余 66.37 元。
    //第 10 个人:4.19 元,剩余 62.18 元。
    //第 11 个人:2.2 元,剩余 59.98 元。
    //第 12 个人:4.52 元,剩余 55.46 元。
    //第 13 个人:4.67 元,剩余 50.79 元。
    //第 14 个人:3.92 元,剩余 46.87 元。
    //第 15 个人:3.44 元,剩余 43.43 元。
    //第 16 个人:1.32 元,剩余 42.11 元。
    //第 17 个人:1 元,剩余 41.11 元。
    //第 18 个人:3.52 元,剩余 37.59 元。
    //第 19 个人:2.21 元,剩余 35.38 元。
    //第 20 个人:4.19 元,剩余 31.19 元。
    //第 21 个人:1.29 元,剩余 29.9 元。
    //第 22 个人:2.87 元,剩余 27.03 元。
    //第 23 个人:4.88 元,剩余 22.15 元。
    //第 24 个人:3.98 元,剩余 18.17 元。
    //第 25 个人:2.93 元,剩余 15.24 元。
    //第 26 个人:3.83 元,剩余 11.41 元。
    //第 27 个人:3.82 元,剩余 7.59 元。
    //第 28 个人:1.57 元,剩余 6.02 元。
    //第 29 个人:1.26 元,剩余 4.76 元。
    //第 30 个人:2.2 元,剩余 2.56 元。
    //第 30 个人:2.56 元。
    Actrace
    Actrace
    Apr 26, 2017
    58
    过大的粒度可能会让结果偏离更大,比如一个人拿了 99 块钱。。
    过小的粒度同样如此,最后一个人拿了 99 块钱。。。
    mingyun
    mingyun
    Apr 26, 2017
    59
    可以参考微信红包算法
    zhihaofans
    zhihaofans
    Apr 26, 2017
    60
    用 html+js 做了个粗暴的方法,为什么大部分都在前面呢, js 随机算法的原因?

    zander
    zander
    Apr 26, 2017 via iPhone
    61
    @ifishman 29 个点, 30 个线段。
    imn1
    imn1
    Apr 27, 2017
    62
    @libook
    @Actrace
    这样分,其实不太合理,因为
    理论上只有第一位有可能获得最高值 99.71 (其他都是 0.01 ),剩下的 29 位均无法获得这个最高值,概率为 0
    而且获得更大值的概率逐次降低
    所以,这样做,必须分配完成后打乱顺序,否则顺位靠前必然占优,即使是随机,概率也是占优的

    使用平均数也是不合理的,因为算出平均数,再做一次腾挪的话,基本上每份的最大值只能是 平均数*2 ,除非做多次腾挪;但这种方法,如果本意是避免落差太大的分配,是可以的
    libook
    libook
    Apr 27, 2017
    63
    @imn1 所以二分法之后再打乱顺序的话是不是就公平了?
    Actrace
    Actrace
    Apr 27, 2017
    64
    @imn1 你提到的问题好解决,如果你想要让某个人获得最高值的概率是随机的,可以把红包提前分配好,然后再将分配好的红包配额随机分发给其他人。

    理论上来说,算法本身是合理的,因为随机数函数本身就是随机的。按照微信红包的那种设定,即使第一个人领取了更多的配额,这也是随机的,不存在公平性问题讨论,因为这里的目标是“随机”,实际上第一个领红包的人也是随机的。

    设定 min 和 max 也只是为了让落差有一个可控的程度,也就是说楼主想要“公平”的话,这个是附加福利(可选项)。
    imn1
    imn1
    Apr 27, 2017
    65
    @libook
    依题目,只要符合一次整体随机就可以了,就是数额和顺序,所以递减逐次随机取数额及随机顺序应该满足
    不过我个人不太喜欢,又说不出什么道理

    个人倾向#15 的权重方法,这种方法是不理会总额的完全随机,再换算成总额 100 的占比

    #1 插板法理论上也是完全随机,但这种方法有个弊端,就是不能在相同位置插两块,所以程序是要限制的
    而#15 则没有这个问题
    imn1
    imn1
    Apr 27, 2017
    66
    @Actrace
    如上面所说,我也说不出道理
    不过抢红包有点不同,它本身就含有“抢”顺位的意味,所以顺位靠前获得更多的数额,这个概率大点是合理的
    zhangsen1992
    zhangsen1992
    Apr 27, 2017
    67
    l = [random.randint(0,100) for i in range(30)]
    map(lambda x:100*x/float(sum(l)),l)
    imn1
    imn1
    Apr 28, 2017
    68
    再补充一下:
    这个不同抽样方法之间的区别就是,每次抽取的样本是否返回样本库,是,则为排列,否,则为组合

    #1 每次取值均不能与前面相同,就是样本不返回样本库
    #15 可以多次相同值,所以是样本返回样本库,而且理论上样本库无限大,只要计算机能处理则可
    而每次扣除取出金额,递减再随机,也是不返回样本库,而且样本库的减少比#1 插板法更甚
    至于平均再腾挪,属于分段抽样法,样本库远小于前面三种

    其实哪种方法都能实现目标(没有离题),看需求选择啰
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Privacy   ·   Solana   ·   1858 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 98ms · UTC 00:00 · PVG 08:00 · LAX 16:00 · JFK 19:00
    ♥ Do have faith in what you're doing.