thongkorn โพสต์ 2017-10-23 21:23:07

[VB.NET/C#] การคิดวิเคราะห์โจทย์เพื่อแก้ปัญหา การแสดงผลพื้นที่รูปสี่เหลี่ยมขนมเปียกปูน

http://www.g2gnet.com/webboard/images/vbnet/rhombusfull.png
__X
_XXX
XXXXX
_XXX
__Xโจทย์ข้อนี้ต้องการให้พิมพ์ X เป็นรูปพื้นที่สี่เหลี่ยมขนมเปียกปูน (Rhombus Area) โดยการรับค่าตัวเลขชนิดจำนวนเต็มแบบเลขคี่เข้ามา แล้วทำการพิมพ์ดังภาพตัวอย่าง ...

โจทย์ลักษณะแบบนี้ หากมองดูเผินๆค่อนข้างไร้สาระ เพราะนำไปใช้งานจริงไม่ได้ แต่ทว่าสำหรับแอดมินกลับมองเห็นเป็นความคลาสสิค คลาสสิคยังไงล่ะ??? ก็เพราะว่าคนส่วนใหญ่มักตีโจทย์โดยการคิดหาคำตอบแบบเดินหน้า (Forward Algorithm) คือพิมพ์คำตอบทีละบรรทัดน่ะซิ แต่หากคิดอีกแบบคือ การคิดย้อนกลับ (Backward Algorithm) ด้วยการมองไปที่คำตอบแบบรวมเอาไว้ทั้งหมด จากนั้นแยกวิเคราะห์เพื่อหาคำตอบกลับคืนไปทีละส่วนๆ เหมือนดั่งที่แอดมินจะนำมาอธิบายเอาไว้ในบทความชิ้นนี้ครับผม และความคลาสสิคอีกอย่างคือ เรานำหลักการวิธีคิดการมองภาพรวมเพื่อไปแก้ปัญหาโจทย์กับงานจริงๆได้แน่นอนครับ ... (อย่าลืมลองคิดหาวิธีอื่นๆด้วยล่ะ) ... หรืออ่านเสริมเรื่อง Working Backward Algorithm

ใช้ภาพประกอบจากทางด้านบน ... สิ่งแรกที่ต้องพิจารณา คือ จะรู้ได้อย่างไรว่าจำนวนตัวเลข (คี่) นั้นๆ มันอยู่ในลำดับที่เท่าไร??? และเพื่ออะไร??? ...

ลำดับที่ของเลขจำนวนเต็ม (คี่) = (ค่าอินพุท หารตัดเศษด้วย 2) + (ค่าอินพุท หารเอาเศษด้วย 2)
เช่น เลข 13 จะถูกจัดเรียงดังนี้ 1, 3, 5, 7, 9, 11, 13
ดังนั้นลำดับที่ของเลข 13 ...
= (13 \ 2) + (13 Mod 2)
= 6 + 1
= 7 --> นี่คือคำตอบ ซึ่งจะเป็นเงื่อนไขในการวนรอบ

เอ้า!!! แล้วคำตอบส่วนด้านล่างนั่นล่ะ มันหายไปไหน??? ... มันไม่ได้หายไปไหน แต่มันเป็นเงาของคำตอบที่อยู่ซีกด้านบน เพราะในเมื่อเราพิมพ์ส่วนด้านบนได้แล้ว ก็เอามันมาเป็นคำตอบในซีกด้านล่างซ่ะซิครับ จะไปคิดคำนวณหาให้เมื่อยก้นไปทำไมล่ะ จริงหรือเปล่า ... 5555+

FIRST ANALYSIS ...
      Dim S As String = ""
      Dim sArr(100) As String '// เก็บค่าคำตอบเงา
      For i As Integer = 1 To orderNum
            '// (1) คำตอบในการแสดงผล
            S = S + "X"
            '// เก็บค่าคำตอบเงา โดยเริ่มจาก Index = 0
            sArr(i - 1) = S
            '// แสดงผลคำตอบ พร้อมกับขึ้นบรรทัดใหม่
            Console.Write(S & vbCrLf)
            '// เพิ่ม "X" ไว้รออีก 1 ตัว เพราะค่าที่จะพิมพ์มันเป็นเลขคี่ เมื่อวนกลับไปที่ (1)
            S = S + "X"
      Nextสมมุติว่าค่าอินพุทที่รับเข้ามา คือ 5 ดังนั้น 1, 3, 5 ลำดับที่ของเลข 5 หรือ orderNum = 3
- รอบที่ 1 ... ค่า S เริ่มต้นจะว่างเปล่า จากนั้นเอา S = S & "X" คือการพิมพ์ X ออกไป 1 ตัว แต่ก่อนที่มันจะวนรอบกลับขึ้นไปก็เพิ่ม X เข้าไปต่อท้ายเป็น XX

- รอบที่ 2 ... ค่า S = "XX" จากนั้น S = S & "X" เมื่อนำไปต่อท้ายกับของเดิมคือ XX ก็จะกลายเป็นพิมพ์ XXX แต่ก่อนที่มันจะวนรอบกลับขึ้นไปก็เพิ่ม X เข้าไปต่อท้ายเป็น XXXX

- รอบที่ 3 ... ค่า S = "XXXX" จากนั้น S = S & "X" เมื่อนำไปต่อท้ายกับของเดิมคือ XXXX ก็จะกลายเป็นพิมพ์ XXXXX แต่ก่อนที่มันจะวนรอบกลับขึ้นไปก็เพิ่ม X เข้าไปต่อท้ายเป็น XXXXXX ...
X
XXX
XXXXXเนื่องจากค่าสูงสุดในการวนลูปคือ 3 รอบ (orderNum = 3) ก็เป็นการสิ้นสุดการพิมพ์ในชุดแรก ... จากบรรทัดคำสั่ง sArr(i - 1) = S คือการเก็บค่าที่พิมพ์ออกไปทั้ง 3 ชุดเอาไว้ ก็นำมันกลับมาพิมพ์ใหม่อีกรอบ แต่เป็นการพิมพ์คำตอบแบบเรียงกลับด้านกัน
      For i = orderNum - 1 To 1 Step -1
            Console.Write(sArr(i - 1) & vbCrLf)
      Next
XXX
Xเราจะเริ่มต้นพิมพ์แบบกลับด้านกัน โดยเริ่มจากค่า Upper Bound หรือค่าสูงสุดของ Array แล้วลดค่าลงทีละ 1 (Step -1) แต่ตัดส่วนคำตอบแรกทิ้งไป i = orderNum - 1 เพื่อไม่ต้องพิมพ์ซ้ำ

SECOND ANALYSIS ...
ส่วนแรกแม้ว่าเราจะยังไม่ได้คำตอบตามที่โจทย์ต้องการ แต่มันคือจุดเริ่มต้นของการคิด วิเคราะห์ปัญหา แบบมีลำดับขั้นตอน ...
__X
_XXX
XXXXX
_XXX
__Xให้ดู X แถวแรกบนสุดมันอยู่ตำแหน่งที่เท่าไร??? คำตอบคือ อยู่จุดกึ่งกลางของแถวที่ 3 ที่มีค่าสูงสุด และเนื่องจากว่ามันเป็นเลขคี่ เราลองกระจายลำดับเลขเล่นๆไป เช่น 1 2 3 4 5 เราจะสังเกตเห็นว่า จุดกึ่งกลางของเลขชุดนี้คือ 3 จะมีจำนวนตัวเลขที่อยู่ทางซ้ายและทางขวาเท่ากัน ... ตามนิยามกฏการสมมาตรอะไรประมาณนี้แหละครับ 5555+

ดังนั้นหากเราอยากรู้จุดกึ่งกลางของเลข 5 ก็คือการเอา (5 มาหารตัดเศษด้วย 2) + (5 หารเอาเศษด้วย 2) ก็คือ (5 \ 2) + (5 Mod 2) = 3 ... แต่โจทย์ต้องการให้เราพิมพ์ช่องว่าง (ASCII Code = 32 ฐาน 10) นำหน้า X เราจึงคิดเฉพาะส่วนแรกคือ 5 หารตัดเศษด้วย 2 ก็พอ
      For i As Integer = 1 To orderNum
            '// หาค่า Space นำหน้า ด้วยการหารตัดเศษ (โดยที่ค่า N จะลดค่าลงทีละ 2 เพราะมันเป็นเลขคี่ ... อยู่ด้านล่างใกล้ Next)
            For sP = 1 To (N \ 2)
                S = Chr(32) & S
            Next
            '// (1) เอา Space นำหน้าตามด้วยคำตอบ
            S = S + "X"
            '// เก็บค่าคำตอบเงา โดยเริ่มจาก Index = 0
            sArr(i - 1) = S
            '// แสดงผลคำตอบ พร้อมกับขึ้นบรรทัดใหม่
            Console.Write(S & vbCrLf)
            '// เพิ่ม "X" ไว้รออีก 1 ตัว เพราะค่าที่จะพิมพ์มันเป็นเลขคี่ เมื่อวนกลับไปที่ (1)
            '// ตัดค่าช่องว่างออกไป เพราะจะวนไปรับค่า Space หรือ Chr(32) ใหม่
            S = Trim(S & "X")
            '// ลดค่าอินพุทลงไปทีละ 2 เพราะเป็นเลขคี่ เช่นจาก 5 ก็จะเหลือ 3 และเหลือ 1 เพื่อสิ้นสุด
            N = N - 2
      Nextดังนั้นเราเพิ่มโค้ดใส่ช่องว่างนำหน้าเข้าไปก่อนการพิมพ์ โดยจำนวนช่องว่างที่ต้องพิมพ์ sP = 1 To (N \ 2)
- รอบที่ 1 ... sP = 1 To (5 \ 2) = 2 คือพิมพ์ Chr(32) หรือช่องว่างนำหน้าไป 2 ตัว เมื่อพิมพ์เสร็จลดค่ามันลงไป 2 ก็จะเป็น N = 5 - 2 = 3

- รอบที่ 2 ... sP = 1 TO (3 \ 2) = 1 คือพิมพ์ Chr(32) หรือช่องว่างนำหน้าไป 1 ตัว เมื่อพิมพ์เสร็จลดค่ามันลงไป 2 ก็จะเป็น N = 3 - 2 = 1

- รอบที่ 3 ... sP = 1 TO (1 \ 2) = 0 คือไม่มีการพิมพ์ Chr(32) หรือช่องว่างใดๆออกมา ... และมันเป็นจังหวะเดียวกับที่ orderNum = 3 จะเป็นการวนรอบในครั้งสุดท้ายพอดี ... ก็จบบริบูรณ์

มาดูโค้ดฉบับเต็ม VB.NET ...
' / --------------------------------------------------------------------------------
' / Developer : Mr.Surapon Yodsanga (Thongkorn Tubtimkrob)
' / eMail : thongkorn@hotmail.com
' / URL: http://www.g2gnet.com (Khon Kaen - Thailand)
' / Facebook: https://www.facebook.com/g2gnet (For Thailand)
' / Facebook: https://www.facebook.com/commonindy (Worldwide)
' / Purpose: How to solve problems in programming.
' / Microsoft Visual Basic .NET (2010)
' /
' / This is open source code under @CopyLeft by Thongkorn Tubtimkrob.
' / You can modify and/or distribute without to inform the developer.
' / --------------------------------------------------------------------------------
Module Module1

    Sub Main()
      Console.Clear()
      Dim orderNum As Integer
      Console.Write("Enter number: ")
      Dim N As Integer = Console.ReadLine()
      '// หาลำดับที่ของตัวเลข ด้วยการ (จำนวนหารตัดเศษด้วย 2)+(จำนวนหารเอาเศษด้วย 2) ... จะเป็นค่าสูงสุดในการวนรอบ
      orderNum = (N \ 2) + (N Mod 2)
      Console.WriteLine("The order is {0}", orderNum)
      Call FirstMethod(N, orderNum)
      Call SecondMethod(N, orderNum)
    End Sub

    Sub FirstMethod(ByVal N As Integer, orderNum As Integer)
      Dim S As String = ""
      Dim sArr(100) As String '// เก็บค่าคำตอบเงา
      For i As Integer = 1 To orderNum
            '// (1) คำตอบในการแสดงผล
            S = S + "X"
            '// เก็บค่าคำตอบเงา โดยเริ่มจาก Index = 0
            sArr(i - 1) = S
            '// แสดงผลคำตอบ พร้อมกับขึ้นบรรทัดใหม่
            Console.Write(S & vbCrLf)
            '// เพิ่ม "X" ไว้รออีก 1 ตัว เพราะค่าที่จะพิมพ์มันเป็นเลขคี่ เมื่อวนกลับไปที่ (1)
            S = S + "X"
      Next
      For i = orderNum - 1 To 1 Step -1
            Console.Write(sArr(i - 1) & vbCrLf)
      Next
      Console.ReadLine()
    End Sub

    Sub SecondMethod(ByVal N As Integer, orderNum As Integer)
      Dim S As String = ""
      Dim sP As Integer = orderNum - 1
      Dim sArr(100) As String
      For i As Integer = 1 To orderNum
            '// หาค่า Space นำหน้า ด้วยการหารตัดเศษ (โดยที่ค่า N จะลดค่าลงทีละ 2 เพราะมันเป็นเลขคี่ ... อยู่ด้านล่างใกล้ Next)
            For sP = 1 To (N \ 2)
                S = Chr(32) & S
            Next
            '// (1) เอา Space นำหน้าตามด้วยคำตอบ
            S = S + "X"
            '// เก็บค่าคำตอบเงา โดยเริ่มจาก Index = 0
            sArr(i - 1) = S
            '// แสดงผลคำตอบ พร้อมกับขึ้นบรรทัดใหม่
            Console.Write(S & vbCrLf)
            '// เพิ่ม "X" ไว้รออีก 1 ตัว เพราะค่าที่จะพิมพ์มันเป็นเลขคี่ เมื่อวนกลับไปที่ (1)
            '// ตัดค่าช่องว่างออกไป เพราะจะวนไปรับค่า Space หรือ Chr(32) ใหม่
            S = Trim(S & "X")
            '// ลดค่าอินพุทลงไปทีละ 2 เพราะเป็นเลขคี่ เช่นจาก 5 ก็จะเหลือ 3 และเหลือ 1 เพื่อสิ้นสุด
            N = N - 2
      Next
      '// แสดงผลส่วนเงา
      For i = orderNum - 1 To 1 Step -1
            Console.Write(sArr(i - 1) + vbCrLf)
      Next
      Console.ReadLine()
    End Sub
มาดูโค้ดฉบับเต็ม C# ...
ขอให้สังเกตการวนรอบเพื่อพิมพ์ช่องว่าง คราวนี้แอดมินเปลี่ยนใหม่เป็น การใช้ค่าลำดับตัวเลขของอินพุทเป็นเงื่อนไขแทน หากเราพิจารณาจากรูปประกอบทางด้านบนจะเห็นได้ว่า จำนวนช่องว่างจะเริ่มต้นจากค่าลำดับตัวเลข - 1 จากนั้น มันก็จะถูกลดค่าลงหนึ่งเสมอทุกๆครั้งที่วนรอบกลับมา ... อั้ยย่ะ!!!using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace BasicThinkingCSharp
{
    class Program
    {
      static void Main(string[] args)
      {
            int orderNum = 0;
            Console.Write("Enter number: ");
            string n = Console.ReadLine();
            orderNum = (Int32.Parse(n) / 2) + (Int32.Parse(n) % 2);
            Console.WriteLine("The order is {0}", orderNum);
            firstmethod(Int32.Parse(n), orderNum);
}

      static void firstmethod(int m, int order) {
            string s = "";
            int sp = order-1;
            string[] sArr = new string;
            for (int i = 1; i <= order; i++) {
                for (int j = 1; j <= sp; j++) s = " " + s;
                s = s + "X";
                sArr = s;
                Console.Write(s + "\n");
                s = s.Replace(" ", "");
                s = s + "X";
                sp--;
            }
            for (int i = order-1; i >= 0; i--) Console.Write(sArr + "\n");
            Console.ReadLine();
      }

}
}
http://www.g2gnet.com/webboard/images/vbnet/forwardalgorithm.png
วิธีคิดหาคำตอบแบบเดินหน้า (VB.NET)
ก็คือการพิมพ์แบบทีละบรรทัด โดยเริ่มจากบรรทัดแรกบนสุด ซึ่งจะมีช่องว่างเริ่มต้นจาก (ลำดับที่ตัวเลข - 1) ... แอดมินคิดว่าในเรื่องนี้คงไม่ต้องอธิบายมากมาย เพราะเชื่อว่าหลายๆคนคงต้องคิดแบบลักษณะนี้กันอยู่แล้วแหละข่ะรับกระผม
    Sub ThirdMethod(ByVal N As Integer, orderNum As Integer)
      Dim S As String = ""
      Dim sP As Integer = orderNum - 1
      Dim L As Integer = 1
      For i As Integer = 1 To orderNum
            For k As Integer = 1 To sP
                '// จำนวนช่องว่าง
                S = S & " "
            Next
            For j As Integer = 1 To L
                S = S & "X"
            Next
            Console.WriteLine(S)
            L += 2 : sP -= 1 : S = ""
      Next
      '// ทำกลับด้าน
      sP = 1
      L = N - 2
      For i As Integer = orderNum - 1 To 1 Step -1
            For k As Integer = 1 To sP
                '// จำนวนช่องว่าง
                S = S & " "
            Next
            For j As Integer = 1 To L
                S = S & "X"
            Next
            Console.WriteLine(S)
            L -= 2 : sP += 1 : S = ""
      Next
      Console.ReadLine()
    End Sub
วิธีคิดหาคำตอบแบบเดินหน้า (C#)
      // การคิดแบบเดินหน้าหาคำตอบ (Forward Algorithm)
      static void secondmethod(int m, int order) {
            string s = "";
            int sp = order - 1;
            int x = 1;
            for (int i = 1; i <= order; i++) {
                for (int j = 1; j <= sp; j++)s = s + " ";
                for (int k = 1; k <= x; k++)s = s + "X";
                Console.WriteLine(s);
                x += 2; sp -= 1; s = "";
            }
            sp = 1;
            x = m - 2;
            for (int i = order-1; i >= 1; i--) {
                for (int j = 1; j <= sp; j++) s = s + " ";
                for (int k = 1; k <= x; k++) s = s + "X";
                Console.WriteLine(s);
                x -= 2; sp += 1; s = "";
            }
            Console.ReadLine();
      }Conclusion: จะเห็นได้เลยว่าการแก้ปัญหาโจทย์ข้อนี้ไม่ได้ใช้กฏสมการ หรือการคำนวณอะไรที่ซับซ้อนใดๆเลย ใช้เพียงแต่คำว่า "พื้นฐาน" เท่านั้นเอง ... สวัสดี
หน้า: [1]
ดูในรูปแบบกติ: [VB.NET/C#] การคิดวิเคราะห์โจทย์เพื่อแก้ปัญหา การแสดงผลพื้นที่รูปสี่เหลี่ยมขนมเปียกปูน