รูปแบบสำหรับอัลกอริทึมที่แสดงคำอธิบายถึงวิธีการแก้ปัญหาเมื่อจำเป็น


14

สถานการณ์ต่อไปนี้เกิดขึ้นกับฉันหลายครั้ง

ฉันตั้งโปรแกรมอัลกอริทึมที่แก้ปัญหาบางอย่าง มันทำงานได้ดีและค้นหาวิธีแก้ไขที่ถูกต้อง ตอนนี้ฉันต้องการมีตัวเลือกในการบอกอัลกอริทึม "เขียนคำอธิบายโดยละเอียดเกี่ยวกับวิธีการแก้ปัญหา" เป้าหมายของฉันคือสามารถใช้อัลกอริทึมในการสาธิตออนไลน์ชั้นเรียนกวดวิชาและอื่น ๆ ฉันยังต้องการมีตัวเลือกในการเรียกใช้อัลกอริทึมแบบเรียลไทม์โดยไม่มีคำอธิบาย รูปแบบการออกแบบที่ดีที่จะใช้คืออะไร?

ตัวอย่าง: สมมติว่าผมใช้วิธีนี้ในการหาตัวหารร่วมมาก วิธีที่ใช้ในปัจจุบันส่งกลับคำตอบที่ถูกต้อง แต่ไม่มีคำอธิบาย ฉันต้องการมีตัวเลือกสำหรับวิธีการอธิบายการกระทำของมันเช่น:

Initially, a=6 and b=4. The number of 2-factors, d, is initialized to 0.
a and b are both even, so we divide them by 2 and increment d by 1.
Now, a=3 and b=2.
a is odd but b is even, so we divide b by 2.
Now, a=3 and b=1.
a and b are both odd, so we replace a by (a-b)/2 = 1.
Now, a=1 and b=1.
a=b, so the GCD is a*2^d = 2.

ควรส่งคืนเอาต์พุตเพื่อให้สามารถแสดงได้อย่างง่ายดายทั้งในคอนโซลและในแอปพลิเคชันบนเว็บ

อะไรคือรูปแบบที่ดีในการให้คำอธิบายเมื่อจำเป็นในขณะที่ไม่กระทบต่อประสิทธิภาพของอัลกอริทึมแบบเรียลไทม์เมื่อไม่ต้องการคำอธิบาย

คำตอบ:


50

"รูปแบบ" ที่คุณกำลังค้นหาเรียกว่า "การบันทึก" เพียงสร้างข้อความสั่งการบันทึกเป็น verbose ตามที่คุณต้องการ ด้วยการใช้เฟรมเวิร์กการบันทึกข้อมูลที่ดีคุณควรจะสามารถเปิดและปิดในเวลาทำงานให้ระดับ verbosity ที่แตกต่างกันหรือปรับเอาท์พุทสำหรับวัตถุประสงค์ที่แตกต่างกัน (เช่นเว็บกับคอนโซล)

หากสิ่งนี้มีผลกระทบต่อประสิทธิภาพที่สังเกตเห็นได้ (แม้ว่าจะปิดการบันทึก) อาจขึ้นอยู่กับภาษากรอบงานและจำนวนคำสั่งการบันทึกที่คุณต้องการในกรณีเฉพาะ ในภาษาที่คอมไพล์หากสิ่งนี้กลายเป็นปัญหาคุณสามารถจัดเตรียมสวิตช์คอมไพเลอร์เพื่อสร้าง "ตัวแปรการบันทึก" และ "ตัวแปรที่ไม่ใช่การบันทึก" ของโค้ดของคุณ อย่างไรก็ตามฉันขอแนะนำอย่างยิ่งให้ปรับ "ในกรณี" โดยไม่ต้องวัดก่อน


2
ในขณะที่พวกเขาไม่ใช่สิ่งที่คุณเปิดและปิดเช่นเข้าสู่ระบบมันรู้สึกเหมือนความคิดเห็นและรหัสเอกสารด้วยตนเองอย่างน้อยควรได้รับการกล่าวถึงอย่างมีเกียรติในคำถามเกี่ยวกับ "อัลกอริทึมที่อธิบายตัวเอง"
candied_orange

9
@CandiedOrange คำถามถามโดยเฉพาะสำหรับ "คำอธิบาย" ที่มีค่าเวลาทำงานจริงรวมอยู่ในนั้น ความคิดเห็นจะไม่ช่วยอะไรมากในกรณีนี้
metacubed

@metacubed โอ้มา ฉันไม่ได้บอกว่าเป็นอีกทางเลือกหนึ่งในการบันทึก ดูที่ชื่อคำถามและคิดถึงการจราจรที่ผ่านมาที่นี่
candied_orange

