请求shell脚本定位每行输入的最长重复段

请求shell脚本定位每行输入的最长重复段,shell,awk,sed,Shell,Awk,Sed,我有一个大文件,其中每一行由24个用空格分隔的小整数组成。我想找到,每一条线,最长的重复段,允许线环绕。例如,给定一行 0 10 4 2 7 9 11 8 6 5 0 10 4 2 7 11 9 3 8 3 1 1 6 5 序列65010427最长;它的长度为7,两个引用之间由10个位置或14个位置分隔 有人能告诉我如何拼凑一个脚本来返回每行最长序列的长度和两个开头之间的间隔吗 按照文件的构造方式,任何段都不可能重复多次,即出现两次以上,因为0到11之间的每个数字都被限制为正好出现两次 非常感

我有一个大文件,其中每一行由24个用空格分隔的小整数组成。我想找到,每一条线,最长的重复段,允许线环绕。例如,给定一行

0 10 4 2 7 9 11 8 6 5 0 10 4 2 7 11 9 3 8 3 1 1 6 5
序列65010427最长;它的长度为7,两个引用之间由10个位置或14个位置分隔

有人能告诉我如何拼凑一个脚本来返回每行最长序列的长度和两个开头之间的间隔吗

按照文件的构造方式,任何段都不可能重复多次,即出现两次以上,因为0到11之间的每个数字都被限制为正好出现两次


非常感谢-劳埃德

有很多语言可以让这比awk更容易,包括gawk,但这里有一个全awk的答案

尝试将其放入可执行的awk文件:

#!/usr/bin/awk -f

BEGIN { DELIM=":" }

function reorder(start) {
    head = ""
    tail = ""
    for( i=1;i<=NF;i++ ) {
        if( i<start ) tail = sprintf( "%s%s%s", tail, $i, FS )
        else head = sprintf( "%s%s%s", head, $i, FS )
    }

    # last field is the starting index
    return( head tail start )
}

function longest(pair) {
    split( pair, a, DELIM )
    split( a[1], one, FS )
    split( a[2], two, FS )
    long = ""
    for( i=1;i<=NF;i++ ) {
        if( one[i] != two[i] ) break
        long = sprintf( "%s%s%s", long, one[i], FS )
    }
    return( i-1 DELIM two[NF+1]-one[NF+1] DELIM long )
}

{
for( k=1;k<=NF;k++ ) {
    pairs[$k] = (pairs[$k]==""?"":pairs[$k]DELIM) reorder( k )
    }
for( p in pairs ) {
    tmp = longest( pairs[p] )
    out = tmp>out ? tmp : out
    }
print out
}
对于输入数据,其为:

7:14:6 5 0 10 4 2 7 
请注意,我没有费心清理匹配数据末尾的额外空间。有了更多的输入数据,我会更好地知道这是否有bug

我想看看我能以多快的速度做到这一点:

#!/usr/bin/awk -f

BEGIN { OFS=":" }

function longest(first, second) {
    long = ""
    s = second
    flds = 0
    for( f=first;f<=NF;f++ ) {
        if( $f != $s ) break
        long = sprintf( "%s%s%s", long, $f, " " )
        if( s==NF ) s=0
        s++
        flds++
    }
    return( flds OFS second-first OFS long )
}

{
for(k=1;k<=NF;k++) {
    val = pos[$k]
    if( val!="" ) {
        tmp = longest( val, k )
        delete pos[$k] #### need an awk/gawk that can remove elems or "delete pos" l8r
        }
    else pos[$k] = k
    out = tmp>out ? tmp : out
    }
print out
}

比第一轮快约200%。它只使用一个外部字段循环,并在使用原始解析字段找到第二个匹配编号时处理每个匹配编号。在超过2400行的数据上运行相同的数据,给了我0.33秒的系统时间,而不是我在相同数据上从第一个脚本得到的71.10秒。

这里有一个相当模糊的解决方案,可以在一行输入上工作。将整个过程包装在一个循环中,从您的输入中读取行,而不是显式地设置行,您应该有一个可行的解决方案,尽管速度非常慢而且丑陋

#!/bin/sh

input='0 10 4 2 7 9 11 8 6 5 0 10 4 2 7 11 9 3 8 3 1 1 6 5'

trap 'rm -f $TMP1 $TMP2' 0
TMP1=$(mktemp $(basename $0.XXXX))
TMP2=$(mktemp $(basename $0.XXXX))

