Arrays 基于面积的随机
我有一个元素数组:Arrays 基于面积的随机,arrays,random,latitude-longitude,area,Arrays,Random,Latitude Longitude,Area,我有一个元素数组: $arr = array( '0' => 265000, // Area '1' => 190000, '2' => 30000, '3' => 1300 ); 我想根据面积(数组值)获得随机索引。我需要更频繁地选择具有大值的区域。 我该怎么做 我现在拥有的: $random_idx = mt_rand(0, count($arr)-1); $selected_area = (object)$arr[$random_idx];
$arr = array(
'0' => 265000, // Area
'1' => 190000,
'2' => 30000,
'3' => 1300
);
我想根据面积(数组值)获得随机索引。我需要更频繁地选择具有大值的区域。
我该怎么做
我现在拥有的:
$random_idx = mt_rand(0, count($arr)-1);
$selected_area = (object)$arr[$random_idx];
谢谢 1。重复值 假设我们有一个数组,其中每个值对应于其索引的相对概率。例如,给定一枚硬币,掷硬币的结果可能是50%的反面和50%的正面。我们可以用数组表示这些概率,比如(我将使用PHP,因为这似乎是OP使用的语言): 而掷两个骰子的结果可以表示为:
$dice = array( '2' => 1, '3' => 2, '4' => 3, '5' => 4, '6' => 5, '7' => 6,
'8' => 5, '9' => 4, '10' => 3, '11' => 2, '12' => 1
);
选择概率与这些数组的值成比例(因此与基础模型一致)的随机键(索引)的一种简单方法是创建另一个数组,其元素是原始数组的键,重复次数与值指示的次数相同,然后返回一个随机值。例如,对于dice
数组:
$arr = array( 2, 3, 3, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 6, ...
这样做,我们有信心以正确的相对概率拾取每个关键点。我们可以使用构造函数将所有逻辑封装在一个类中,该构造函数使用以下方法构建助手数组和返回随机索引的函数:
问题是OP的数组包含大的值,这导致了一个非常大的数组(但仍然可以管理,即使没有将所有值除以100)
2。步骤
一般来说,离散概率分布可能更为复杂,浮点数不易转换为重复次数
另一种解决问题的方法是将数组中的值视为划分所有可能值的全局范围的时间间隔:
+---------------------------+-----------------+-------+----+
| | | | |
|<--- 265000 --->|<-- 190000 -->|<30000>|1300|
|<------- 455000 ------>| |
|<---------- 485000 --------->| |
|<---------------- 486300 -------------->|
或者有一些示例和帮助函数的现场演示来检查密钥的概率分布
对于较大的数组,也可以考虑使用二进制搜索来查找索引。此解决方案基于元素的索引,而不是元素的值。所以我们需要对数组进行排序,以确保值越大的元素具有越大的索引 随机索引生成器现在可以表示为线性相关性
x=y
:
(y)
a i 4 +
r n 3 +
r d 2 +
a e 1 +
y x 0 +
0 1 2 3 4
r a n d o m
n u m b e r (x)
我们需要非线性生成指数(指数越大,概率越大):
要查找长度为c
的数组的x
值的范围,我们可以计算范围0..c
中所有数字的总和:
(c * (c + 1)) / 2;
要找到任何y
的x
,让我们求解二次方程
y ^ 2 + y - 2 * x = 0;
解决了这个问题,我们得到了
y = (sqrt(8 * x + 1) - 1) / 2;
现在,让我们把它们放在一起:
$c = $count($arr);
$range = ($c * ($c + 1)) / 2;
$random_x = mt_rand(0, range);
$random_idx = floor((sqrt(8 * $random_x + 1) - 1) / 2);
就性能而言,此解决方案最适合大型阵列—它不依赖于阵列大小和类型 您的数组描述了离散概率分布。每个数组值(“面积”或“权重”)与离散随机变量从数组键范围中获取特定值的概率有关
/**
*从给定的离散概率分布中抽取一个伪随机样本。
*输入数组值将被规范化,不必求和为一。
*
*@param数组$arr样本数组=>离散概率(权重)。
*@退货样品
*/
函数绘制离散样本($arr){
$rand=mt_rand(0,数组和($arr)-1);
foreach($arr as$key=>$weight){
如果($rand-=$weight)<0)返回$key;
}
}
将第一行替换为$rand=mt\u rand()/mt\u getrandmax()*array\u sum($arr)代码>如果要支持非整数权重/概率
你可能还想看看类似的问题。如果您只对一小部分已知分布的样本感兴趣,我建议您使用分析方法。这个问题与操作系统识别下一个运行线程的方式有些相似
我们的想法是根据每个区域的大小和所有这些门票的数量,为每个区域分配一些门票。根据所选的随机数,您知道哪张彩票中奖,从而知道中奖区域
首先,你需要把所有的面积加起来,然后找到一个随机数。现在,您只需遍历数组并查找第一个元素,该元素到此为止的总和大于随机数
假设您正在寻找PHP解决方案:
function get_random_index($array) {
// generate total
$total = array_sum($array);
// get a random number in the required range
$random_number = rand(0, $total-1);
// temporary sum needed to find the 'winning' area
$temp_total = 0;
// this variable helps us identify the winning area
$current_area_index = 0;
foreach ($array as $area) {
// add the area to our temporary total
$temp_total = $temp_total + $area;
// check if we already have the right ticket
if($temp_total > $random) {
return $current_area_index;
}
else {
// this area didn't win, so check the next one
$current_area_index++;
}
}
}
“基于面积”是什么意思?现在还不清楚你想做什么。这些值是随机加权的吗?这意味着您希望数组每选择1300次索引3
,就选择265000次索引0
?可能吧。感谢您的回复。如果我理解正确,您的解决方案要求我们首先找到一个描述随机数和索引之间非线性映射的函数。如何以编程方式通过插值为给定数组找到这样的函数?评估这样一个插值函数可能会破坏您的方法的性能优势,不过…总体思路是:1)定义从特定范围返回随机生成索引的特定代数函数;2) 找出函数的范围;3) 从范围中生成随机数;4) 将生成的数字传递到函数中并获取数组索引。对于此实现,使用函数y=(floor(sqrt(8*x+1)-1)/2)
。它的工作原理与在第二个图形上绘制的一样。任何其他功能都可以使用,您只需要为它找到一个正确的范围。只要把答案的最后四行抄下来就行了——它应该适合你。
a i 4 + + + + +
r n 3 + + + +
r d 2 + + +
a e 1 + +
y x 0 +
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
r a n d o m
n u m b e r
(c * (c + 1)) / 2;
y ^ 2 + y - 2 * x = 0;
y = (sqrt(8 * x + 1) - 1) / 2;
$c = $count($arr);
$range = ($c * ($c + 1)) / 2;
$random_x = mt_rand(0, range);
$random_idx = floor((sqrt(8 * $random_x + 1) - 1) / 2);
function get_random_index($array) {
// generate total
$total = array_sum($array);
// get a random number in the required range
$random_number = rand(0, $total-1);
// temporary sum needed to find the 'winning' area
$temp_total = 0;
// this variable helps us identify the winning area
$current_area_index = 0;
foreach ($array as $area) {
// add the area to our temporary total
$temp_total = $temp_total + $area;
// check if we already have the right ticket
if($temp_total > $random) {
return $current_area_index;
}
else {
// this area didn't win, so check the next one
$current_area_index++;
}
}
}