欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  后端开发

用程序生成n个随机数,要求n个数的和等于100

程序员文章站 2022-06-16 16:41:32
...
想破了头,也没想出来怎么算!!!

回复内容:

想破了头,也没想出来怎么算!!!

1, 先生成n个随机数
2, 对这n个数的和m
3, 用100除以m, 求出放大系数k
4, n个随机数分别乘以k

这个方法是有精度损失的, 我测试一般在99.9 - 100.1之间
放个python3的代码

import random

def foo(n, m):
    numbers = [random.random() for _ in range(n)]
    summation = sum(numbers)
    k = m / summation
    return [i * k for i in numbers]

if __name__ == '__main__':
    res = foo(10,100)
    print('10个数为:', res)
    print('它们的和为:', sum(res))

输出:

10个数为: [11.656631528447768, 16.926541353866945, 11.491003842424307, 15.187012385101323,  
1.4760319842835616, 8.838953893828934, 14.315979522491865, 3.882534453021053, >8.290003662873072, 
7.935307373661164]
它们的和为: 99.99999999999999

不妨换个角度看这个问题,会简单很多:把100个1随机分配给N个数。所以循环100次,每次随机选中第1到N间的某个数,给它加1。
如果要求不能有数为0,则一开始每个数初始化为1,然后只循环90次即可。

(1,100)生成第一个数n1
(1,100-n1)生成第二个n2
...
最后一个是100-(n1+n2...)

假设是n个数。
给你另一种思路,先有100这个数据池子,从里面每次随机取出一个数字,池子减少相应的数字,递归这个过程。
当需要跳出递归,最后一次的数据取出全部。
整个过程类似微信红包。唯一注意的时候,需要判断剩余的池子里能不能最少满足你的n。

唉,这个首先要看你随机数的范围,我给你个代码看看是不是你想要的

$rand_array = array();
function  get_rand_n($rand_array) {
    $rand_number = mt_rand(1,9);
    if(empty($rand_array)) {
        $rand_array[] = $rand_number;
        return get_rand_n($rand_array);
    } else {
        $count = 0;
        foreach($rand_array as $item) {
            $count += $item;
        }
        if($count<100) {
            if($count+$rand_number == 100) {
                $rand_array[] = $rand_number;
                return $rand_array;
            } else if($count+$rand_number < 100) {
                $rand_array[] = $rand_number;
                return get_rand_n($rand_array); // 回掉再次计算
            } else { // 如果得到的值大于了100
                return get_rand_n($rand_array); // 重新获得随机数,知道为100的时候返回这个随机数数组
            }
        }
    }
}
$rand_array = get_rand_n($rand_array);
var_dump($rand_array);

具体结果请自测,这个取随机数有范围的。

-我写了一个都是整数的,不知道符不符合要求。

    <?php
$max = 100; $sum = 0; $salt = $max; $num = 0; while($sum < 100){ $salt = $max - $sum; $num = rand(0,$salt); echo $num."
"; $sum += $num; } echo '和:'.$sum;
?>

我可以从另一个角度提供一点思路。 如果给你n个非整负数,要求这n个数的和为100,那么这个几个数可以取哪些值呢?假如我们有一个函数f,f返回一共有多少种解法。那么这个f可以被定义为这样: f(n, 100)。
我们接着试一下看能不能推导出"f(n)"与"f(n-1)"之间的关系呢?
其实如果我们假定最后一个数为0,那么剩下n-1个数的和必定为100。所以最后一个数为0时,解的个数应当为
f(n-1, 100),最后一个数为1时,解的个数为f(n-1, 99),最后一个数为100时,解的个数为f(n-1, 0);
那么我们就可以推导出:
f(n, 100) = f(n-1, 100) + f(n-1, 99) + ... + f(n-1, 0)
f(n-1, 100) = f(n-2, 100) + f(n-2, 99) + ... + f(n-2, 0)
...
f(2, 100) = f(1, 100) + f(1, 99) + ... + f(1, 0)
那么显然,f(1, k) = 1;而上面的表达式终归是由这些个1堆出来的。
我不熟悉php,这里我写一个cpp的demo,希望能提供些帮助:

#include#includevoid print(const std::vector& vec) {
    for (auto i : vec) {
        printf("%d ", i);
    }
    printf("\n");
}

int collect(int k, int target, std::vector& vec) {
    if (k == 1) {
        vec.push_back(target);
        print(vec);
        return 1;   
    }

    k--;
    int sum = 0;
    for (int i=0; i<= target; i++) {
        std::vectorcopy(vec);
        copy.push_back(i);
        sum += collect(k, (target - i), copy);
    }
    return sum;
}

int main() {
    std::vectorvec = std::vector();
    int result = collect(3, 5, vec);
    printf("result is %d\n", result);
    return 0;
}

以3个数加起来等于5为例(数太大膨胀的厉害)
运行结果:
g++ -std=c++11 -o test test.cpp
./test

0 0 5
0 1 4
0 2 3
0 3 2
0 4 1
0 5 0
1 0 4
1 1 3
1 2 2
1 3 1
1 4 0
2 0 3
2 1 2
2 2 1
2 3 0
3 0 2
3 1 1
3 2 0
4 0 1
4 1 0
5 0 0
result is 21


我这里使用递归的方式实现了下,不过这个方式没有考虑负数的情况,不知道符合预期不

function fn($n, $m) {
    $t = mt_rand(0, $m);

    if ($n <= 1) {
        echo $m , "\n";
        return $m;
    } else {
        echo $t. "\n";
        return fn($n-1, $m - $t);
    }
}
fn(10, 100);


