首页 >PHP讨论组 >php - 面试问题:发一个随机红包,100块钱给10个人。每个人最多12块钱,最少6块钱。怎么分?

php - 面试问题:发一个随机红包,100块钱给10个人。每个人最多12块钱,最少6块钱。怎么分?php - 面试问题:发一个随机红包,100块钱给10个人。每个人最多12块钱,最少6块钱。怎么分?火热

以前想过一个类似问题,就是没有每个人最大、最小的得钱数的限制,以前的问题可以很好用随机数解决。

于是这个问题也被以前的思想带坑里了,把突破口完全放在了如何处理每个人的随机数上。

于是在面试时间就没有解决这个问题,直到面试结束自己安静下来,仔细想想,发现思路错了。

我认为正确的思路是:每个人先得6块钱,这样剩下40块钱,之后每次拿出一块钱,随机分配给一个人,如果某个人的钱数达到了上限,那么这个人下次就没有了再得到钱的资格了。这样直到剩下钱都分配完。

当然在接口的实际处理上可以做些优化,例如剩下的钱每次随机分配的钱可以是随机的(当然这个随机要做一些限制,以免一下就分配超额了),然后如果某个人钱+这次随机分配的钱>每个人的上限,那么他就没有资格得到这个钱了。

随机分配也好实现,先算有几个人有资格得到这笔钱,随即一个数,决定给第几个符合资格的人。

我的思路就是这样,大家如果有更好的思路,请告知。谢谢。

 
38
 
+1
-1
 
