使用任何脚本语言(Perl、Python或Shell脚本)查找范围内的缺失值

7

我遇到了一个问题,需要找出一定范围内缺失的数值,而且这个范围对于连续的行也是不同的。

输入

673 673 673 676 676 680
2667 2667 2668 2670 2671 2674

输出结果应该像这样

674 675 677 678 679
2669 2672 2673

这只是其中的一部分,行值可能会更多。如果需要澄清,请让我知道。

12个回答

8

纯Bash。

使用两个子shell运行diff,然后清理结果。

diff <(cat my_range_with_holes) <(seq 1 1000) | grep '>' | cut -c 3-

6

在Python中:

def report_missing_numbers(f):
    for line in f:
        numbers = [int(n) for n in line.split()]
        all_numbers = set(range(numbers[0], numbers[-1]))
        missing = all_numbers - set(numbers)
        yield missing

注意: all_numbers 有些虚假,因为范围排除了最后一个数字,但由于该数字保证在集合中,这不影响算法的正确性。

注意: 我从原答案中删除了[-1],因为int(n)不关心尾随的'\n'


1
你的代码只从输入中读取了一行。顺便提一下,file是一个内置名称,请不要将其用作变量。 - jfs
1
是的,你可以。顺便说一下,.split()没有参数时不会在意外的双空格上断开:'1 2'.split() -> ['1', '2']但是'1 2'.split(' ') -> ['1', '', '2']int()不能用于空字符串。 - jfs
太酷了!非常感谢您的投票和关于split()的提示。在很久以前的某个时候,我曾经知道它,但我已经忘记了。 - Marcelo Cantos

5

Perl:

use Modern::Perl;

for my $line (<DATA>) {
    chomp $line;
    my @numbers     = split /\s+/, $line;
    my ($min, $max) = (sort { $a <=> $b } @numbers)[0, -1];
    my @missing     = grep { not $_ ~~ @numbers } $min .. $max;
    say join " ", @missing;
}

__DATA__
673 673 673 676 676 680
2667 2667 2668 2670 2671 2674

2

Python:

for line in open("inputfile.txt"):
    vals = set(map(int, line.split()))
    minv, maxv = min(vals), max(vals)
    missing = [str(v) for v in xrange(minv + 1, maxv) if v not in vals]
    print " ".join(missing)

这个运行得很好,非常感谢。如果可能的话,您能解释一下吗? - manu
@manu:这段代码非常线性。读取输入(跳过重复项(为什么?)),转换为整数;获取最小值和最大值;从最小值到最大值迭代,跳过输入中的值,将它们制作成字符串列表;将列表连接起来形成单个字符串。 - badp
@bp:将其转换为集合更多是为了在列表推导中进行查找,而不是为了删除重复项。我承认这并不完全一致;如果只有几个值,那么这并不重要;如果有很多值,那么人们可能会使用itertools.imap而不是map - balpha

2

使用Perl的示例代码:

#!/usr/bin/perl
use strict;
use warnings;

my @missing;

while(<DATA>) {
    my @data = split (/[ ]/, $_);
    my $i = shift @data;
    foreach (@data) {
        if ($_ != ++$i) {
               push @missing, $i .. $_ - 1;
               $i = $_;
        }
    }
}

print join " ", @missing;

__DATA__
673 673 673 676 676 680
2667 2667 2668 2670 2671 2674

输出

674 675 677 678 679 2669 2672 2673

1

Ruby:

$stdin.each_line do |line|
  numbers = line.scan(/\d+/).map(&:to_i)
  missing = (numbers.min..numbers.max).to_a - numbers
  puts missing.join " "
end

高尔夫版(79个字符):

puts $stdin.map{|l|n=l.scan(/\d+/).map(&:to_i);((n.min..n.max).to_a-n).join" "}

1

纯Bash:

while read -a line ; do
  firstvalue=${line[0]}
  lastvalue=${line[${#line[@]}-1]}
  output=()
  # prepare the output array
  for (( item=firstvalue; item<=lastvalue; item++ )); do
    output[$item]=1
  done
  # unset array elements with an index from the input set
  for item in ${line[@]}; do
    unset  "output[$item]"
  done
  # echo the remaining indices
  echo -e "${!output[@]}"
done < "$infile"

1

Perl一行命令:

perl -anE'($a,$b)=@F[0,-1];$,=" ";@h{@F}=();say grep!exists$h{$_},$a..$b'

0
a = [ 673, 673, 673, 676, 676, 680]

def woo(a):
    max_, min_ = a[0:-1]
    a = set(a)

    tot = set(list(range(min_,max_+1)))
    return list( tot - a )

你有一个列表。

集合运算符对于比较列表非常有用。在你的情况下,你想要找到所有满足以下条件的元素:

  • 在第一个值和最后一个值之间
  • 已经被传递(不连续)

集合运算符从列表中生成所有唯一的值

要选择所有在tot中但不在a中的值,只需执行tot - a。只需将输出格式化为列表即可

如果你想将a保留为列表,你需要在函数中使用copy()

a = [ 673, 673, 673, 676, 676, 680]

def woo(a):
    max_, min_ = a[0:-1]
    a = set(a.copy())

    tot = set(list(range(min_,max_+1)))
    return list( tot - a )

0

在Marcelo的解决方案基础上进行修改,以确保在出现异常时安全释放文件句柄:

with open('myfile.txt') as f:
    numbers = [int(n) for n in f.readline()[:-1].split(' ')]
all_numbers = set(range(numbers[0], numbers[-1]))
missing = all_numbers - set(numbers)

这也避免了使用内置名称file


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接