谢谢大家给的参考,不能全部采纳,请见谅!
其实我刚才还有一个要求忘记了,就是必须不能让任何一个随机数有为0的情况!!!
谢谢 @sPeng 的代码!在你的代码基础上,我修改一下,虽然笨一些,但是好歹是实现了!

<?php
function foo($n ,$max = 100){
    $array = $zero = $normal = [];
    for($i=1;$i $val){
        $value = floor($val * $k); //直接保留整数,以保证下一步的和肯定<100
        if($value<1){
            $zero[] = $value;
        }else{
            $normal[] = $value;
        }
    }
    $sum = array_sum($normal);
    $diff = $max - $sum; //这个值肯定<100
    if(!empty($zero)){ //如果有为0的值
        $count = count($zero);
        foreach($zero as $z){
            $normal[] = $diff / $count;
        }
    }else{ //随机分配给一个人
        $key = array_rand($normal);
        $normal[$key] = $normal[$key]+$diff;
    }
    print_r($zero);
    print_r($normal);
    print_r(array_sum($normal));
    unset($array,$zero,$sum,$diff);
    return $normal;
}
foo(10);


没有考虑负数和小数的情况

function ret100($n){
    $s = 1;
    $ret = [];
    while($s100){
            return ret100($n);
        }
        $s++;
    }
}

//test
for($i=2;$i<=100000;$i++){
    if(array_sum(ret100(mt_rand(1,30)))!=100){
        echo 'test error: '.$i;exit;
    }
}


采用数学的方法来解决这个问题
(1)类似于快速排序的方法,一定有一个数大而一个数小
(2)通过类似于高斯当年解决1+。。。+100的想法

`#coding=utf-8
import random

def foo(n, m):
    mid = m * 2 // n
    duo = m - mid * n // 2
    numbers = [random.randint(0,mid) for _ in range(n // 2)]
    numbers1 = [mid-i for i in numbers]
    numbers.extend(numbers1)
    numbers[0] = numbers[0] + duo
    if(n%2):
        numbers.append(mid//2)
    return [i for i in numbers]

if __name__ == '__main__':
    res = foo(9,100)
    print('10个数为:', res)
    print('它们的和为:', sum(res))`

微信红包 算法

很简单,用个循环,采用概率随机数的方法
rand(1,100) 随机出来一个,然后通过减法算出下次的区间比如
rand(1,87) 一直循环,最后一直到剩下的数字作为n次的数字

来个JavaScript的:每次生成一个余下范围的

// n是个数,v是总和

function lessANumber(n, v) {
    var i,
        s = 0,
        r = [],
        x = v;

    for (i = 1; i < n; i++) {
        x = Math.random() * x;
        r.push(x);
        s += x;
        x = v - s;
    }

    r.push(x);

    return r;
}

这是到目前为止题主的说明,在题主自己回答的答案中,提供了另外一个条件,不能有数为零。
所以没有任何说明这个数是什么数,从题主的回答来看也不能是负数。那么我们记题主用来生成这种数的基本的随机数产生函数为函数fn。
1, 使用函数fn,生成N - 1个数大于0,小于100的数,如果相同则舍弃该数,再生产一个
2, 将这N - 1个函数升序排列,在首尾添加0与100,形成N + 1个数的数列A(因为数不能是负数)
3, 计算数列A相邻元素的差值并存入集合S
则集合S即为所需的数集。

  • 如果每个数有最值限制,比如大于a,小于b。已经可以转化再使用上述思路解决。

你想弄红包算法?

假定要求生成的数是整数,如果不要求整数,处理更简单些,还去掉一些限制。
代码如下:

public class Test {
    public static void main(String[] args) {
        try {
            Test.createN(1, 100);
            Test.createN(3, 100);
            Test.createN(5, 100);
            Test.createN(10, 100);
            Test.createN(99, 100);
            Test.createN(100, 100);
            Test.createN(101, 100);
        } catch (Exception ex) {
            Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    
    public static void createN(int n, int t) throws Exception {
        
        if(n>t) {
            throw new Exception("Error parameter n("+n+") > t("+t+")");
        }
        
        int total = t;
        int[] numbers = new int[n];
        
        for(int i=0; ii; k--) {
                    numbers[k] = 1;
                }
                break;
            }
        }
        numbers[n-1] = total;
        
        
        // check
        System.out.println(Arrays.toString(numbers));
        int s = 0;
        for(int i=0; i<n; i++) {
            s += numbers[i];
        }
        System.out.println("The sum is "+s);
    }
}

想了一下,我是这样实现的

function numsNone(n){
    var nums = [],
        result = 0;
    for(var i=0; i<n; i++){
        nums.push(Math.floor((Math.random() * 100)));
        result += nums[i];
    }
    if(result === 100){
        console.log(nums);
        return nums;
    }else{
        return numsNone(n);
    }
}

numsNone(3);

sPeng的答案最好,其他说每次取随机数后把随机数范围减小的算法在分布上不够随机


// 用程序生成n个随机数,要求n个数的和等于100
function ssum($num,$sum)
{
    $numArr = [];
    $top = $sum-$num+1;
    while(true){
    $tmp = rand(1,$top);
    $numArr[] = $tmp;
    $sum = $sum-$tmp;
    $top = $sum;
    $num--;
        if($num == 1){
            $numArr[] = $top;
            break;
        }
    }
    return $numArr;
}

好好捋捋逻辑。。。并不难

如果n个数都是整数就更简单了。。。

直接随机生成99个数,最后一个数用100减去前面99个数的总和

使用递归,不断改变随机数的范围。

暴力大法好,无限循环生成随机数,如果和大于100则清空记录和的变量的值,一直到有等于100的结束循环

这么简单的问题,我都不屑于解决~且看你们如何答

相关标签: php