|
ทฤษฎีบทพีทาโกรัส คือ ทฤษฎีความสัมพันธ์ระหว่างด้านทั้งสามของสามเหลี่ยมมุมฉาก สามเหลี่ยมที่มีมุมใดมุมหนึ่งตั้งฉาก 90 องศา เป็นทฤษฎีที่คิดค้นโดยพีทาโกรัส นักคณิตศาสตร์ชาวกรีก ซึ่งกล่าวถึงทฤษฎีนี้ไว้ว่า
"ในสามเหลี่ยมมุมฉากใด ๆ พื้นที่ของสี่เหลี่ยมจัตุรัสที่มีด้านเป็นด้านตรงข้ามมุมฉาก เท่ากับผลรวมพื้นที่ของสี่เหลี่ยมจัตุรัสที่มีด้านเป็นด้านประชิดมุมฉากของสามเหลี่ยมมุมฉากนั้น"
จากทฤษฎีบทพีทาโกรัส ทำให้เราสามารถเขียนสูตรพีทาโกรัสเพื่อหาความยาวของด้านต่างๆของสามเหลี่ยมมุมฉากได้ดังนี้
a² + b² = c²
โจทย์ของเราก็คือ การหาชุดของความยาวด้านของสามเหลี่ยมมุมฉากที่เป็นจำนวนเต็มทั้งหมดที่จะเป็นไปได้
ในการทดสอบค่าความถูกต้องของสมการ Pythagoras ซึ่งเป็น a^2 + b^2 = c^2 เราจะทดสอบค่า a, b, และ c ทุกคู่ที่เป็นไปได้ ซึ่งค่า a, b, และ c จะต้องเป็นจำนวนเต็มบวก และค่า c ต้องมากกว่าค่า a และมากกว่าค่า b ด้วย ดังนั้นเราสามารถใช้ Nested Loop สามชั้นเพื่อทดสอบทุกคู่ของค่า a และ b แล้วคำนวณหาค่า c เพื่อเปรียบเทียบค่าของ a^2 + b^2 ว่าเท่ากับ c^2 หรือไม่?
ข้อสังเกต:
การใช้ Nested Loop ซ้อนกันถึง 3 ลูป โดยให้ลูป B และ ลูป C ทำการอ่านค่าเริ่มต้นจาก 1 ทำให้จำนวนรอบวงในสุดจะต้องอ่านค่าและเปรียบเทียบกันถึง 100 x 100 x 100 = 1,000,000 รอบ ซึ่งจะต้องใช้เวลามาก แต่คำตอบที่ได้จะเกิดจากการย้ายข้างของ a กับ b เช่น a = 3, b = 4 คำตอบ c = 5 พอให้ a = 4, b = 3 (การเริ่มนับจาก 1) แต่คำตอบ c = 5 เหมือนเดิม ดังนั้นเราจะใช้การลดจำนวนการนับลงไป โดยให้ลูปในมีค่าเริ่มต้นจากลูปนอกแทน ...
เมื่อกำหนดค่าเริ่มต้นใหม่โดยให้ลูปในมีค่าเริ่มต้นจากลูปนอกแทน ... เราให้ลูปในมีค่าเริ่มต้นจากค่าของลูปนอกแทน คือ b เริ่มจากค่า a ส่วน c จะเริ่มจากค่า b (b จะนับค่าต่ำกว่า a ไม่ได้ และ c จะนับค่าต่ำกว่า b ไม่ได้นั่นเอง) ...
- For b As Integer = a To Val(txtMaxValue.Text)
- For c As Integer = b To Val(txtMaxValue.Text)
คัดลอกไปที่คลิปบอร์ด จากลูป b จะวนรอบเพียง 50% ส่วนลูป c จะวนรอบเหลือไม่เกิน 20% คำตอบที่ได้ก็จะเหลือเพียง 52 รายการ แต่อีก 52 รายการที่ไม่ได้นำมาคิดคำนวณ ก็คือการสลับค่ากันระหว่างค่า a และ b นั่นเอง ...
การนำ LINQ (Language Integrated Query) มาช่วยในการแก้ปัญหาของความช้าในการวนลูปแบบ Nested Loop ... LINQ (ปกติจะอ่านว่าลิ้ง แต่บางคนก็อ่านว่าลิน หรือลินคิว ก็เอาตามที่สะดวกล่ะกันครับ) เจ้าตัวนี้จัดว่าเป็นความสามารถอันทรงพลังของ WinForm หรือ .Net Framework เลยก็ว่าได้ เพราะ LINQ คือชุดคำสั่งที่ทำงานกับกลุ่มของข้อมูล คล้ายๆกับการ Query ในฐานข้อมูลยังไงยังงั้นแหละครับทั่นผู้ชม
- ' / LINQ
- Dim PythagoreanTriple =
- From a In Enumerable.Range(1, MaxValue)
- From b In Enumerable.Range(a, MaxValue)
- Let c = Math.Sqrt(a * a + b * b)
- Where (c = Math.Floor(c) AndAlso c <= MaxValue)
- Select New With {a, b, c}
คัดลอกไปที่คลิปบอร์ด ตัวอย่างที่แอดมินทำการทดสอบโดยให้ค่าสูงสุดที่ 100 อาจจะมองไม่เห็นผลสักเท่าไหร่ ลองใช้สัก 1,000 หรือ 10,000 รอบดู ทุกๆท่านจะได้เห็นผลลัพธ์ของการทำงานที่แตกต่างกันมากเลยทีเดียว ...
เพิ่มเติม: นอกจากจะเอา LINQ มาแก้ปัญหาแล้ว โจทย์ข้อนี้ยังนำทางให้เราไปฝึกเขียนโค้ดเพื่อแยกการทำงานของ Thread ได้ด้วย รวมไปถึงการเรียนรู้ใช้งาน Parallel programming in .NET ... เอาไว้แอดมินจะนำมาเสนอให้ทุกๆท่านได้รับชมกันอีกทีครับ
แอดมินนำผลลัพธ์ที่ได้ไปใส่ไว้ TextBox, ListView และ DataGridView Control เพื่อสำหรับมือใหม่ๆจะได้ฝึกเรียนรู้ในการใช้งาน Control พื้นฐานต่างๆเหล่านี้ไปด้วยครับผม ...
มาดูโค้ดฉบับเต็มกันเถอะ ...
- Imports System.Text
- Public Class frmPythagoras
- '/ Start-Stop Timer
- Private mTimeDouble As Double
- Private sWatch As New Stopwatch()
- Private Sub frmPythagoras_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
- lblCount.Text = ""
- lblTimer.Text = ""
- lblLoop.Text = ""
- txtMaxValue.MaxLength = 6
- '// ฝากเป็นโจทย์ไปคิดในกรณีที่เราคิดกลับ เมื่อใช้สูตร a=2n, b=n²-1, c=n²+1
- End Sub
- ' / -------------------------------------------------------------------------------------------------
- ' / เริ่มต้นในการประมวลผลแบบพื้นฐานด้วยการใช้ Nested Loop
- ' / -------------------------------------------------------------------------------------------------
- Private Sub btnNestedLoop_Click(sender As System.Object, e As System.EventArgs) Handles btnNestedLoop.Click
- TextBox1.Clear()
- Call InitializeListView()
- Call InitializeDataGridView()
- '// สร้าง String Builder สำหรับเก็บค่าไปแสดงผลใน TextBox Control (System.Text)
- Dim strBuilder As New StringBuilder()
- '// สร้าง ListViewItem เพื่อเก็บข้อมูล
- Dim items As New List(Of ListViewItem)
- '// สร้าง DataTable เพื่อเก็บข้อมูลให้กับ DataGridView
- Dim DT As New DataTable
- With DT.Columns
- .Add("A", GetType(Integer))
- .Add("B", GetType(Integer))
- .Add("C", GetType(Integer))
- End With
- lblTimer.Text = "Timer Nested Loop : "
- sWatch.Reset()
- sWatch.Start()
- Cursor = Cursors.WaitCursor
- '// ไว้นับจำนวนลูป
- Dim CountLoopA As Integer = 0 : Dim CountLoopB As Integer = 0 : Dim CountLoopC As Integer = 0
- '// ใช้ Nested Loop โดยกำหนดให้ลูปในใช้ค่าเริ่มต้นจากลูปนอก
- '// เพราะค่าตัวแปรของ a สลับกันกับ b ก็จะไม่มีผลอะไร เช่น a = 3, b = 4 หากกลับค่า a = 4, b = 3 ก็จะทำให้ได้ค่า c = 5 เท่ากัน
- '// และจะทำให้ลดจำนวนการวนรอบของลูปลงไป
- For a As Integer = 1 To Val(txtMaxValue.Text)
- CountLoopA += 1
- For b As Integer = a To Val(txtMaxValue.Text)
- CountLoopB += 1
- For c As Integer = b To Val(txtMaxValue.Text)
- CountLoopC += 1
- ' / --------------------------------------------------------
- ' / เงื่อนไขที่ต้องการทดสอบ คือ a^2 + b^2 = c^2
- ' / --------------------------------------------------------
- If a * a + b * b = c * c Then
- '// สำหรับ TextBox Control
- strBuilder.AppendLine("a = " & a & vbTab & "b = " & b & vbTab & "c = " & c)
- '// สำหรับ ListView Control
- Dim NewItem As New ListViewItem(a) '// กำหนดค่าให้กับคอลัมน์แรก
- NewItem.SubItems.Add(b) ' เพิ่มค่าในคอลัมน์ที่สอง
- NewItem.SubItems.Add(c) ' เพิ่มค่าในคอลัมน์ที่สาม
- items.Add(NewItem)
- '// สำหรับ DataGridView Control
- Dim row As String() = New String() {"", "", ""}
- DT.Rows.Add(a, b, c)
- '// หากใช้ Exit For ก็จะช่วยให้ลดจำนวนการวนรอบลงไปได้อีก
- 'Exit For
- End If
- Next
- Next
- Next
- '// แสดงผลลัพธ์ใน TextBox Control
- TextBox1.Text = strBuilder.ToString()
- '// แสดงผลลัพธ์ใน ListView Control
- ListView1.Items.AddRange(items.ToArray())
- '// แสดงผลลัพธ์ใน DataGridView Control
- DataGridView1.DataSource = DT
- lblCount.Text = "จำนวน: " & DataGridView1.RowCount & " รายการ"
- DT.Dispose()
- txtMaxValue.Focus()
- Cursor = Cursors.Default
- sWatch.Stop()
- mTimeDouble = sWatch.ElapsedMilliseconds * 0.001
- lblTimer.Text = lblTimer.Text & mTimeDouble.ToString & " sec."
- lblLoop.Text = "a = " & Format(CountLoopA, "#,##") & vbCrLf & "b = " & Format(CountLoopB, "#,##") & vbCrLf & "c = " & Format(CountLoopC, "#,##")
- End Sub
- ' / -------------------------------------------------------------------------------------------------
- ' / สามารถใช้ LINQ ใน VB.NET เพื่อหาชุดของความยาวด้านของสามเหลี่ยมมุมฉาก
- ' / Pythagorean Triplets ที่เป็นเลขจำนวนเต็มทั้งหมดที่จะเป็นไปได้ดังนี้
- ' / -------------------------------------------------------------------------------------------------
- Private Sub btnLinQ_Click(sender As System.Object, e As System.EventArgs) Handles btnLinQ.Click
- TextBox1.Clear()
- Call InitializeListView()
- Call InitializeDataGridView()
- '//
- Dim MaxValue As Integer = Val(txtMaxValue.Text) '// กำหนดความยาวด้านสูงสุดที่ต้องการทดสอบ
- '// สร้าง String Builder สำหรับเก็บค่าไปแสดงผลใน TextBox Control (System.Text)
- Dim strBuilder As New StringBuilder()
- '// สร้าง ListViewItem เพื่อเก็บข้อมูล
- Dim items As New List(Of ListViewItem)
- '// สร้าง DataTable เพื่อเก็บข้อมูลให้กับ DataGridView
- Dim DT As New DataTable
- With DT.Columns
- .Add("A", GetType(Integer))
- .Add("B", GetType(Integer))
- .Add("C", GetType(Integer))
- End With
- lblTimer.Text = "Timer LINQ : "
- sWatch.Reset()
- sWatch.Start()
- Cursor = Cursors.WaitCursor
- ' / -------------------------------------------------------------------------------------------------
- ' / LINQ
- Dim PythagoreanTriple =
- From a In Enumerable.Range(1, MaxValue)
- From b In Enumerable.Range(a, MaxValue)
- Let c = Math.Sqrt(a * a + b * b)
- Where (c = Math.Floor(c) AndAlso c <= MaxValue)
- Select New With {a, b, c}
- ' / -------------------------------------------------------------------------------------------------
- For Each triplet In PythagoreanTriple
- strBuilder.AppendLine("a = " & triplet.a & vbTab & "b = " & triplet.b & vbTab & "c = " & triplet.c)
- '// สำหรับ ListView Control
- Dim NewItem As New ListViewItem(triplet.a) '// กำหนดค่าให้กับคอลัมน์แรก
- NewItem.SubItems.Add(triplet.b) ' เพิ่มค่าในคอลัมน์ที่สอง
- NewItem.SubItems.Add(triplet.c) ' เพิ่มค่าในคอลัมน์ที่สาม
- items.Add(NewItem)
- '// สำหรับ DataGridView Control
- Dim row As String() = New String() {"", "", ""}
- DT.Rows.Add(triplet.a, triplet.b, triplet.c)
- Next
- '// แสดงผลลัพธ์ใน TextBox Control
- TextBox1.Text = strBuilder.ToString()
- '// แสดงผลลัพธ์ใน ListView Control
- ListView1.Items.AddRange(items.ToArray())
- '// แสดงผลลัพธ์ใน DataGridView Control
- DataGridView1.DataSource = DT
- lblCount.Text = "จำนวน: " & DataGridView1.RowCount & " รายการ"
- DT.Dispose()
- Cursor = Cursors.Default
- sWatch.Stop()
- mTimeDouble = sWatch.ElapsedMilliseconds * 0.001
- lblTimer.Text = lblTimer.Text & mTimeDouble.ToString & " sec."
- lblLoop.Text = ""
- End Sub
- ' / -------------------------------------------------------------------------------------------------
- Private Sub txtMaxValue_KeyPress(sender As Object, e As System.Windows.Forms.KeyPressEventArgs) Handles txtMaxValue.KeyPress
- '// เมื่อมีการกด Enter
- If Asc(e.KeyChar) = 13 Then
- If String.IsNullOrWhiteSpace(txtMaxValue.Text) AndAlso Not Char.IsControl(e.KeyChar) Then
- e.Handled = True
- Else
- '// ไปประมวลผล
- Call btnNestedLoop_Click(sender, e)
- e.Handled = True
- End If
- ElseIf Not Char.IsDigit(e.KeyChar) AndAlso Not Char.IsControl(e.KeyChar) Then
- e.Handled = True '// ไม่อนุญาตให้ป้อนค่าที่ไม่ใช่ตัวเลข
- End If
- End Sub
- Private Sub TextBox1_KeyPress(sender As Object, e As System.Windows.Forms.KeyPressEventArgs) Handles TextBox1.KeyPress
- '// ป้องกันไม่ให้กดคีย์อะไรลงไปใน TextBox Control.
- e.Handled = True
- End Sub
- ' / -------------------------------------------------------------------------------------------------
- ' / Initialized DataGridView Control.
- ' / -------------------------------------------------------------------------------------------------
- Private Sub InitializeDataGridView()
- With DataGridView1
- .RowHeadersVisible = False ' True
- .AllowUserToAddRows = False
- .AllowUserToDeleteRows = False
- .AllowUserToResizeRows = False
- .MultiSelect = True ' False
- .SelectionMode = DataGridViewSelectionMode.FullRowSelect
- .ReadOnly = True
- '// Data rows
- .Font = New Font("Tahoma", 10)
- .RowTemplate.MinimumHeight = 27
- .RowTemplate.Height = 27
- '// Autosize Column
- .AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill
- '// Header
- With .ColumnHeadersDefaultCellStyle
- .BackColor = Color.RoyalBlue
- .ForeColor = Color.White
- .Font = New Font(DataGridView1.Font, FontStyle.Bold)
- End With
- End With
- End Sub
- ' / -------------------------------------------------------------------------------------------------
- ' / Initialized ListView Control.
- ' / -------------------------------------------------------------------------------------------------
- Private Sub InitializeListView()
- With ListView1
- .Columns.Clear() '// make sure columns collection is empty.
- .Items.Clear()
- .View = View.Details
- .HeaderStyle = ColumnHeaderStyle.Nonclickable '// set to whatever you need.
- .GridLines = True
- .FullRowSelect = True
- .HideSelection = False
- .MultiSelect = True 'False
- '// Add 3 columns
- .Columns.Add("A")
- .Columns.Add("B")
- .Columns.Add("C")
- End With
- With ListView1
- .Columns(0).Width = ListView1.Width \ 3 - 10
- .Columns(1).Width = ListView1.Width \ 3 - 10
- .Columns(2).Width = ListView1.Width \ 3 - 5
- End With
- End Sub
- Private Sub frmPythagoras_FormClosed(sender As Object, e As System.Windows.Forms.FormClosedEventArgs) Handles Me.FormClosed
- Me.Dispose()
- GC.SuppressFinalize(Me)
- Application.Exit()
- End Sub
- Private Sub btnExit_Click(sender As System.Object, e As System.EventArgs) Handles btnExit.Click
- Me.Close()
- End Sub
- End Class
คัดลอกไปที่คลิปบอร์ด
ดาวน์โหลดโค้ดต้นฉบับ VB.NET (2010) ได้ที่นี่ ... |
ขออภัย! โพสต์นี้มีไฟล์แนบหรือรูปภาพที่ไม่ได้รับอนุญาตให้คุณเข้าถึง
คุณจำเป็นต้อง ลงชื่อเข้าใช้ เพื่อดาวน์โหลดหรือดูไฟล์แนบนี้ คุณยังไม่มีบัญชีใช่ไหม? ลงทะเบียน
x
|