我有一个按下限排序的间隔数组(每个$ i $ $ [$ i]< = $ a [$ i + 1]),键 l 更低bound和,key h 是上限,我想删除所有行,这些行的间隔由较大的间隔包围。
$a[0] = array('l' => 123, 'h'=>241);
$a[1] = array('l' => 250, 'h'=>360);
$a[2] = array('l' => 280, 'h'=>285);
$a[3] = array('l' => 310, 'h'=>310);
$a[4] = array('l' => 390, 'h'=>400);
所以我想得到的结果是
$a[0] = array('l' => 123, 'h'=>241);
$a[1] = array('l' => 250, 'h'=>360);
$a[2] = array('l' => 390, 'h'=>400);
这就是我的尝试
function dup($a){
$c = count($a)-1;
for ($i = $c; $i > 0; $i --){
while ($a[$i]['h'] <= $a[$i-1]['h']){
unset($a[$i]);
}
}
$a = array_values($a);
}
答案 0 :(得分:1)
第一个回答的问题是由其他贡献者给出的不同变化:对于每个区间,每个区间的循环寻找更大的封闭区间。它理解和编写简单,而且确实有效。
这基本上是n2阶,这意味着对于n个间隔,我们将进行n * n次循环。可以有一些技巧来优化它:
现在让我说:
出于教育目的,我想知道我们是否可以开发一种避免n * n顺序的不同算法,随着你增加计算间隔的数量,运行时间必然会逐渐恶化。
&#34;虚拟规则&#34;算法强>
我想象这个算法我称之为&#34;虚拟规则&#34;。
我不假装这是最好的解决方案。但我们可以假设这比基本方法更快,因为尽管在循环期间要做很多测试,但这是n阶。
代码示例
我写了评论以尽可能清楚。
<?php
function removeEnclosedIntervals_VirtualRule($a, $debug = false)
{
$rule = array();
// place one point on a virtual rule for each low or up bound, refering to the interval's index in $a
// virtual rule has 2 levels because there can be more than one point for a value
foreach($a as $i => $interval)
{
$rule[$interval['l']][] = array('l', $i);
$rule[$interval['h']][] = array('h', $i);
}
// used in the foreach loop
$open = array();
$enclosed = array();
// loop through the points on the ordered virtual rule
ksort($rule);
foreach($rule as $points)
{
// Will register open intervals
// When an interval starts and ends while another was opened before and is still open, it is enclosed
// starts
foreach($points as $point)
if($point[0] == 'l')
$open[$point[1]] = $point[1]; // register it as open
// ends
foreach($points as $point)
{
if($point[0] == 'h')
{
unset($open[$point[1]]); // UNregister it as open
// was it opened after a still open interval ?
foreach($open as $i)
{
if($a[$i]['l'] < $a[$point[1]]['l'])
{
// it is enclosed.
// is it *strictly* enclosed ?
if($a[$i]['h'] > $a[$point[1]]['h'])
{
// so this interval is strictly enclosed
$enclosed[$point[1]] = $point[1];
if($debug)
echo debugPhrase(
$point[1], // $iEnclosed
$a[$point[1]]['l'], // $lEnclosed
$a[$point[1]]['h'], // $hEnclosed
$i, // $iLarger
$a[$i]['l'], // $lLarger
$a[$i]['h'] // $hLarger
);
break;
}
}
}
}
}
}
// obviously
foreach($enclosed as $i)
unset($a[$i]);
return $a;
}
?>
对基本方法进行基准测试
基本方法毫无疑问有效。比较两种方法的结果使我能够预先确定&#34; VirtualRule&#34;方法有效,因为据我测试,它返回相同的结果
// * include removeEnclosingIntervals_VirtualRule function *
// arbitrary range for intervals start and end
// Note that it could be interesting to do benchmarking with different MIN and MAX values !
define('MIN', 0);
define('MAX', 500);
// Benchmarking params
define('TEST_MAX_NUMBER', 100000);
define('TEST_BY_STEPS_OF', 100);
// from http://php.net/manual/en/function.microtime.php
// used later for benchmarking purpose
function microtime_float()
{
list($usec, $sec) = explode(" ", microtime());
return ((float)$usec + (float)$sec);
}
function debugPhrase($iEnclosed, $lEnclosed, $hEnclosed, $iLarger, $lLarger, $hLarger)
{
return '('.$iEnclosed.')['.$lEnclosed.' ; '.$hEnclosed.'] is strictly enclosed at least in ('.$iLarger.')['.$lLarger.' ; '.$hLarger.']'.PHP_EOL;
}
// 2 foreach loops solution (based on user3137702's *damn good* work ;) and currently accepted answer)
function removeEnclosedIntervals_Basic($a, $debug = false)
{
foreach ($a as $i => $valA)
{
$found = false;
foreach ($a as $j => $valB)
{
if (($valA['l'] > $valB['l']) && ($valA['h'] < $valB['h']))
{
$found = true;
if($debug)
echo debugPhrase(
$i, // $iEnclosed
$a[$i]['l'], // $lEnclosed
$a[$i]['h'], // $hEnclosed
$j, // $iLarger
$a[$j]['l'], // $lLarger
$a[$j]['h'] // $hLarger
);
break;
}
}
if (!$found)
{
$out[$i] = $valA;
}
}
return $out;
}
// runs a benchmark with $number intervals
function runTest($number)
{
// Generating a random set of intervals with values between MIN and MAX
$randomSet = array();
for($i=0; $i<$number; $i++)
// avoiding self-closing intervals
$randomSet[] = array(
'l' => ($l = mt_rand(MIN, MAX-2)),
'h' => mt_rand($l+1, MAX)
);
/* running the two methods and comparing results and execution time */
// Basic method
$start = microtime_float();
$Basic_result = removeEnclosedIntervals_Basic($randomSet);
$end = microtime_float();
$Basic_time = $end - $start;
// VirtualRule
$start = microtime_float();
$VirtualRule_result = removeEnclosedIntervals_VirtualRule($randomSet);
$end = microtime_float();
$VirtualRule_time = $end - $start;
// Basic method works for sure.
// If results are the same, comparing execution time. If not, sh*t happened !
if(md5(var_export($VirtualRule_result, true)) == md5(var_export($VirtualRule_result, true)))
echo $number.';'.$Basic_time.';'.$VirtualRule_time.PHP_EOL;
else
{
echo '/;/;/;Work harder, results are not the same ! Cant say anything !'.PHP_EOL;
stop;
}
}
// CSV header
echo 'Number of intervals;Basic method exec time (s);VirtualRule method exec time (s)'.PHP_EOL;
for($n=TEST_BY_STEPS_OF; $n<TEST_MAX_NUMBER; $n+=TEST_BY_STEPS_OF)
{
runTest($n);
flush();
}
结果(对我而言)
答案 1 :(得分:0)
一种简单的方法,可能不是你想要的,但至少应该指向正确的方向。我可以根据需要改进它,只是有点忙,并且不想让这个问题没有答案。
$out = [];
foreach ($a as $valA)
{
$found = false;
foreach ($a as $valB)
{
if (($valA['l'] > $valB['l']) && ($valA['h'] < $valB['h']))
{
$found = true;
break;
}
}
if (!$found)
{
$out[] = $valA;
}
}
这是完全未经测试的,但最终只能以$ out中的唯一(大)范围结束。我在评论中提到的重叠是未处理的。
答案 2 :(得分:0)
问题是在while循环中缺少中断
function dup($a){
$c = count($a)-1;
for ($i = $c; $i > 0; $i --){
while ($a[$i]['h'] <= $a[$i-1]['h']){
unset($a[$i]);
break; //here
}
}
$a = array_values($a);
}
答案 3 :(得分:0)
这是代码
function sort_by_low($item1,$item2){
if($item1['l'] == $item2['l'])
return 0;
return ($item1['l']>$item2['l'])? -1:1;
}
usort($a,'sort_by_low');
for($i=0; $i<count($a); $i++){
for($j=$i+1; $j<count($a);$j++){
if($a[$i][l]<=$a[$j]['l'] && $a[$i][h]>=$a[$j]['h']){
unset($a[$j]);
}
}
}
$a=array_values($a);
答案 4 :(得分:0)
这是工作代码(已测试)
CREATE TABLE test (
StartOfWeek DATETIME,
Total INT
)
INSERT INTO test
VALUES ('1/17/2016',8),
('1/24/2016',8),
('1/31/2016',10),
('2/7/2016',10),
('2/14/2016',14),
('2/21/2016',10),
('2/28/2016',10)
;WITH cte AS (
SELECT *,
ROW_NUMBER() OVER (ORDER BY StartOfWeek) Rn
FROM test
)
SELECT c1.StartOfWeek,
c1.Total,
-- incremement by 1 if Total value changes
SUM(CASE WHEN c1.Total= c2.Total THEN 0
ELSE 1 END) OVER (ORDER BY c1.Rn) AS RowNumber
FROM cte c1
LEFT JOIN cte c2 ON c1.Rn = c2.Rn + 1
Result
StartOfWeek Total RowNumber
----------------------- ----------- -----------
2016-01-17 00:00:00.000 8 1
2016-01-24 00:00:00.000 8 1
2016-01-31 00:00:00.000 10 2
2016-02-07 00:00:00.000 10 2
2016-02-14 00:00:00.000 14 3
2016-02-21 00:00:00.000 10 4
2016-02-28 00:00:00.000 10 4
$result = array();
usort($a, function ($item1, $item2) {
if ($item1['l'] == $item2['l']) return 0;
return $item1['l'] < $item2['l'] ? -1 : 1;
});
foreach ($a as $element) {
$exists = false;
foreach ($result as $r) {
if (($r['l'] < $element['l'] && $r['h'] > $element['h'])) {
$exists = true;
break;
}
}
if (!$exists) {
$result[] = $element;
}
}
将包含所需的结果