Вот, что получилось, сравнивая соседние диапозоны:
<?php
// input string
$str = '1-5,10,7-8,20-30,11,15-18';
$out = '';
$prev = 0;
$start = 1;
$end = 40;
$sep = "\n";
$timer = array_sum(explode(' ', microtime()));
$arr = explode(',', $str);
natsort($arr);
foreach ($arr as $v)
{
if (!empty($prev))
{
$prev_range = explode('-', $prev);
$range = explode('-', $v);
$from = isset($prev_range[1]) ? $prev_range[1] : $prev_range[0];
$to = $range[0];
if ($to>$from+1)
{
$out.= implode($sep, range($from+1, $to-1));
$out.= $sep;
}
}
else // first
{
$range = explode('-', $v);
if ($range[0]>1)
{
$out.= implode($sep, range(1, $range[0]));
}
}
$prev = $v;
}
$last = array_pop($arr);
$range = explode('-', $last);
$from = isset($range[1]) ? $range[1] : $range[0];
if ($from<$end)
{
$out.= implode($sep, range($from+1, $end));
}
echo $out.'<br>';
echo "timer: ".round(array_sum(explode(' ', microtime()))-$timer, 4);
function p_r($arr)
{
echo '<pre>'.print_r($arr, true).'</pre>';
}
?>
6 9 12 13 14 19 31 32 33 34 35 36 37 38 39 40
timer: 0.0003
Можно еще быстрее? :)
Если последовательность будет достаточно большая.