想破了头,也没想出来怎么算!!!
业精于勤,荒于嬉;行成于思,毁于随。
1, 先生成n个随机数2, 对这n个数的和m3, 用100除以m, 求出放大系数k4, 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."<br/>"; $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 <cstdio> #include <vector> void print(const std::vector<int>& vec) { for (auto i : vec) { printf("%d ", i); } printf("\n"); } int collect(int k, int target, std::vector<int>& vec) { if (k == 1) { vec.push_back(target); print(vec); return 1; } k--; int sum = 0; for (int i=0; i<= target; i++) { std::vector<int> copy(vec); copy.push_back(i); sum += collect(k, (target - i), copy); } return sum; } int main() { std::vector<int> vec = std::vector<int>(); 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<=$n;$i++){ $array[] = mt_rand(0,100); } $k = $max / array_sum($array); //求出放大系数k foreach($array as $key => $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($s<=$n){ if($s==$n){ array_push($ret,100-array_sum($ret)); }else{ array_push($ret,mt_rand(1, 100-array_sum($ret))); } if(array_sum($ret)==100){ return $ret; } if(array_sum($ret)>100){ 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, 先生成n个随机数
2, 对这n个数的和m
3, 用100除以m, 求出放大系数k
4, n个随机数分别乘以k
这个方法是有精度损失的, 我测试一般在99.9 - 100.1之间
放个python3的代码
输出:
不妨换个角度看这个问题,会简单很多:把100个1随机分配给N个数。所以循环100次,每次随机选中第1到N间的某个数,给它加1。
如果要求不能有数为0,则一开始每个数初始化为1,然后只循环90次即可。
(1,100)生成第一个数n1
(1,100-n1)生成第二个n2
...
最后一个是100-(n1+n2...)
假设是n个数。
给你另一种思路,先有100这个数据池子,从里面每次随机取出一个数字,池子减少相应的数字,递归这个过程。
当需要跳出递归,最后一次的数据取出全部。
整个过程类似微信红包。唯一注意的时候,需要判断剩余的池子里能不能最少满足你的n。
唉,这个首先要看你随机数的范围,我给你个代码看看是不是你想要的
具体结果请自测,这个取随机数有范围的。
-我写了一个都是整数的,不知道符不符合要求。
<?php
?>
我可以从另一个角度提供一点思路。 如果给你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,希望能提供些帮助:
以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
我这里使用递归的方式实现了下,不过这个方式没有考虑负数的情况,不知道符合预期不
谢谢大家给的参考,不能全部采纳,请见谅!
其实我刚才还有一个要求忘记了,就是必须不能让任何一个随机数有为0的情况!!!
谢谢 @sPeng 的代码!在你的代码基础上,我修改一下,虽然笨一些,但是好歹是实现了!
没有考虑负数和小数的情况