4
@CandiedOrange: ฉันคิดว่าชื่อคำถามนั้นทำให้เข้าใจผิดคุณถูกต้องมันสามารถตีความได้ด้วยวิธีนั้น แต่มันไม่ใช่สิ่งที่ OP ขอมา แต่ฉันขอแก้ไขให้ถูกต้องฉันจะแก้ไขชื่อ
Doc Brown

1
โปรดทราบว่าสิ่งที่ชอบtreelogได้รับการออกแบบมาโดยเฉพาะเพื่อผลิตผลลัพธ์ที่อธิบายการคำนวณที่ซับซ้อนโดยสร้างบันทึกการเรียกใช้ฟังก์ชันที่สมบูรณ์
Boris the Spider

7

รูปแบบที่ดีคือนักสังเกตการณ์ https://en.wikipedia.org/wiki/Observer_pattern

ในอัลกอริทึมของคุณในแต่ละจุดที่คุณต้องการส่งออกบางสิ่งคุณจะแจ้งผู้สังเกตการณ์บางคน พวกเขาตัดสินใจว่าจะทำอะไรไม่ว่าจะเป็นข้อความของคุณออกบนคอนโซลหรือส่งไปยัง HTML engine / Apache เป็นต้น

ขึ้นอยู่กับภาษาการเขียนโปรแกรมของคุณอาจมีวิธีที่แตกต่างกันในการทำให้รวดเร็ว ตัวอย่างเช่นใน Java (ถือว่าเป็น pseudocode เพื่อความกะทัดรัดทำให้มัน "ถูกต้อง" ด้วย getters, setters ถูกปล่อยให้ผู้อ่าน):

interface AlgoLogObserver {
   public void observe(String message);
}

class AlgorithmXyz {   
   AlgoLogObserver observer = null;
   void runCalculation() {   
       if (observer!=null) { oberserver.observe("Hello"); }
       ...
   }   
}

...
algo = new AlgorithmXyz();
algo.observer = new ConsoleLoggingObserver();  // yes, yes make a 
                                               // setter instead, or use Ruby :-)
algo.runCalculation();

นี่เป็นเรื่องละเอียดเล็กน้อย แต่การตรวจสอบ==nullควรเร็วที่สุดเท่าที่จะทำได้

(โปรดทราบว่าในกรณีทั่วไปobserverอาจเป็นการVector observersอนุญาตให้ผู้สังเกตการณ์มากกว่าหนึ่งคนซึ่งเป็นไปได้แน่นอนเช่นกันและจะไม่นำไปสู่ค่าใช้จ่ายมากขึ้นคุณยังสามารถเพิ่มประสิทธิภาพที่คุณตั้งไว้observers=nullแทนที่จะมี ว่างเปล่าVector)

แน่นอนว่าคุณต้องใช้ผู้สังเกตการณ์หลายประเภทขึ้นอยู่กับสิ่งที่คุณต้องการบรรลุ นอกจากนี้คุณยังสามารถใส่สถิติกำหนดเวลา ฯลฯ หรือทำสิ่งแฟนซีอื่น ๆ


5

เป็นการปรับปรุงเล็กน้อยในการบันทึกโดยตรงสร้างวัตถุบางอย่างที่จำลองการดำเนินการของอัลกอริทึม เพิ่ม "ขั้นตอน" ไปยังวัตถุคอนเทนเนอร์นี้ทุกครั้งที่โค้ดของคุณทำสิ่งที่น่าสนใจ ในตอนท้ายของอัลกอริทึมบันทึกขั้นตอนสะสมจากคอนเทนเนอร์

นี่มีข้อดีบางประการ:

  1. คุณสามารถบันทึกการดำเนินการแบบเต็มเป็นรายการบันทึกเดียวซึ่งมักมีประโยชน์เมื่อมีความเป็นไปได้ของเธรดอื่นที่กำลังบันทึกสิ่งต่าง ๆ ระหว่างขั้นตอนอัลโกของคุณ
  2. ในรุ่น Java ของชั้นนี้ (เรียกง่ายๆ "Debug") ฉันไม่เพิ่มสตริงเป็นรายการบันทึก แต่ lambdasที่ผลิตสตริง lambdas เหล่านี้จะได้รับการประเมินถ้ามีการบันทึกที่เกิดขึ้นจริงเท่านั้นเช่นหากวัตถุ Debug พบว่าระดับการบันทึกถูกเปิดใช้งานในปัจจุบัน วิธีนี้ไม่มีค่าใช้จ่ายประสิทธิภาพการทำงานของการสร้างสตริงการบันทึกโดยไม่จำเป็น