分享
全部回复 (38)我来回答
    ︶ㄣyi個┐L
    3楼
    ︶ㄣyi個┐L · 4个月前

    $money =[6,6,6,6,6,6,6,6,6,6];
    $left=40; //最低6元,所以先每个6块钱分过去,剩40了
     
    while($left!=0){
    	 for($i=0;$i<=9;$i++){
    		 $t=rand(0,(1200-$money[$i]*100))/100;//随机生成一个数,在0到自己离12多远的数之间
    		 if($t>=$left){$t=$left;}//万一生成的数超过剩下的总值,就干脆全给他好了
    		 $money[$i]=$money[$i]+$t;//加上
    		 $left=$left-$t;//看看生成数后,总仓库还有多少
    		 if ($left===0){exit ;}//仓库都没了的话就不用算了,直接结束掉
    	 }
     
     }
    print_r($money); 
    print_r(array_sum($money));

    这样的话感觉效率不会很高,但很少代码量....

    万一那个生成数一直很小,是要循环很多次......

      赞+0添加回复

    phpcn_u1749
    2楼
    phpcn_u1749 · 7个月前

    最简单示例:

    $money = '';
    while(array_sum($money)!=100) {
        for($i = 1; $i < 11; $i++) {
            $money[$i] = rand(5,11)+rand(1,99)/100;
        }
    }
    print_r($money);


      赞+1添加回复

    phpcn_u1652
    1楼
    phpcn_u1652 · 7个月前

    这是我无意间看到的:不能理解随机安全上限 

    $total=20;//红包总金额   $num=10;// 分成10个红包,支持10人随机领取   $min=0.01;//每个人最少能收到0.01元   
    for ($i=1;$i<$num;$i++)   
    {   
       $safe_total=($total-($num-$i)*$min)/($num-$i);//随机安全上限   
       $money=mt_rand($min*100,$safe_total*100)/100;   
       $total=$total-$money;  
        
       echo '第'.$i.'个红包:'.$money.' 元,余额:'.$total.' 元 ';   
    }   
    echo '第'.$num.'个红包:'.$total.' 元,余额:0 元';


      赞+0添加回复

    Peter_Zhu
    0楼
    Peter_Zhu · 8个月前

    这么多人会,就我不会

      赞+0添加回复

    PHPzhong
    0楼
    PHPzhong · 8个月前

    function markPacket($money=100,$people=10,$min=6,$max=12){
        $cash = $money-$people*$min;
        $packet_arr = [$min,$min,$min,$min,$min,$min,$min,$min,$min,$min];
        $i = 0;
        while($cash > 0){
            $people_rand = rand(0,($people-1));
            $money_rand = rand(0,($max-$min)*100)/100;
            $people_money = $packet_arr[$people_rand] + $money_rand;
            if($people_money > 12){
                continue;
            }
            if($money_rand > $cash){
                $people_money = $packet_arr[$people_rand] + $cash;
            }
            $packet_arr[$people_rand] = $people_money;
            $cash -= $money_rand;
            $i++;
        }
        var_dump($packet_arr,array_sum($packet_arr),$i);
    }

      赞+0添加回复

    怪我咯
    0楼
    怪我咯 · 8个月前

    import random
    def pocket(total=100,num=10,min=6,max=12):
        result = []
        while num > 1:
            rand_num = round(random.uniform(min,max),2)
            left_money = total - rand_num
    
            if left_money > min * (num-1) and left_money < max * (num-1):
                result.append(rand_num)
                num -= 1
                total -= rand_num
        result.append(round(total,2))
        return result
    result = pocket()
    print result
    print sum(result)

      赞+0添加回复

    伊谢尔伦
    0楼
    伊谢尔伦 · 8个月前

    楼主的方法只能处理整数问题吧,题目中并没有交代一定是整数。
    每次取随机的范围都是变化的
    下限从6和(剩余钱数-12*(剩余人数-1))中取大的
    上限从12和(剩余钱数-6*(剩余人数-1))中取小的
    贴Java代码

    public class Allocation{
        public static final double Total = 100;
        public static final double Min = 6;
        public static final double Max = 12;
        public static final int peopleNum = 10;
        private static double money_left;
    
        public static double[] allocateMoney(){
            double[] result = new double[10];
            money_left = Total;
            double lowerBound = Min;
            double upperBound = Max;
            double money_now = 0;
            double sum = 0;
            for (int i = 0; i < peopleNum ; i++) {
                lowerBound = Min > money_left - Max*(peopleNum-i-1)?Min:(money_left - Max*(peopleNum-i-1));
                upperBound = Max < money_left - Min*(peopleNum-i-1)?Max:(money_left - Min*(peopleNum-i-1));
                money_now = (upperBound - lowerBound)*Math.random()+lowerBound;
                System.out.println((i+1)+" : " + money_now);
                result[i] = money_now;
                //verify
                sum += money_now;
                money_left = money_left - money_now;
            }
            //verify
            System.out.print("Total = " + sum);
            return result;
        }
        public static void main(String[] args) {
            allocateMoney();
        }
    }
    

    某次运行截图

      赞+0添加回复

    PHP中文网
    0楼
    PHP中文网 · 8个月前

    作为热门问题 前端人员表示想过来贴个 JavaScript版

    //  100  10   max 12 min 6
    var count = 0;
    function getAmount(totalAmount,number,max,min){
        count++;
    
        var usedAmount = 0;
        var amountList = [];
    
        for(var i = 0; i< number-1; i++){
            var amount = parseFloat((Math.random()*(max-min)).toFixed(2));
    
            amountList[i] = amount;
            usedAmount += amount;
        }
    
        var balance = totalAmount - min*number - usedAmount;
    
        amountList[9] = balance;
    
        if(balance > (max-min) || balance < 0){
            getAmount(totalAmount,number,max,min);
        }else{
            usedAmount = 0;
            for(var i = 0; i< number; i++){
                redPacket[i] = (6 + amountList[i]).toFixed(2);
                usedAmount += parseFloat(redPacket[i]);
            }
            // 确认总额是否是100
            console.warn(usedAmount.toFixed(2));
            // 打印每个红包金额
            console.info(redPacket);
            console.info('计算次数:' + count);
        }
    }
    getAmount(100,10,12,6);

      赞+0添加回复

    怪我咯
    0楼
    怪我咯 · 8个月前

    答案真多,也没仔细看, 不知道有没有和我一样想法的。

    我的总体思路是这样的:先均分6块;再分析每个人得钱的随机范围;每个人依次得钱直至分完,一次就分完了。

    1、每人至少6块,所以先分出60,剩下40。那剩下人的每个人能得到的初看是[0~6];
    2、反推。从最后一个人(第10个人)推起,到他时,剩下的钱必定是<=6,继续往前推一个,到他时,剩下的钱必定是<=12,然后依次类推(最好在纸上写一下)。这么推我们可以得出到第5个人得钱时,剩下的钱必定是<=36, 那么前4个人必须要分掉至少4块钱,以此再推,前5个人必定要分掉至少10块钱,至最后一个至少得分掉34块钱。这样通过找规律我们得出了每个人得钱的随机范围的第一个值。
    3、虽然我们得出了每个人随机范围的第一个值,但并不是每一个人的随机范围的第二个值都是 6,因为当前面的几个人已经把大部分钱都分了的时候,后面的人就没得分了。这次,我们从第一个人开始往后推,当到第N个人时剩下的钱$R<6时,这个人所能分得的钱的随机范围第二个值就是剩下的钱$R,然后他后面的人所能分得的钱的随机范围的第二个值还是$R;

    说明完毕,也不知道有没有说明白,还是直接上代码吧:

        $R = 40;    //剩余钱数
        $add_max = 12 - 6;
        $arr = array(6, 6, 6, 6, 6, 6, 6, 6, 6, 6);
        for($i = 0; $i<10; $i++) {
            //默认随机数是 0~6
            $rand = mt_rand(0, $add_max);
            //反推过程可以得出从第4个人开始,rand要么是以$diff为初始值,要么就是上一行的$rand;而随机范围第二个值由剩余钱$R决定。
            if($i > 2) {
                if($R <= 0) break;
                $diff = $R - (10 - $i - 1) * $add_max;
                $rand_end = $R < 6 ? $R : $add_max;
                $diff > 0 && ($rand =  mt_rand($diff, $rand_end));
            }
    
            $arr[$i] += $rand;
            $R -= $rand;
        }
    
        var_dump($arr,array_sum($arr));

    这里得出的都是整型的,如果要得到浮点数,把rand函数替换成获得浮点数的随机函数就行。这是稍微精简后的,通俗过程:

        $R = 40;
        $add_max = 12 - 6;
        $arr = array(6, 6, 6, 6, 6, 6, 6, 6, 6, 6);
        for($i = 0; $i<10; $i++) {
            //根据反推,前三个是不用考虑随机范围的,从第4个人开始才需要考虑随机范围
            if($i < 3) {
                $rand = mt_rand(0, 6);
            } else if($i == 3) {
                //留给第5个人的不能超过36。当$rand_start不为0时,说明钱还很充足的。充足到第$i个人必须抽走一些钱才行。
                
                $rand_start = $R - 36 ? $R - 36 : 0;
                $rand = mt_rand($rand_start, 6);
            } else if($i == 4) {
                //留给第6个人的不能超过30.
                $rand_start = $R - 30 ? $R - 30 : 0;
                $rand = mt_rand($rand_start, 6);
            } else if($i == 5) {
                //留给第6个人的不能超过24.到现在为止,钱一定还是充足的,所以不用考虑第二个值。
                $rand_start = $R - 24 ? $R - 24 : 0;
                $rand = mt_rand($rand_start, 6);
            }
    
            //...
    
            else if($i == 6) {
                //通过上面得出留给下一个人的不能超过 (10-$i-1)*6;  然后到第7个人时,假设前面每个人都得到了6块,即只剩下4块钱时,随机范围第二个数就不再是6了,而是剩余数$R
                $rand_start = $R - 24 ? $R - 24 : 0;
                $rand_end = $R > 6 ? 6 : $R;
                $rand = mt_rand($rand_start, $rand_end);
            }
    
            //...
    
            $arr[$i] += $rand;
            $R -= $rand;
        } 

    困死了,不写了

      赞+0添加回复

    黄舟
    0楼
    黄舟 · 8个月前

     function rand_red($min,$max,$num,$count){
          $return=[];
          $shenyu=$count-($min*$num);//每个人分6块剩余的金额
           $rand_num=$max-$min;//最大分配
           $mt_rand_min=1;//剩余金额最小值 默认分1
          for($i=1;$i<=$num;$i++){
            $num_count=mt_rand($mt_rand_min,$rand_num);//随机分配金额 
            if($shenyu>$num_count){
           $shenyu=$shenyu-$num_count;
           $mt_rand_min=$shenyu/($num-$i);//计算剩余小值
           $red_num=$min+$num_count;//最少分配加上 max-min随机值
           $return[]=$red_num;
        }else{
          if($shenyu!==0){
          $red_num=$min+$shenyu;
          $shenyu=0;
           $return[]=$red_num;
          }else{
           $return[]=$rand_num;
        }
        }
        }
        return $return;
        }
        $arr=rand_red(6,12,10,100);
        var_dump($arr);
        var_dump(array_sum($arr));
    
    借鉴了楼主思路  英文不好 变量命名不是很标准 别喷~ 期待更好随机性解析代码

      赞+0添加回复

    巴扎黑
    0楼
    巴扎黑 · 8个月前

    如题,首先要获取大于6小于12的随机数,那么只要我们随机出0-6的随机数,并且加上6,就是符合要求的。
    然后这个是红包,按照微信红包的需求来设计,那么随机数是有两位有效小数的。那么我们需要随机出0-600的随机数,然后除以100。
    因为这种设计,所以随机出来的数值一定大于6,所以6这边边际问题,就解决了,只需要考虑12的情况。

    随机出来一个数字,只要确保后面的n位数字的平均值不大于600就可以。


    $sum = 0;                 //生成随机数的和
    $total = 4000;           //随机数总和不能大于4000
    $num = 10;                //生成num个随机数
    $factor_start = 0;             //优化算法效率,在一定情况下,提高系数,可以随机数的效率。
    $factor_end = 600;             //后面能随机的值不够时,需要控制后面随机数的范围。
    $rand_num = array();
    foreach($i==1;$i<=$num;$i++){
        if($i==10){
          $rand_num[9] = 6 + ($total - $sum)/100;
          break;
        }
        do{
            $rand = mt_rand($factor_start,$factor_end);
            $tmp_sum = $sum + $rand;
            if((($total - $tmp_sum) / ($num - $i)) < 600){
                $sum  = $tmp_sum;
                $rand_num[] = 6 + $rand / 100;
                if($total - $sum<=600){
                    $factor_end = $total - $sum;
                }
                break;
            }else{
                $factor_start += 100;
            }
        }while(true)
    }
    var_dump(shuffle($rand_num));                //重新打乱数组,并输出

    算法就是这样,结果一定是正确的。
    算法中添加的$factor_start和$factor_end变量,就是为了提高算法效率。
    假设前面的随机数都很小,那么后面的随机数就要大起来。这个时候就需要增大$factor_start,减少后面while循环次数,提高算法效率。

    假设前面的随机数特别大,让后面的数,无法满足0,到600的随机,那么就通过$factor_end来控制。

      赞+0添加回复

    大家讲道理
    0楼
    大家讲道理 · 8个月前

    相当于生成 10 个 [0, 6] 之间的随机数,并且让它们和为 40,然后再每个数加上 6 即可。

    如果用 Python,可以这样实现:

    import random
    
    r = [6] * 10
    for i in range(40):
        r[random.choice([i for i in range(10) if r[i] < 12])] += 1
    print(r)

      赞+0添加回复

    Peter_Zhu
    0楼
    Peter_Zhu · 8个月前

    数学模型为:取随机数 x < s,范围为 [a, b],另一个条件是:
    s-x:[(n-1)a, (n-1)b] ==> x: [s-(n-1)a, s-(n-1)b],
    其中 s, n, a, b 分别是题中的 金额(100),人数(10),下限(6),上限(12)
    php 没用过,用python的生成器可以简单的实现:

    import random
    
    def hongbao(s, n, a, b):
        for i in range(n-1, -1, -1):
            x = random.randint(max(a, s-b*i), min(b, s-a*i))
            s -= x
            yield x

      赞+0添加回复

    大家讲道理
    0楼
    大家讲道理 · 8个月前

    <?php
    function randMoney($totalMoney, $people, $max, $min)
    {
        if ($max * $people < $totalMoney || $min * $people > $totalMoney) {
            throw new Exception("总金钱不可以大于最大值与人数的乘积,不可以小于最小值与人数的乘积", 1);
            return [];
        }
    
        $minRest   = round(($people * $max - $totalMoney) / ($people - 1), 2) + 0.01;
        $restMoney = $totalMoney;
        $result    = [];
        $time      = 0;
        while ($restMoney) {
            for ($i = 0; $i < $people; $i++) {
                $time++;
                if ($restMoney > 0) {
                    if ($restMoney <= $min) {
                        $currenRand = $restMoney;
                    } else {
                        $currenRand = $max - $min;
                    }
                    if ($restMoney <= $minRest) {
                        $rand = $restMoney;
                    } else {
                        $rand = mt_rand(0.00, $currenRand * 100) / 100;
                    }
                    if (isset($result[$i])) {
                        if ($result[$i] + $rand <= $max) {
                            $result[$i] += $rand;
                            $restMoney -= $rand;
                        }
                    } else {
                        $result[$i] = $min + $rand;
                        $restMoney -= $result[$i];
                    }
                    if (!$restMoney) {
                        break;
                    }
                }
            }
        }
        return $result;
    }
    
    try {
        $result = randMoney(100, 10, 12, 6);
        var_dump($result);
        echo array_sum($result);
    } catch (Exception $e) {
        echo $e->getMessage();
    }

    按照平均法求出X和Y
    X+10Y = 100;
    X+Y<=12;
    故X的值为20/9
    (借鉴了上一位回答者的回答)

      赞+0添加回复

    迷茫
    0楼
    迷茫 · 8个月前

    <?php

    function hongbao($money, $people, $min, $max) {
        if($people * $min > $money || $people * $max < $money) {
            return false;
        }
        $result = [];
        for($i = 0;$i < $people; ++$i) {
            if($i == $people - 1) {
                $result[$i] = $money - array_sum($result);
                break;
            }
            $rand = mt_rand($min * 100, $max * 100)/100;
            while(!isset($result[$i])) {
                $restMoney = $money - array_sum($result) - $rand;
                if($restMoney > ($people - $i -1) * $max) {
                    $rand += 1;
                    $rand > $max && $rand = $max;
                } elseif($restMoney < ($people - $i - 1) * $min) {
                    $rand -= 1;
                } else {
                    $result[$i] = $rand;
                }
            }
        }
        return $result;
    }
    
    $result = hongbao(100, 10, 6, 12);
    print_r($result);
    print_r(array_sum($result));

    ?>
    整个算法时间复杂度比较稳定

      赞+0添加回复

    怪我咯
    0楼
    怪我咯 · 8个月前

    function fhb($money,$num,$min,$max){
        $firstUse=$num*$min;
        $surplusMoney=$money-$firstUse;
    
        $arr=array();
        for($i=1;$i<=$num;$i++){
            $arr[]=$min;
        }
    
        $diff=$surplusMoney*100;
        while($diff>0){
            $randUid = rand(0, $num - 1);
            if($arr[$randUid]<$max){
                $arr[$randUid]+=0.01;
                $arr[$randUid]=number_format($arr[$randUid],2,'.','');
                $diff--;
            }
        }
        return $arr;
    }
    
    $a=fhb(100,10,6,12);

    此算法的特征是每个人分得比较均衡。

    Array
    (
        [0] => 9.75
        [1] => 9.84
        [2] => 10.06
        [3] => 10.15
        [4] => 9.94
        [5] => 10.17
        [6] => 10.00
        [7] => 10.24
        [8] => 9.86
        [9] => 9.99
    )

      赞+0添加回复

    阿神
    0楼
    阿神 · 8个月前

    根据楼主我实现一种

    import java.util.ArrayList;
    import java.util.List;
    import java.util.Random;
    public class RandomMoney {
        public static void main(String[] args) {
            int len = 10;
            float allMoney = 100;//总钱数
            float remainMoeny = 0;//剩余得钱数
            float randomMoney = 0;//随机生成得钱数![图片描述][1]
            float sumMoney = 0;//每次随机生产钱数得总和
            int index; //随机索引
            float oneMoney;//某一次随机得钱数
            List<Object> list = new ArrayList<Object>();
            Random  random = new Random();
            for (int i = 0; i < len; i++) {
                list.add(6f);
            }
            sumMoney = sumMoney(list ,len);
            Long star = System.nanoTime();//System.currentTimeMillis();
            while ((remainMoeny = allMoney - sumMoney) > 0) {
                //产生一个剩余钱下的随机数
                index = random.nextInt(10);
                //当剩余得钱数少于一角 就把剩下得随机给某一个
                if (remainMoeny < 1f&&remainMoeny > 0) {
                    //某一个人的钱数
                    oneMoney = (float)list.get(index)+(allMoney-sumMoney);
                    if (oneMoney < 12f) {
                        list.set(index, oneMoney);
                        sumMoney = sumMoney(list, len);
                        System.out.println(list);
                        System.out.println(sumMoney);
                    }
                }else {
                    //随机生产得钱数
                    randomMoney = random.nextInt((int)remainMoeny)+random.nextFloat();
                    //某一个人得钱数
                    oneMoney = (float)list.get(index)+randomMoney;
                    if (oneMoney < 12f) {
                        list.set(index, oneMoney);
                        sumMoney = sumMoney(list , len);
                    }
                }
            }
            long end = System.nanoTime();//System.currentTimeMillis();
            System.out.println(end-star);
        }
        public static float sumMoney(List<Object> list ,int len){
            float sumMoney = 0;
            for (int i = 0; i < len; i++) {
                sumMoney += (float)list.get(i);
            }
            return sumMoney;
        }
    }

      赞+0添加回复

    伊谢尔伦
    0楼
    伊谢尔伦 · 8个月前

    100元,给10人;范围6-12元
    1,每人先发6元。 剩下每人最多还能分到6元。 剩下40元
    2,如果按照整元分; 那么等价于40元分到60个槽。。。
    3,如果要精确到分, 那么等价于40 00 分 分到 60 * 100个槽。。。

      赞+0添加回复

    Peter_Zhu
    0楼
    Peter_Zhu · 8个月前

    我从题目中获取的信息是这样的:
    1、总数是100;
    2、10个人分;
    3、最小6块;
    4、最大12块;
    5、红包分完(例如都是6块的情况不存在)。
    思路:
    先从总数中扣除最小红包,保证每个红包的最小基数,设置一个奖金池,奖金池等于总是减去保底红包;
    每个人实际领到的红包 等于 红包基数 加上 从奖金池中获取的随机奖金;

    随机奖金会判断当前奖金池数量 与 剩余人数之间的关系,决定最小奖金的大小:minSignal
    ① restNum * range <= restPool : 每个人得到最大奖金依然没有(刚好)分配完奖金:retrun range;
    ② (restNum-1) * range > restPool : 判断下一次剩余人员能否分配完奖金池,如果能,则本次随机区间在[0,range]
    ③ restNum range > restPool > (restNum-1) range : 不能,则随机区间在[restPool % range,range]

    function demo(total, min, max, num){
      var pool = total - min * num;
      var restNum = num ; // 剩余人数
      var restPool = pool; // 剩余奖金
    
      var minSignal = function(){ 
        var range = max - min; // 最大奖金
        return restNum * range > restPool ? (restNum-1) * range > restPool ? 0 : restPool % range : range ;
      };
    
      var dispatch = function (){
        var minS = minSignal();
        var temp = minS + Math.random() * ( max - min - minS);
        temp = temp > restPool ? restPool : temp; 
        restPool -= temp;
        return min + temp;
      }
    
      for(var i = 0; i < num; i++){  
        var prize = dispatch(); 
        restNum --; 
        console.log("第"+ (i+1) +"个人:" + prize ,"剩余奖金池:" + restPool);
      }
    }
    
    // 测试
    demo(100, 6 , 12, 10)
    2016-07-20 12:57:29.056 VM9998:19 第1个人:8.917007956230712 剩余奖金池:37.08299204376929
    2016-07-20 12:57:29.056 VM9998:19 第2个人:11.711160108665778 剩余奖金池:31.371831935103508
    2016-07-20 12:57:29.056 VM9998:19 第3个人:9.60431933144148 剩余奖金池:27.767512603662027
    2016-07-20 12:57:29.057 VM9998:19 第4个人:9.005495062706432 剩余奖金池:24.762017540955597
    2016-07-20 12:57:29.057 VM9998:19 第5个人:6.881776388287364 剩余奖金池:23.880241152668233
    2016-07-20 12:57:29.057 VM9998:19 第6个人:11.477396582224884 剩余奖金池:18.40284457044335
    2016-07-20 12:57:29.057 VM9998:19 第7个人:11.980543481582266 剩余奖金池:12.422301088861083
    2016-07-20 12:57:29.057 VM9998:19 第8个人:10.577151778799255 剩余奖金池:7.845149310061829
    2016-07-20 12:57:29.057 VM9998:19 第9个人:10.915993913333269 剩余奖金池:2.92915539672856
    2016-07-20 12:57:29.057 VM9998:19 第10个人:8.92915539672856 剩余奖金池:0

      赞+0添加回复

    猪哥
    0楼
    猪哥 · 8个月前

    这个问题很简单,100块给10个人,那么平均数就是10,先随机一个6到12的数,如果小于10,那么剩下的钱除以9肯定平均数大于10,那就在10到12随机一个数,然后把剩下的钱除以8,如果平均数大于10继续在10到12里面随机,如果小于10,那么就在6到10之间随机,由此类推得到10个随机数。然后把这10个随机数打乱分给10个人。

      赞+0添加回复

    猪哥
    0楼
    猪哥 · 8个月前

    function microtime_float(){
        list($usec, $sec) = explode(" ", microtime());
        return ((float)$usec + (float)$sec);
    }
    
    function getRandParcent(){
        return rand(1,10)/rand(10,100);  
    }
    
    
    function randUserMoney($cash,$min=6,$max=12){
        $cash_ini = $cash;
        $user_arr = array($min,$min,$min,$min,$min,$min,$min,$min,$min,$min);
        $start = microtime_float();
        while($cash>0){
            $user_id = rand(0, 9);
            $rand_point = getRandParcent();
            if($user_arr[$user_id]<$max){
                $ing = microtime_float();
                if($ing-$start>0.01){
                    return randUserMoney($cash_ini);
                }
                $rand_money = round($rand_point*$cash,2);
                $user_money = $user_arr[$user_id]+$rand_money ;
                if($user_money<$max){
                    $user_arr[$user_id] = $user_money;
                    $cash = $cash - $rand_money;
                }
            }
        }
        return [
        'user_money'=>$user_arr,
        'total_money'=>array_sum($user_arr),
        'excute_time'=>$ing-$start
        ];
    }
    
    var_dump(randUserMoney(40));
    
    array (size=3)
      'user_money' => 
        array (size=10)
          0 => float 11.59
          1 => float 9.07
          2 => float 11.99
          3 => float 12
          4 => float 9.14
          5 => float 11.6
          6 => float 11.86
          7 => float 9.93
          8 => float 6
          9 => float 6.82
      'total_money' => float 100
      'excute_time' => float 0.004000186920166

      赞+0添加回复

    伊谢尔伦
    0楼
    伊谢尔伦 · 8个月前

    function roundmoney()
        {
            $cash=100;
        
            for ($i = 0; $i < 10; $i++) {
                $people[$i]=rand(6,12);
                $cash=$cash-$people[$i];
                if($cash==0&&$i==9)
                {
                    return $people;
                }
            }
            return false;
        
        }
        while(true)
        {
            if(($res=roundmoney())!=false)
            {
                var_dump($res);
                break;
            }
        
        }

    感觉有点粗暴,就是完全让计算机随机分,什么时候刚好分够10个人并且每个人不少于6不大于10 就停止

      赞+0添加回复

    怪我咯
    0楼
    怪我咯 · 8个月前

    
    static void Main(string[] args)
            {
                int all = 100;//总金额
                int person = 10;//人数
                double min = 8;//最小金额
                double max = 12;//最大金额
    
                double[] array = new double[person];//分配结果集
                int i = 0;//第几人
                Random ran = new Random();
    
                //默认分配最小值
                for (i = 0; i < person; i++)
                {
                    array[i] = min;
                }
    
                double yet = min * person;//已分配金额
    
                int whileTimes = 0;//记录循环次数
                while (yet < all)
                {
                    double thisM = Math.Round((ran.NextDouble() * (max - min - 1)), 2);
                    i = ran.Next(0, person);
                    if (yet + thisM > all)
                    {
                        thisM = all - yet;
                    }
    
                    if (array[i] + thisM < max)//判断是否超出最大金额
                    {
                        array[i] += thisM;
                        yet += thisM;
                    }
                    whileTimes++;
                }
    
    
                Console.Write("共循环{0}次\r\n", person + whileTimes);
                yet = 0;
                for (i = 0; i < person; i++)
                {
                    yet += array[i];
                    Console.Write("第{0}人=>分配{1}元,合计分配{2}元\r\n", i + 1, array[i], yet);
                }
                Console.Read();
    
            }
    

      赞+0添加回复

    阿神
    0楼
    阿神 · 8个月前

    根据题主的思路写了这样的一个答案

    function faHongBao(money, pe, min, max) {
        var sum = money,
            result = [];
    
        for(var i=0; i<pe; i++) {
            result.push(min);
            sum -= min;
        }
    
        while(sum > 0) {
            var ran = Math.floor( Math.random() * (pe - 1) );
            if(result[ran] < max) {
                result[ran]++;
                sum--;
            }
        }
    
        return result;
    }

      赞+0添加回复

    猪哥
    0楼
    猪哥 · 8个月前

    半夜看到这个问题,忽然想到二分法那种模式,于是搞了一个解决方案,先讲思路。
    1.先将人分为两半,左半人分的总金额最少是左半人数x最少金额总金额-右半人数x最多金额两个数字中较大的;
    2.左半人分的总金额最多是左半人数x最多金额总金额-右半人数x最少金额两个数字中较小的;
    3.在这个范围内取随机数作为分给左半人的金额,然后将问题递归为左半人分钱和右半人分钱。
    py代码(直接以分为单位,数字都是整数):

    import random
    
    def deliver(sum_of_money,num_of_people,min_of_money,max_of_money):
        if num_of_people==1:
            arr = [sum_of_money]
            return arr
        else:
            half = num_of_people>>1
            border_left = max(min_of_money*half,(sum_of_money-(num_of_people-half)*max_of_money))
            border_right = min(max_of_money*half,(sum_of_money-(num_of_people-half)*min_of_money))
            sum_of_left = random.randint(border_left,border_right)
            arr_left = deliver(sum_of_left,half,min_of_money,max_of_money)
            arr_right = deliver(sum_of_money-sum_of_left,num_of_people-half,min_of_money,max_of_money)
            return arr_left+arr_right
            
    list = deliver(10000,10,600,1200)
    print list

      赞+0添加回复

    阿神
    0楼
    阿神 · 8个月前

    min = 6;
    max = 12;
    total = 100;
    pe = 10;
    maxTotal = max*pe - total;
    
    list = array();
    for (0<pe)
        if(maxTotal>0)
            temp = random(0,maxTotal>=6?6:maxTotal);
            maxTotal-=temp;
            list.push(12-temp);
            
    if(maxTotal>0)
        pre = maxTotal/pe;
        for (0<pe)list[temp2]+=pre;
     
    shuffle(list);
    

    此方法是错误的,没有强迫症,不修改了

      赞+0添加回复

    猪哥
    0楼
    猪哥 · 8个月前

     <?php
        // 1.初始化,平均分配每人$initAvgMoney(根据限制条件随机产生)
        // 2.每人随机拿出一定金额到共享资金池中,进行重新分配
        // 限制条件:$initAvgMoney应满足条件:"小宝"一分钱也不放入共享资金池("特自私"),其余九人拿出尽可能多的钱到共享资金池(每人只留6元)的情况下,共享资金池平均分配后小宝的钱也不超过12块
    
        header("Content-type:text/html;charset=utf-8");
        $minInitAvgMoney = 600;
        // ($maxInitAvgMoney - 600) * 9 / 10 + $maxInitAvgMoney <= 1200;
        $maxInitAvgMoney = floor(1740 / 1.9);
        echo("maxInitAvgMoney:");var_dump($maxInitAvgMoney);
        
        $initAvgMoney = mt_rand($minInitAvgMoney, $maxInitAvgMoney) ;
        echo("initAvgMoney:");var_dump($initAvgMoney);
        
        $maxMinus = $initAvgMoney - 600;
        echo("maxMinus:");var_dump($maxMinus);
        
        
        $moneyArr = array();
        for($i = 0; $i < 10; $i ++){
            $randMinusArr[$i] = mt_rand(0, $maxMinus / 10) * 10;
            // echo("randMinusArr-{$i}");var_dump($randMinusArr[$i]);
            $moneyArr[$i] = $initAvgMoney - $randMinusArr[$i];
        }
        
        
        $randMinusSum = 10000 - $initAvgMoney * 10 + array_sum($randMinusArr);
        
        $avgAddMoney = $randMinusSum / 10;
        
        for($i = 0; $i < 10; $i ++){
            $moneyArr[$i] = ($moneyArr[$i] + $avgAddMoney) / 100;
        }
        
        echo "最终结果:";var_dump($moneyArr);
        echo "结果验证:";var_dump(array_sum($moneyArr));
        // 感觉还有可以完善的地方,先这样吧
    

      赞+0添加回复

    ringa_lee
    0楼
    ringa_lee · 8个月前

    先每个人分6分,然后把剩下的钱再分
    这个时候取10个随机值(0-9)随意,然后取各个值在随机值总和的百分比,分别乘以剩下的10000-60;Math.ceil或者Math.floor都可以
    最后一个值防止取ceil时溢出,直接用10000-60-前面九个数的和即可
    然后分别加上6,换算即可

      赞+0添加回复

    小葫芦
    0楼
    小葫芦 · 8个月前

    $arr = [];
    for ($i=0; $i < 9; $i++) { 
        $arr[] = mt_rand(6,12);
    }
    array_push($arr, 100 - array_sum($arr));
    var_dump($arr);
    var_dump(array_sum($arr));

      赞+0添加回复

    小葫芦
    0楼
    小葫芦 · 8个月前

    你的想法基本靠谱,我认为可以这样分:
    1.先每人分6元,还剩下40元。
    2.起一个循环,每人分0-(12-他已有的钱)元随机,直到钱分完为止。
    没有题主讲的这么麻烦的。

      赞+0添加回复

    首页上一页12下一页尾页