อะไรคือปัญหาในโลกแห่งความจริงที่วิธีการวนซ้ำเป็นวิธีแก้ปัญหาที่เป็นธรรมชาตินอกเหนือจากการค้นหาในเชิงลึกก่อน (DFS)
(ผมไม่ได้พิจารณาหอคอยฮานอย , จำนวนฟีโบนักชีหรือปัญหาที่เกิดขึ้นจริงในโลกแฟกทอ. พวกเขาเป็นบิต contrived ในใจของฉัน.)
อะไรคือปัญหาในโลกแห่งความจริงที่วิธีการวนซ้ำเป็นวิธีแก้ปัญหาที่เป็นธรรมชาตินอกเหนือจากการค้นหาในเชิงลึกก่อน (DFS)
(ผมไม่ได้พิจารณาหอคอยฮานอย , จำนวนฟีโบนักชีหรือปัญหาที่เกิดขึ้นจริงในโลกแฟกทอ. พวกเขาเป็นบิต contrived ในใจของฉัน.)
คำตอบ:
มีตัวอย่างมากมายที่นี่ แต่คุณต้องการตัวอย่างโลกแห่งความเป็นจริงดังนั้นด้วยการคิดสักนิดนี่อาจเป็นสิ่งที่ดีที่สุดที่ฉันสามารถเสนอได้:
คุณพบผู้ที่ติดเชื้อชนิดที่ระบุซึ่งไม่ร้ายแรงและแก้ไขได้เองอย่างรวดเร็ว (ประเภท A) ยกเว้น 1 ใน 5 คน (เราจะเรียกคนประเภทนี้ว่า B) ที่ติดเชื้ออย่างถาวรและแสดงว่าไม่มี อาการและเป็นเพียงการทำหน้าที่เป็นตัวแพร่กระจาย
สิ่งนี้สร้างคลื่นแห่งความหายนะที่ค่อนข้างน่ารำคาญเมื่อประเภท B ติดเชื้อประเภท A จำนวนมาก
งานของคุณคือการติดตาม Bs ทั้งหมดและสร้างภูมิคุ้มกันเพื่อหยุดกระดูกสันหลังของโรค น่าเสียดายที่คุณไม่สามารถให้การรักษาทั่วประเทศกับทุกคนได้เพราะคนที่เป็นประเภท A ก็แพ้ยาที่เหมาะกับประเภท B
วิธีที่คุณจะทำเช่นนี้คือการค้นพบทางสังคมโดยให้ผู้ติดเชื้อ (ประเภท A) เลือกผู้ติดต่อทั้งหมดในสัปดาห์ที่แล้วโดยทำเครื่องหมายผู้ติดต่อแต่ละรายบนกอง เมื่อคุณทดสอบบุคคลที่ติดเชื้อให้เพิ่มบุคคลเหล่านั้นในคิว "ติดตามผล" เมื่อบุคคลเป็นประเภท B ให้เพิ่มพวกเขาใน "ติดตาม" ที่หัว (เพราะคุณต้องการหยุดเร็วนี้)
หลังจากประมวลผลบุคคลที่กำหนดให้เลือกบุคคลจากด้านหน้าของคิวและใช้การฉีดวัคซีนหากจำเป็น หาผู้ติดต่อทั้งหมดที่ไม่เคยไปมาก่อนแล้วทดสอบดูว่าพวกเขาติดไวรัส
ทำซ้ำจนกว่าคิวผู้ติดเชื้อจะกลายเป็น 0 แล้วรอการระบาดอีก ..
(ตกลงนี่เป็นการทำซ้ำเล็กน้อย แต่เป็นวิธีการแก้ปัญหาซ้ำซากในกรณีนี้การข้ามผ่านฐานประชากรครั้งแรกอย่างกว้าง ๆ ที่พยายามค้นหาเส้นทางที่เป็นไปได้ของปัญหาและนอกจากนี้การแก้ปัญหาแบบวนซ้ำมักจะเร็วกว่าและมีประสิทธิภาพมากกว่า และฉันบังคับให้ลบการเรียกซ้ำทุกที่จนกลายเป็นสัญชาตญาณ .... ไอ้บ้า!)
เกี่ยวกับสิ่งที่เกี่ยวข้องกับโครงสร้างไดเร็กทอรีในระบบไฟล์ การค้นหาไฟล์ซ้ำ ๆ การลบไฟล์การสร้างไดเร็กทอรี ฯลฯ
นี่คือการใช้งาน Java ที่พิมพ์ซ้ำเนื้อหาของไดเร็กทอรีและไดเร็กทอรีย่อย
import java.io.File;
public class DirectoryContentAnalyserOne implements DirectoryContentAnalyser {
private static StringBuilder indentation = new StringBuilder();
public static void main (String args [] ){
// Here you pass the path to the directory to be scanned
getDirectoryContent("C:\\DirOne\\DirTwo\\AndSoOn");
}
private static void getDirectoryContent(String filePath) {
File currentDirOrFile = new File(filePath);
if ( !currentDirOrFile.exists() ){
return;
}
else if ( currentDirOrFile.isFile() ){
System.out.println(indentation + currentDirOrFile.getName());
return;
}
else{
System.out.println("\n" + indentation + "|_" +currentDirOrFile.getName());
indentation.append(" ");
for ( String currentFileOrDirName : currentDirOrFile.list()){
getPrivateDirectoryContent(currentDirOrFile + "\\" + currentFileOrDirName);
}
if (indentation.length() - 3 > 3 ){
indentation.delete(indentation.length() - 3, indentation.length());
}
}
}
}
quicksort , ผสานเรียงลำดับและส่วนใหญ่ N-บันทึกอื่น ๆ ทุกประเภท N
ตัวอย่างของ Matt Dillard เป็นสิ่งที่ดี โดยทั่วไปแล้วการเดินของต้นไม้โดยทั่วไปสามารถจัดการได้โดยการเรียกซ้ำได้อย่างง่ายดาย ตัวอย่างเช่นการรวบรวมต้นไม้แยกวิเคราะห์การเดินผ่าน XML หรือ HTML เป็นต้น
การเรียกซ้ำมีความเหมาะสมเมื่อใดก็ตามที่สามารถแก้ไขปัญหาได้โดยแบ่งออกเป็นปัญหาย่อยซึ่งสามารถใช้อัลกอริทึมเดียวกันในการแก้ปัญหาได้ อัลกอริทึมบนต้นไม้และรายการที่จัดเรียงเป็นแบบธรรมชาติ ปัญหามากมายในเรขาคณิตเชิงคำนวณ (และเกม 3 มิติ) สามารถแก้ไขซ้ำได้โดยใช้ต้นไม้แบ่งพื้นที่ไบนารี (BSP) การแบ่งส่วนย่อยไขมันหรือวิธีอื่น ๆ ในการแบ่งโลกออกเป็นส่วนย่อย
การเรียกซ้ำยังเหมาะสมเมื่อคุณพยายามรับประกันความถูกต้องของอัลกอริทึม ด้วยฟังก์ชันที่รับอินพุตที่ไม่เปลี่ยนรูปและส่งกลับผลลัพธ์ที่เป็นการรวมกันของการเรียกแบบวนซ้ำและแบบไม่เรียกซ้ำในอินพุตโดยปกติแล้วการพิสูจน์ว่าฟังก์ชันนั้นถูกต้อง (หรือไม่) โดยใช้การเหนี่ยวนำทางคณิตศาสตร์ มักจะทำไม่ได้กับฟังก์ชันวนซ้ำหรือด้วยอินพุตที่อาจกลายพันธุ์ สิ่งนี้จะมีประโยชน์เมื่อต้องจัดการกับการคำนวณทางการเงินและการใช้งานอื่น ๆ ซึ่งความถูกต้องเป็นสิ่งสำคัญมาก
แน่นอนว่าคอมไพเลอร์จำนวนมากใช้การเรียกซ้ำอย่างหนัก ภาษาคอมพิวเตอร์เป็นภาษาที่เรียกซ้ำโดยเนื้อแท้ (กล่าวคือคุณสามารถฝังคำสั่ง 'if' ไว้ในคำสั่ง 'if' อื่น ๆ ได้)
การปิดใช้งาน / การตั้งค่าแบบอ่านอย่างเดียวสำหรับตัวควบคุมเด็กทั้งหมดในคอนโทรลคอนเทนเนอร์ ฉันจำเป็นต้องทำสิ่งนี้เพราะการควบคุมเด็กบางคนเป็นตู้คอนเทนเนอร์เอง
public static void SetReadOnly(Control ctrl, bool readOnly)
{
//set the control read only
SetControlReadOnly(ctrl, readOnly);
if (ctrl.Controls != null && ctrl.Controls.Count > 0)
{
//recursively loop through all child controls
foreach (Control c in ctrl.Controls)
SetReadOnly(c, readOnly);
}
}
(ที่มา: mit.edu )
นี่คือคำจำกัดความของ eval:
(define (eval exp env)
(cond ((self-evaluating? exp) exp)
((variable? exp) (lookup-variable-value exp env))
((quoted? exp) (text-of-quotation exp))
((assignment? exp) (eval-assignment exp env))
((definition? exp) (eval-definition exp env))
((if? exp) (eval-if exp env))
((lambda? exp)
(make-procedure (lambda-parameters exp)
(lambda-body exp)
env))
((begin? exp)
(eval-sequence (begin-actions exp) env))
((cond? exp) (eval (cond->if exp) env))
((application? exp)
(apply (eval (operator exp) env)
(list-of-values (operands exp) env)))
(else
(error "Unknown expression type - EVAL" exp))))
นี่คือคำจำกัดความของการใช้:
(define (apply procedure arguments)
(cond ((primitive-procedure? procedure)
(apply-primitive-procedure procedure arguments))
((compound-procedure? procedure)
(eval-sequence
(procedure-body procedure)
(extend-environment
(procedure-parameters procedure)
arguments
(procedure-environment procedure))))
(else
(error
"Unknown procedure type - APPLY" procedure))))
นี่คือคำจำกัดความของลำดับการประเมิน:
(define (eval-sequence exps env)
(cond ((last-exp? exps) (eval (first-exp exps) env))
(else (eval (first-exp exps) env)
(eval-sequence (rest-exps exps) env))))
eval
-> apply
-> eval-sequence
->eval
การเรียกซ้ำใช้ในสิ่งต่างๆเช่นต้นไม้ BSP สำหรับการตรวจจับการชนกันในการพัฒนาเกม (และพื้นที่อื่น ๆ ที่คล้ายคลึงกัน)
ผู้คนมักจะจัดเรียงเอกสารหลายชุดโดยใช้วิธีเรียกซ้ำ ตัวอย่างเช่นสมมติว่าคุณกำลังจัดเรียงเอกสาร 100 ฉบับโดยมีชื่อกำกับอยู่ วางเอกสารลงในกองตามตัวอักษรตัวแรกจากนั้นเรียงแต่ละกอง
การค้นหาคำในพจนานุกรมมักดำเนินการโดยใช้เทคนิคการค้นหาแบบไบนารีซึ่งเป็นแบบวนซ้ำ
ในองค์กรหัวหน้ามักจะให้คำสั่งกับหัวหน้าแผนกซึ่งจะให้คำสั่งแก่ผู้จัดการและอื่น ๆ
ความต้องการในโลกแห่งความจริงที่ฉันได้รับเมื่อเร็ว ๆ นี้:
ข้อกำหนด A: ใช้คุณลักษณะนี้หลังจากทำความเข้าใจข้อกำหนด A อย่างถี่ถ้วนแล้ว
พาร์เซอร์และคอมไพเลอร์อาจเขียนด้วยวิธีการสืบเชื้อสายซ้ำ ไม่ใช่วิธีที่ดีที่สุดเนื่องจากเครื่องมือเช่น lex / yacc สร้างตัวแยกวิเคราะห์ได้เร็วขึ้นและมีประสิทธิภาพมากขึ้น แต่มีแนวคิดที่เรียบง่ายและใช้งานง่ายดังนั้นจึงยังคงเป็นเรื่องปกติ
การเรียกซ้ำจะใช้กับปัญหา (สถานการณ์) ซึ่งคุณสามารถแยกมันออก (ลด) เป็นส่วนย่อย ๆ และแต่ละส่วนจะมีลักษณะคล้ายกับปัญหาเดิม
ตัวอย่างที่ดีว่าสิ่งที่มีส่วนเล็ก ๆ คล้ายกับตัวมันเองอยู่ที่ใด:
การวนซ้ำเป็นเทคนิคในการแบ่งปัญหาออกเป็นชิ้นเล็ก ๆ และเล็กลงเรื่อย ๆ จนกระทั่งชิ้นส่วนหนึ่งในนั้นมีขนาดเล็กพอที่จะเป็นชิ้นเค้กได้ แน่นอนว่าหลังจากที่คุณแยกมันออกคุณจะต้อง "ต่อ" ผลลัพธ์กลับเข้าด้วยกันในลำดับที่ถูกต้องเพื่อหาวิธีแก้ปัญหาเดิมของคุณทั้งหมด
อัลกอริธึมการเรียงลำดับแบบวนซ้ำอัลกอริทึมการเดินบนต้นไม้แผนที่ / อัลกอริธึมการลดขั้นตอนการแบ่งและพิชิตเป็นตัวอย่างทั้งหมดของเทคนิคนี้
ในการเขียนโปรแกรมคอมพิวเตอร์ภาษาประเภทการเรียกกลับแบบสแต็กส่วนใหญ่มีความสามารถในตัวสำหรับการเรียกซ้ำอยู่แล้ว: เช่น
ฉันมีระบบที่ใช้การเรียกหางซ้ำแบบเพียว ๆในสถานที่ไม่กี่แห่งเพื่อจำลองเครื่องสถานะ
ตัวอย่างที่ดีของการเรียกซ้ำพบได้ในภาษาโปรแกรมที่ใช้งานได้ ในภาษาโปรแกรมที่ใช้งานได้ ( Erlang , Haskell , ML / OCaml / F #เป็นต้น) เป็นเรื่องปกติมากที่จะมีการเรียกใช้การประมวลผลรายการซ้ำ
เมื่อจัดการกับรายการในภาษาสไตล์ OOP ที่จำเป็นโดยทั่วไปมักจะเห็นรายการที่ใช้เป็นรายการที่เชื่อมโยงกัน ([item1 -> item2 -> item3 -> item4]) อย่างไรก็ตามในภาษาการเขียนโปรแกรมที่ใช้งานได้บางภาษาคุณจะพบว่ามีการนำรายการนั้นมาใช้ซ้ำโดยที่ "ส่วนหัว" ของรายการจะชี้ไปที่รายการแรกในรายการและ "หาง" จะชี้ไปยังรายการที่มีรายการที่เหลืออยู่ ( [item1 -> [item2 -> [item3 -> [item4 -> []]]]]) มันค่อนข้างสร้างสรรค์ในความคิดของฉัน
การจัดการรายการนี้เมื่อรวมกับการจับคู่รูปแบบจะมีประสิทธิภาพมาก สมมติว่าฉันต้องการรวมรายการตัวเลข:
let rec Sum numbers =
match numbers with
| [] -> 0
| head::tail -> head + Sum tail
โดยพื้นฐานแล้วจะบอกว่า "ถ้าเราถูกเรียกด้วยรายการว่างให้ส่งกลับ 0" (อนุญาตให้เราแบ่งการเรียกซ้ำ) มิฉะนั้นจะคืนค่าของส่วนหัว + ค่าของ Sum ที่เรียกพร้อมกับรายการที่เหลือ (ดังนั้นการเรียกซ้ำของเรา)
ตัวอย่างเช่นฉันอาจมีรายการไฟล์ URLฉันคิดว่าแยก URL ทั้งหมดที่แต่ละ URL เชื่อมโยงไปจากนั้นฉันจะลดจำนวนลิงก์ทั้งหมดที่ไปยัง / จาก URL ทั้งหมดเพื่อสร้าง "ค่า" สำหรับเพจ (แนวทางที่ Google ใช้กับPageRankและคุณสามารถค้นหาได้กำหนดไว้ในกระดาษMapReduceดั้งเดิม) คุณสามารถทำได้เพื่อสร้างจำนวนคำในเอกสารด้วย และอื่น ๆ อีกมากมายเช่นกัน
คุณสามารถขยายรูปแบบการทำงานนี้เป็นรหัสMapReduceประเภทใดก็ได้ที่คุณสามารถจดรายการบางสิ่งบางอย่างเปลี่ยนรูปแบบและส่งคืนสิ่งอื่น (ไม่ว่าจะเป็นรายการอื่นหรือคำสั่ง zip ในรายการ)
XML หรือข้ามผ่านอะไรก็ได้ที่เป็นต้นไม้ แม้ว่าตามจริงแล้วฉันไม่เคยใช้การเรียกซ้ำในงานของฉันเลย
คำติชมจะวนซ้ำในองค์กรตามลำดับชั้น
หัวหน้าระดับสูงบอกให้ผู้บริหารระดับสูงรวบรวมความคิดเห็นจากทุกคนใน บริษัท
ผู้บริหารแต่ละคนรวบรวมรายงานโดยตรงของตนและบอกให้รวบรวมข้อเสนอแนะจากรายงานโดยตรงของตน
และลงเส้น
ผู้ที่ไม่มีรายงานโดยตรง - โหนดใบไม้ในต้นไม้ - ให้ข้อเสนอแนะ
ข้อเสนอแนะจะเดินทางกลับไปที่ต้นไม้โดยผู้จัดการแต่ละคนจะเพิ่มข้อเสนอแนะของตนเอง
ในที่สุดคำติชมทั้งหมดก็กลับไปที่หัวหน้าระดับสูง
นี่เป็นวิธีแก้ปัญหาตามธรรมชาติเนื่องจากวิธีการเรียกซ้ำช่วยให้สามารถกรองได้ในแต่ละระดับ - การเรียงลำดับของรายการที่ซ้ำกันและการลบข้อเสนอแนะที่ไม่เหมาะสม หัวหน้าระดับสูงสามารถส่งอีเมลทั่วโลกและให้พนักงานแต่ละคนรายงานความคิดเห็นกลับไปหาเขาได้โดยตรง แต่มีปัญหา "คุณไม่สามารถจัดการกับความจริงได้" และปัญหา "คุณถูกไล่ออก" ดังนั้นการเรียกซ้ำจะดีที่สุดที่นี่
สมมติว่าคุณกำลังสร้าง CMS สำหรับเว็บไซต์โดยที่หน้าเว็บของคุณอยู่ในโครงสร้างแบบต้นไม้โดยบอกว่ารูทเป็นโฮมเพจ
สมมติว่า {ผู้ใช้ | ลูกค้า | ลูกค้า | เจ้านาย} ของคุณร้องขอให้คุณวางเส้นทางเบรดครัมบ์ในทุกหน้าเพื่อแสดงตำแหน่งที่คุณอยู่ในแผนภูมิ
สำหรับหน้าใด ๆ n คุณอาจต้องการเดินไปยังพาเรนต์ของ n และพาเรนต์และอื่น ๆ ซ้ำเพื่อสร้างรายการโหนดสำรองไปที่รูทของแผนผังเพจ
แน่นอนว่าคุณกดปุ่ม db หลายครั้งต่อหน้าในตัวอย่างนั้นดังนั้นคุณอาจต้องการใช้นามแฝง SQL ที่คุณค้นหา page-table เป็น a และ page-table อีกครั้งเป็น b และเข้าร่วม a.id ด้วย b.parent เพื่อให้คุณสร้างฐานข้อมูลทำการรวมแบบเรียกซ้ำ เป็นเวลานานแล้วดังนั้นไวยากรณ์ของฉันอาจไม่เป็นประโยชน์
จากนั้นอีกครั้งคุณอาจต้องการคำนวณเพียงครั้งเดียวและจัดเก็บไว้กับเรกคอร์ดของเพจโดยจะอัปเดตเฉพาะเมื่อคุณย้ายเพจ นั่นอาจจะมีประสิทธิภาพมากขึ้น
อย่างไรก็ตามนั่นคือ $ .02 ของฉัน
คุณมีโครงสร้างองค์กรที่ลึก N ระดับ มีการตรวจสอบโหนดหลายโหนดและคุณต้องการขยายเฉพาะโหนดที่ได้รับการตรวจสอบ
นี่คือสิ่งที่ฉันเขียนโค้ดจริงๆ ดีและง่ายด้วยการเรียกซ้ำ
ในงานของฉันเรามีระบบที่มีโครงสร้างข้อมูลทั่วไปที่สามารถอธิบายได้ว่าเป็นต้นไม้ นั่นหมายความว่าการเรียกซ้ำเป็นเทคนิคที่มีประสิทธิภาพมากในการทำงานกับข้อมูล
การแก้โดยไม่ต้องเรียกซ้ำจะต้องใช้รหัสที่ไม่จำเป็นจำนวนมาก ปัญหาเกี่ยวกับการเรียกซ้ำคือไม่ง่ายที่จะติดตามสิ่งที่เกิดขึ้น คุณต้องมีสมาธิจริงๆเมื่อทำตามขั้นตอนการประหารชีวิต แต่เมื่อมันใช้งานโค้ดได้อย่างสวยงามและมีประสิทธิภาพ
การคำนวณทางการเงิน / ฟิสิกส์เช่นค่าเฉลี่ยสารประกอบ
การแยกโครงสร้างของตัวควบคุมในWindows Formsหรือ WebForms (.NET Windows Forms / ASP.NET )
ตัวอย่างที่ดีที่สุดที่ฉันรู้คือQuicksortมันง่ายกว่ามากด้วยการเรียกซ้ำ ดูที่:
shop.oreilly.com/product/9780596510046.do
www.amazon.com/Beautiful-Code-Leading-Programmers-Practice/dp/0596510047
(คลิกที่คำบรรยายแรกใต้บทที่ 3: "รหัสที่สวยที่สุดที่ฉันเคยเขียน")
บริษัท โทรศัพท์และสายเคเบิลจะคงรูปแบบของโครงสร้างการเดินสายไว้ซึ่งผลที่ได้คือเครือข่ายหรือกราฟขนาดใหญ่ การเรียกซ้ำเป็นวิธีหนึ่งในการสำรวจโมเดลนี้เมื่อคุณต้องการค้นหาองค์ประกอบหลักทั้งหมดหรือองค์ประกอบลูกทั้งหมด
เนื่องจากการเรียกซ้ำมีราคาแพงจากมุมมองการประมวลผลและหน่วยความจำขั้นตอนนี้โดยทั่วไปจะดำเนินการเฉพาะเมื่อโทโพโลยีมีการเปลี่ยนแปลงและผลลัพธ์จะถูกเก็บไว้ในรูปแบบรายการที่สั่งซื้อล่วงหน้าที่แก้ไขแล้ว
การให้เหตุผลโดยอุปนัยกระบวนการของการสร้างแนวคิดนั้นเกิดขึ้นซ้ำซากในธรรมชาติ สมองของคุณทำงานตลอดเวลาในโลกแห่งความเป็นจริง
แสดงความคิดเห็นเกี่ยวกับคอมไพเลอร์ โหนดแผนภูมิโครงสร้างนามธรรมที่เป็นธรรมชาติจะยืมตัวเองไปสู่การเรียกซ้ำ โครงสร้างข้อมูลแบบวนซ้ำทั้งหมด (รายการที่เชื่อมโยงต้นไม้กราฟ ฯลฯ ) ยังจัดการได้ง่ายกว่าด้วยการเรียกซ้ำ ฉันคิดว่าพวกเราส่วนใหญ่ไม่ได้ใช้การเรียกซ้ำบ่อยนักเมื่อเราออกจากโรงเรียนเนื่องจากปัญหาในโลกแห่งความเป็นจริงหลายประเภท แต่ก็ควรตระหนักไว้เป็นทางเลือกหนึ่ง
การคูณจำนวนธรรมชาติเป็นตัวอย่างของการเกิดซ้ำในโลกแห่งความเป็นจริง:
To multiply x by y
if x is 0
the answer is 0
if x is 1
the answer is y
otherwise
multiply x - 1 by y, and add x