แก้ไข: ตามความเห็นของผู้อื่นแลมบ์ดามีค่าใช้จ่ายดังนั้นคุณจะต้องสร้างมาตรฐานเพื่อให้แน่ใจว่าค่าใช้จ่ายนี้น้อยกว่าการประเมินโค้ดที่ไม่จำเป็นที่จำเป็นในการสร้างสตริงการบันทึก (รายการบันทึกมักไม่ใช่ตัวอักษรธรรมดา วัตถุที่เข้าร่วม)


2
มีแน่นอนค่าใช้จ่ายในการสร้างlambdas คือ ...
Sergio Tulentsev

1
เซร์คิโอทำให้แสงสว่าง แต่ไม่ได้อธิบายความเขลาของตรรกะของคุณอย่างเต็มที่ ค่าใช้จ่ายด้านประสิทธิภาพของการสร้างสตริงการบันทึกเป็นลำดับความสำคัญต่ำกว่าค่าใช้จ่ายด้านประสิทธิภาพของการสร้าง lambdas คุณทำให้การค้าแย่มากที่นี่
Kyeotic

2
@ Tyresius: คุณมีมาตรฐานที่เชื่อถือได้พิสูจน์แล้วหรือไม่? (มาตรฐานที่คุณเชื่อมโยงไปนั้นมีข้อบกพร่องอย่างมาก cf stackoverflow.com/questions/504103/… )
meriton

1
@Tyrsius ทุกอย่างขึ้นอยู่กับสถานการณ์ที่เฉพาะเจาะจง ฉันยังสามารถทำให้คุณมีเนื้อหาcounterexample ที่เกี่ยวข้องมากขึ้น คุณจะเห็นว่าเวอร์ชั่นของ String นั้นมีลำดับความสำคัญช้ากว่า Runnable กรณีนี้เป็นจริงมากขึ้นเนื่องจากในบริบทของคำถามนี้คุณจะต้องสร้างสตริงของคุณแบบไดนามิก นี้มักจะจำเป็นต้องสร้างวัตถุ StringBuilder ขณะที่กับแลมบ์ดาพวกเขาจะถูกสร้างขึ้นเมื่อจำเป็นเท่านั้น (เช่นเมื่อเข้าสู่ระบบอยู่บน)
jhyot

1
Lambdas มีค่าใช้จ่ายตกลง อย่างไรก็ตามการวัดประสิทธิภาพที่โพสต์ไม่เกี่ยวข้องอย่างสมบูรณ์ในบริบทนี้ การบันทึกอัลกอริทึมมักเกี่ยวข้องกับการประเมินโค้ดอื่นที่ไม่ได้รับการประเมินหากการบันทึกถูกข้าม (การดึงข้อมูลเชิงบริบทจากวัตถุที่เข้าร่วม ฯลฯ ) มันเป็นการประเมินผลที่แลมบ์ดาหลีกเลี่ยง แต่คุณพูดถูกแล้วคำตอบของฉันจะถือว่าค่าแลมบ์ดานั้นน้อยกว่าค่าใช้จ่ายนี้สิ่งที่ฉันไม่ได้ทดสอบอย่างสม่ำเสมอ
Cornel Masson

0

ฉันมักจะมองหาการแตกแขนงซึ่งหมายความว่าฉันมองหาคำสั่ง if เนื่องจากสิ่งเหล่านี้บ่งบอกว่าฉันประเมินค่าซึ่งจะควบคุมการไหลของอัลกอริทึม ในแต่ละโอกาส (แต่ละเงื่อนไข) ฉันสามารถบันทึกเส้นทางที่เลือกและสาเหตุที่เลือก

ดังนั้นโดยทั่วไปฉันจะบันทึกค่ารายการ (สถานะเริ่มต้น) ทุกสาขาที่เลือก (เงื่อนไข) และค่าเมื่อป้อนสาขาที่เลือก (สถานะชั่วคราว)


1
สิ่งนี้ไม่แม้แต่จะพยายามตอบคำถามที่ถามเกี่ยวกับการไม่ทำร้ายประสิทธิภาพแบบเรียลไทม์ของอัลกอริทึมเมื่อไม่ต้องการคำอธิบาย
gnat

ฉันตอบคำถามทั่วไปมากกว่านั้นและฉันตอบคำถามในระดับการออกแบบ แต่ถ้าเป็นข้อกังวลให้เพิ่มการตั้งค่าสถานะแบบมีเงื่อนไขหากคุณต้องการพิมพ์เพื่อบันทึกหรือไม่ ตั้งค่าสถานะนี้เป็นพารามิเตอร์เมื่อเรียกใช้
Richard Tyregrim
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.