input="$input $input"  # handle wrap-around
seq 0 11 | while read start_value; do
    echo $input | tr \  \\n | grep -w -n $start_value | sed 's/:.*//' | {
        read i
        read j
        delta=$( expr $j - $i )
        echo $input | tr \  \\n | sed -n "$i,${j}p" > $TMP1
        echo $input | tr \  \\n | sed -n "$j,\$p" > $TMP2
        diff $TMP1 $TMP2 | { IFS=a read length junk
            echo $length $delta $start_value
        }
    }
done | sort -rn | sed 1q | { read length delta start;
    printf "%s " "The sequence"
    echo $input | tr \  \\n | awk '$0==k{t=1}t' k=$start | sed "${length}q"
    echo ' is the longest sequence.' 
    /bin/echo -n The difference between starting positions is $delta '(or ' 
    expr 24 - $delta
    echo ')'
} | tr \\n ' '
echo

你试了什么?65010427最长;它的长度是6。长度是多少?此外,您提到的2个事件由10个位置分隔。你指的是12个位置,即0 10 4 2 7 11 9 3 8 3 1 1吗?需要更多的例子来更好地理解模式。对不起,杰克,当我指长度=7时,我不知怎么写了长度6。我已经改正了。10个位置或14的间隔位于图案6 5 0 10 4 2 7的两个实例的起始位置之间。当从位置8开始时,考虑到线路从位置0开始,然后再次从位置22开始时,会发生这种情况。考虑到环境因素,这两个位置相距10;绕线,即从位置22向前到位置8为10个位置。如果从位置8向前测量到位置22,该距离也是14。我不介意记录这些距离中的哪一个。-1感谢在几个人花时间按照您的原始规范回答后更改要求。谢谢!我试过了,效果很好,但不幸的是,它对我来说太慢了。我在comp.unix.shell上找到了一个更快的解决方案,在那里我发布了相同的问题,但即使这样,对于我有2600万行要读的输入大小也不起作用。我可能只需要用C写一个类似的过程。但再次感谢你的发帖。很抱歉,我没有向上投票的名声。我知道这个脚本远远没有得到优化:循环太多了。下一次,将问题的语言从拼凑脚本改为尽可能快的实现。此外,如果问题至少有10个不同的示例输入,海报将更容易生成自己的更大测试数据集以检查速度。感谢您的建议,如果我让您浪费了时间,请再次道歉。别担心,这是我的困惑时间。你可能已经有了一个足够快的答案,但是我重新努力了,我猜新剧本大概需要一个小时才能完成2600万行。谢谢你,威廉!我应该明确表示我对一个非常快速的解决方案感兴趣,因为我有一个2600万行的巨大输入文件要检查。见我对上面海报的评论。但再次感谢您的帖子——像您这样的解决方案让我想熟悉行业的所有诀窍,尽管我可能只需要在工作中每一两年做一次这样的事情。很抱歉,我没有提升投票的名声。如果你需要一个性能解决方案,你需要用一种为性能而设计的语言来写一些东西。我推荐C。
#!/bin/sh

input='0 10 4 2 7 9 11 8 6 5 0 10 4 2 7 11 9 3 8 3 1 1 6 5'

trap 'rm -f $TMP1 $TMP2' 0
TMP1=$(mktemp $(basename $0.XXXX))
TMP2=$(mktemp $(basename $0.XXXX))

input="$input $input"  # handle wrap-around
seq 0 11 | while read start_value; do
    echo $input | tr \  \\n | grep -w -n $start_value | sed 's/:.*//' | {
        read i
        read j
        delta=$( expr $j - $i )
        echo $input | tr \  \\n | sed -n "$i,${j}p" > $TMP1
        echo $input | tr \  \\n | sed -n "$j,\$p" > $TMP2
        diff $TMP1 $TMP2 | { IFS=a read length junk
            echo $length $delta $start_value
        }
    }
done | sort -rn | sed 1q | { read length delta start;
    printf "%s " "The sequence"
    echo $input | tr \  \\n | awk '$0==k{t=1}t' k=$start | sed "${length}q"
    echo ' is the longest sequence.' 
    /bin/echo -n The difference between starting positions is $delta '(or ' 
    expr 24 - $delta
    echo ')'
} | tr \\n ' '
echo