grep -n | sort | sed | cut
( export LC_ALL=C
grep -n '' | sort -t: -nmk1,1 ./L - |
sed /:/d\;n | cut -sd: -f2-
) <./F
ที่ควรจะทำงานได้อย่างรวดเร็ว(การทดสอบตามกำหนดเวลาบางส่วนจะรวมอยู่ด้านล่าง)พร้อมอินพุตทุกขนาด หมายเหตุบางประการเกี่ยวกับวิธีการ:
export LC_ALL=C
- เนื่องจากจุดของการดำเนินการต่อไปนี้คือการทำให้ไฟล์ทั้งหมดของ
./F
กองซ้อนในแนวเดียวกันกับ./L
ไฟล์ lineno ของมันตัวละครเดียวที่เราต้องกังวลคือ[0-9]
ตัวเลขASCII และ:
โคลอน
- ด้วยเหตุนี้จึงง่ายกว่าที่คุณจะกังวลเกี่ยวกับการค้นหาตัวละครทั้ง 11 ตัวในกลุ่มของ 128 สิ่งของมากกว่าที่เป็นถ้ามีส่วนเกี่ยวข้องกับ UTF-8
grep -n ''
- แทรกนี้สตริง
LINENO:
เข้ามาในหัวของสายในทุก stdin - <./F
หรือ
sort -t: -nmk1,1 ./L -
sort
ละเลยที่จะเรียงลำดับไฟล์อินพุตเลยและแทน(อย่างถูกต้อง)ทึกทักเอาว่าพวกมันถูกจัดเรียง-m
ไว้แล้วและ-numerically
เรียงตามลำดับโดยไม่สนใจสิ่งใด ๆ นอกเหนือจากตัวอักษรโคลอนที่-k1,1
เกิดขึ้น-t:
- ในขณะนี้อาจต้องใช้พื้นที่ชั่วคราวในการทำ(ขึ้นอยู่กับว่าบางลำดับอาจเกิดขึ้นห่างกัน)มันจะไม่ต้องการมากเมื่อเทียบกับการเรียงลำดับที่เหมาะสมและมันจะเร็วมากเพราะมันเกี่ยวข้องกับการย้อนรอยเป็นศูนย์
sort
จะส่งกระแสข้อมูลเดียวที่มีผ้าปูที่นอนในใด ๆ./L
จะนำหน้าบรรทัดที่เกี่ยวข้อง./F
ทันที ./L
เส้นของต้องมาก่อนเสมอเพราะเส้นสั้นกว่า
sed /:/d\;n
- หากบรรทัดปัจจุบันตรงกับ
/:/
โคลอนd
จะลบออกจากเอาต์พุต อื่นพิมพ์อัตโนมัติในปัจจุบันและn
ต่อสาย
- ดังนั้นการส่งออกของ
sed
ลูกพรุนsort
ไปยังคู่สายตามลำดับเท่านั้นซึ่งไม่ตรงกับเครื่องหมายโคลอนและบรรทัดต่อไปนี้ - หรือเฉพาะบรรทัดจาก./L
และถัดไปเท่านั้น
cut -sd: -f2-
cut
-s
ตัวพิมพ์ใหญ่จากเอาต์พุตของบรรทัดอินพุตที่ไม่มี-d:
สตริงตัวกำจัดอย่างน้อยหนึ่งตัว- และ./L
บรรทัดของนั้นจะถูกตัดออกอย่างสมบูรณ์
- สำหรับสายเหล่านั้นที่ทำครั้งแรกของพวกเขา
:
ลำไส้ใหญ่คั่น-f
ield เป็นcut
ไป - และอื่น ๆ ไปทั้งหมดของgrep
's แทรก LineNo ของ
ทดสอบอินพุตขนาดเล็ก
seq 5 | sed -ne'2,3!w /tmp/L
s/.*/a-z &\& 0-9/p' >/tmp/F
... สร้างอินพุตตัวอย่าง 5 บรรทัด จากนั้น ...
( export LC_ALL=C; </tmp/F \
grep -n '' | sort -t: -nmk1,1 ./L - |
sed /:/d\;n | cut -sd: -f2-
)| head - /tmp[FL]
... พิมพ์ ...
==> standard input <==
a-z 1& 0-9
a-z 4& 0-9
a-z 5& 0-9
==> /tmp/F <==
a-z 1& 0-9
a-z 2& 0-9
a-z 3& 0-9
a-z 4& 0-9
a-z 5& 0-9
==> /tmp/L <==
1
4
5
การทดสอบหมดเวลาที่ใหญ่กว่า
ฉันสร้างไฟล์ขนาดใหญ่สองสามไฟล์:
seq 5000000 | tee /tmp/F |
sort -R | head -n1500000 |
sort -n >/tmp/L
... ซึ่งวางสาย 5mil ใน/tmp/F
และ 1.5mil /tmp/L
สายการสุ่มเลือกของเข้าที่ จากนั้นฉันก็:
time \
( export LC_ALL=C
grep -n '' | sort -t: -nmk1,1 ./L - |
sed /:/d\;n | cut -sd: -f2-
) <./F |wc - l
มันพิมพ์:
1500000
grep -n '' \
0.82s user 0.05s system 73% cpu 1.185 total
sort -t: -nmk1,1 /tmp/L - \
0.92s user 0.11s system 86% cpu 1.185 total
sed /:/d\;n \
1.02s user 0.14s system 98% cpu 1.185 total
cut -sd: -f2- \
0.79s user 0.17s system 80% cpu 1.184 total
wc -l \
0.05s user 0.07s system 10% cpu 1.183 total
(ฉันเพิ่มแบ็กสแลชที่นั่น)
ในบรรดาโซลูชั่นที่มีอยู่ในปัจจุบันนี่คือวิธีที่เร็วที่สุดของทั้งหมด แต่หนึ่งวิธีเมื่อเทียบกับชุดข้อมูลที่สร้างขึ้นบนเครื่องของฉัน ของคนอื่น ๆ เพียงคนเดียวที่เข้ามาใกล้ต่อสู้เพื่อที่สองและที่เป็น meuh เป็นที่นี่perl
นี่ไม่ใช่วิธีการดั้งเดิมที่เสนอ - มันลดลงหนึ่งในสามของเวลาดำเนินการขอบคุณคำแนะนำ / แรงบันดาลใจจากผู้อื่น ดูประวัติการโพสต์สำหรับการแก้ปัญหาช้า( แต่ทำไม?)
นอกจากนี้ยังเป็นที่น่าสังเกตว่าคำตอบอื่น ๆ อาจจะดีกว่าถ้ามันไม่ใช่สถาปัตยกรรม multi-cpu ของระบบของฉันและการทำงานพร้อมกันของแต่ละกระบวนการในไปป์ไลน์นั้น พวกเขาทั้งหมดทำงานในเวลาเดียวกัน - แต่ละตัวประมวลผลหลัก - ส่งผ่านข้อมูลและทำส่วนเล็ก ๆ ของทั้งหมด มันเจ๋งมาก
แต่ทางออกที่เร็วที่สุดคือ ...
แต่มันไม่ใช่ทางออกที่เร็วที่สุด วิธีที่เร็วที่สุดที่นำเสนอที่นี่มือลงเป็นโปรแกรม C cselect
ผมเรียกมันว่า หลังจากคัดลอกไปยังคลิปบอร์ด X ของฉันฉันรวบรวมมันเช่น:
xsel -bo | cc -xc - -o cselect
จากนั้นฉันก็:
time \
./cselect /tmp/L /tmp/F |
wc -l
... และผลลัพธ์ก็คือ ...
1500000
./cselect /tmp/L /tmp/F \
0.50s user 0.05s system 99% cpu 0.551 total
wc -l \
0.05s user 0.05s system 19% cpu 0.551 total