我有一个大文件,其中每行包含由空格分隔的24
个小整数。我想为每一行找到重复的最长段,允许行到wrap around
。例如,给定行
0 10 4 2 7 9 11 8 6 5 0 10 4 2 7 11 9 3 8 3 1 1 6 5
序列6 5 0 10 4 2 7
最长;它的长度为7
,两个匹配项由10
个位置(或14
)分隔。
有人可以告诉我如何将一个脚本拼凑起来,为每一行返回最长序列的长度和两个开头之间的间隔吗?
构建文件的方式不可能重复任何一个段(即两次以上),因为从0
到11
的每个数字都被限制为完全出现两次。
非常感谢。 --Lloyd
答案 0 :(得分:0)
有很多语言可以让这比awk(包括gawk)更容易,但这里有一个全部的答案。
尝试将其放入可执行的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
}
如果我调用此awko
,则运行awko data
会以以下格式生成数据:
# of matched fields:index separation:longest match
对于输入数据是:
7:14:6 5 0 10 4 2 7
请注意,我并不打算清理匹配数据末尾的额外空间。有了更多的输入数据,如果有错误,我会有更好的想法。
我想知道我能做到这么快:
#!/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.33s的系统时间,而不是我从同一数据的第一个脚本得到的71.10s。
答案 1 :(得分:0)
这是一个相当混淆的解决方案,适用于单行输入。将整个事物包裹在一个循环中,从你的输入中读取行而不是明确地设置它,你应该有一个可行的(虽然非常缓慢和丑陋)解决方案。
#!/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