首页   注册   登录
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX 提问指南
Coding
V2EX  ›  问与答

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

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

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

    所以这个做好后还需要测试下,最好建个群分真钱测试,这样可以如实调研用户心理。
    --- 楼主建好这个群后请务必加我。我有时间帮你测试!
        28
    jiangzhuo   2017-04-26 14:23:58 +08:00
    楼上说发红包的方法,如果有人不领红包怎么能保证我最后得到 30 个数字。
        29
    crist   2017-04-26 14:42:34 +08:00
    $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);
        30
    vicalloy   2017-04-26 14:44:28 +08:00
    最主要的问题是每次分完后,剩下的前足够余下的人分。
    简单的写了一下,可能会有 bug

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

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

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

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


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

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

    每一个随机数
    ------------------- x 100.00
    所有随机数总和
        55
    qqjt   2017-04-26 20:35:45 +08:00
    钱应该最小是 0.01 , 0-100 间有 999 个取值,问题变成了 [1-999]里面随机取 30 个整数。
        56
    qqjt   2017-04-26 20:36:20 +08:00
    @qqjt 不对是取 29 个。
        57
    Actrace   2017-04-26 21:14:39 +08:00   ♥ 1
    既然入坑了,我就贴一下代码把。。
    ```````````
    <?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 元。
        58
    Actrace   2017-04-26 21:18:17 +08:00
    过大的粒度可能会让结果偏离更大,比如一个人拿了 99 块钱。。
    过小的粒度同样如此,最后一个人拿了 99 块钱。。。
        59
    mingyun   2017-04-26 23:03:43 +08:00
    可以参考微信红包算法
        60
    zhihaofans   2017-04-26 23:38:12 +08:00
    用 html+js 做了个粗暴的方法,为什么大部分都在前面呢, js 随机算法的原因?

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

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

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

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

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

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

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

    其实哪种方法都能实现目标(没有离题),看需求选择啰
    关于   ·   FAQ   ·   API   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   3256 人在线   最高记录 5043   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.3 · 37ms · UTC 10:34 · PVG 18:34 · LAX 02:34 · JFK 05:34
    ♥ Do have faith in what you're doing.