【问题标题】:Sorting arrays of paired numbers and removing duplicates or overlaps?对成对数字的数组进行排序并删除重复或重叠?
【发布时间】:2011-03-27 20:55:21
【问题描述】:

如何按起始坐标按数字顺序对两个坐标数组进行排序,例如

my @starts = (100,100,200,300,400,500,525);
my @ends   = (150,125,250,350,450,550,550);

但是如果在开始或结束列表中有两个匹配项,则选择最大的区别?例如

my @uniq_starts = (100,200,300,400,500);
my @unique_ends = (150,250,350,450,550);

非常感谢任何帮助!

另外,如果列表是这样的呢?

my @starts = (100,125,200,300,400,500,525);
my @ends   = (150,175,250,350,450,550,550);

这将为我提供以下值之间的值:

-25, 25, 50, 50, 50, -25

我需要以下输出:

my @uniq_starts = (100,200,300,400,500);
my @unique_ends = (175,250,350,450,550);

所以我介于两者之间的值是:

25, 50, 50, 50

我可以通过删除和忽略任何负值来解决这个问题,因为我可以想象这会使事情变得更加复杂。

【问题讨论】:

  • 请发布您迄今为止的编码尝试。
  • @unique_ends 的第一项不应该是 150 而不是 125,还是我误解了你所说的“最大差异”是什么意思?
  • 对不起,是的,应该是 150,我已经编辑了文本!
  • 实际上,为了让它更复杂......这些值可能不一定匹配,并且可能仍然存在一些重叠。例如开始 = (100,125);结束 = (150,200)。然后我需要开始 = (100);结束 = (200) :S

标签: arrays perl sorting duplicates


【解决方案1】:

Set::IntSpan怎么样?

use Set::IntSpan;

my @starts = (100,100,200,300,400,500,525);
my @ends = (150,125,250,350,450,550,550);
my @spec = map { "$starts[$_]-$ends[$_]" } 0..$#starts;
my $p = Set::IntSpan->new(@spec);
print "$p\n";

【讨论】:

  • 据我所知,如果第二个列表中有匹配对则不起作用?
  • 例如(104643,104837)(104891,104950)(104912,105195)(104964,105195)(106635,107245)(112029,112166)(113174,113177)(114546,114688)(114773,114940)(115057,115165)
  • 打印出以 105195 结尾的两个!
  • @Steve:对。遗漏了规范的一部分。完全更新我的答案。
  • +1 使用 Set::IntSpan 的好主意。您可以使用map() 更轻松地创建@spec,如下所示:my @spec = map "$starts[$_]-$ends[$_]", 0 .. $#starts
【解决方案2】:

使用Set::IntSpan

use Set::IntSpan;

my @starts = (100,100,200,300,400,500,525);
my @ends   = (150,125,250,350,450,550,550);

my (@uniq_starts, @unique_ends);

for my $s (Set::IntSpan->new([map [$starts[$_], $ends[$_]], 0 .. $#starts])->spans) {
  push @uniq_starts, $s->[0];
  push @uniq_ends, $s->[1];
}

print join(",", @uniq_starts), "\n";
print join(",", @uniq_ends), "\n";

或者穷人的解决方案:

sub spans {
  my @s = sort {$a->[0] <=> $b->[0] or $a->[1] <=> $b->[1]} @_;
  my @res;
  while (@s > 1) {
    if ($s[0][1] >= $s[1][0]) {
      splice @s, 0, 2, [$s[0][0], $s[1][1]];
    } else {
      push @res, shift @s;
    }
  }
  push @res, @s;
  return @res;
}

my @starts = (100,100,200,300,400,500,525);
my @ends   = (150,125,250,350,450,550,550);

my (@uniq_starts, @unique_ends);

for my $s (spans(map [$starts[$_], $ends[$_]], 0 .. $#starts)) {
  push @uniq_starts, $s->[0];
  push @uniq_ends, $s->[1];
}

print join(",", @uniq_starts), "\n";
print join(",", @uniq_ends), "\n";

您可以检查它是否完美无缺。

更多功能spans版本:

sub spans {
  return spans_(sort {$a->[0] <=> $b->[0] or $a->[1] <=> $b->[1]} @_);
}

sub spans_ {
  if (@_ > 1 and $_[0][1] >= $_[1][0]) {
    splice @_, 0, 2, [$_[0][0], $_[1][1]];
    goto &spans_;
  } elsif (@_) {
    return shift, spans_(@_);
  } else {
    return;
  }
}

P.S.:如果有人认为 perl 是简洁的语言,请比较相同的算法 spans erlang 中的函数。我什至不知道它在 APL 或 J 中会是什么样子:

spans(L) -> spans_(lists:sort(L)).

spans_([{A, B}, {C, D}|T]) when B >= C ->
  spans_([{A, D}|T]);
spans_([H|T]) -> [H|spans_(T)];
spans_([]) -> [].

【讨论】:

    【解决方案3】:

    使用一些列表转换:

    my @starts = (100,100,200,300,400,500,525);
    my @ends   = (150,125,250,350,450,550,550);
    
    my (%starts_seen, %ends_seen);
    my @ar = sort { $a->[0] <=> $b->[0] }   # result in ascending sort order of @starts
             grep ! $starts_seen{$_->[0]}++,
             sort { $b->[0] <=> $a->[0] }   # descending sort b -> a
             grep ! $ends_seen{$_->[1]}++,
             sort { $b->[1] <=> $b->[1] }   # descending sort b -> a
             map  [ $starts[$_],$ends[$_] ],
             0 .. $#starts;
    
    print "($_->[0],$_->[1]) " for @ar;
    

    这会导致:

    (100,150) (200,250) (300,350) (400,450) (500,550) 
    

    问候

    rbo

    编辑:修改代码以反映排序的排序顺序

    【讨论】:

    • 如果我想获得 100,150 作为第一对,那么这些对总是最大的一对呢?
    • 史蒂夫,你在排序中使用:{$a->[0] $b->[0]} 用于升序和 $b->[0] $a- >[0]} 用于降序排序(我会添加备注)
    • 它很慢而且过于复杂。为什么要排序三遍?在stackoverflow.com/questions/3449983/…比较穷人的解决方案
    • 如果你有开始 (500, 550) 和结束 (525, 560) 怎么办...我们想以 (500, 560) 结束吗?这不会那样做。
    • runrig,对,这将不会正确处理 int 跨度。问题中最初并未说明这一点-我稍后在 o.p. 的另一篇文章中读到了(跨度请求)。 -
    猜你喜欢
    • 2014-04-29
    • 2021-11-20
    • 2023-03-13
    • 1970-01-01
    • 2021-12-18
    • 2011-06-09
    • 2020-12-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多